9
9
10
10
### 什么是版本控制
11
11
12
- 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。 除了项目源代码,你可以对任何类型的文件进行版本控制。
12
+ 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。除了项目源代码,你可以对任何类型的文件进行版本控制。
13
13
14
14
### 为什么要版本控制
15
15
16
16
有了它你就可以将某个文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态,你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因,又是谁在何时报告了某个功能缺陷等等。
17
17
18
18
### 本地版本控制系统
19
19
20
- 许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。 这么做唯一的好处就是简单,但是特别容易犯错。 有时候会混淆所在的工作目录,一不小心会写错文件或者覆盖意想外的文件。
20
+ 许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。这么做唯一的好处就是简单,但是特别容易犯错。有时候会混淆所在的工作目录,一不小心会写错文件或者覆盖意想外的文件。
21
21
22
22
为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。
23
23
24
24
![ 本地版本控制系统] ( https://oss.javaguide.cn/github/javaguide/tools/git/%E6%9C%AC%E5%9C%B0%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6%E7%B3%BB%E7%BB%9F.png )
25
25
26
26
### 集中化的版本控制系统
27
27
28
- 接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作? 于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。
28
+ 接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作?于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。
29
29
30
30
集中化的版本控制系统都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。
31
31
32
32
![ 集中化的版本控制系统] ( https://oss.javaguide.cn/github/javaguide/tools/git/%E9%9B%86%E4%B8%AD%E5%8C%96%E7%9A%84%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6%E7%B3%BB%E7%BB%9F.png )
33
33
34
34
这么做虽然解决了本地版本控制系统无法让在不同系统上的开发者协同工作的诟病,但也还是存在下面的问题:
35
35
36
- - ** 单点故障: ** 中央服务器宕机 ,则其他人无法使用;如果中心数据库磁盘损坏又没有进行备份,你将丢失所有数据。本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。
37
- - ** 必须联网才能工作: ** 受网络状况、带宽影响。
36
+ - ** 单点故障** :若中央服务器宕机 ,则其他人无法使用;如果中心数据库磁盘损坏又没有进行备份,你将丢失所有数据。本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。
37
+ - ** 必须联网才能工作** : 受网络状况、带宽影响。
38
38
39
39
### 分布式版本控制系统
40
40
41
- 于是分布式版本控制系统(Distributed Version Control System,简称 DVCS)面世了。 Git 就是一个典型的分布式版本控制系统。
41
+ 于是分布式版本控制系统(Distributed Version Control System,简称 DVCS)面世了。Git 就是一个典型的分布式版本控制系统。
42
42
43
- 这类系统,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。
43
+ 这类系统,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。
44
44
45
45
![ 分布式版本控制系统] ( https://oss.javaguide.cn/github/javaguide/tools/git/%E5%88%86%E5%B8%83%E5%BC%8F%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6%E7%B3%BB%E7%BB%9F.png )
46
46
52
52
53
53
### Git 简史
54
54
55
- Linux 内核项目组当时使用分布式版本控制系统 BitKeeper 来管理和维护代码。但是,后来开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使用 BitKeeper 时的经验教训,开发出自己的版本系统,而且对新的版本控制系统做了很多改进。
55
+ Linux 内核项目组当时使用分布式版本控制系统 BitKeeper 来管理和维护代码。但是,后来开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使用 BitKeeper 时的经验教训,开发出自己的版本系统,而且对新的版本控制系统做了很多改进。
56
56
57
57
### Git 与其他版本管理系统的主要区别
58
58
59
- Git 在保存和对待各种信息的时候与其它版本控制系统有很大差异,尽管操作起来的命令形式非常相近,理解这些差异将有助于防止你使用中的困惑 。
59
+ Git 在保存和对待各种信息的时候与其它版本控制系统有很大差异,尽管操作起来的命令形式非常相近。理解这些差异将有助于防止你在使用 Git 中产生困惑 。
60
60
61
61
下面我们主要说一个关于 Git 与其他版本管理系统的主要差别:** 对待数据的方式** 。
62
62
63
63
** Git 采用的是直接记录快照的方式,而非差异比较。我后面会详细介绍这两种方式的差别。**
64
64
65
- 大部分版本控制系统(CVS、Subversion、Perforce、Bazaar 等等)都是以文件变更列表的方式存储信息,这类系统** 将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。**
65
+ 大部分版本控制系统(CVS、Subversion、Perforce、Bazaar 等等)都是以文件变更列表的方式存储信息,这类系统 ** 将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。**
66
66
67
- 具体原理如下图所示,理解起来其实很简单,每当我们提交更新一个文件之后,系统都会记录这个文件做了哪些更新,以增量符号 Δ( Delta) 表示。
67
+ 具体原理如下图所示,理解起来其实很简单,每当我们提交更新一个文件之后,系统都会记录这个文件做了哪些更新,以增量符号 Δ( Delta) 表示。
68
68
69
69
![ ] ( https://oss.javaguide.cn/github/javaguide/tools/git/2019-3deltas.png )
70
70
@@ -76,7 +76,7 @@ Git 在保存和对待各种信息的时候与其它版本控制系统有很大
76
76
77
77
比如我们的增量特别特别多的话,如果我们要得到最终的文件是不是会耗费时间和性能。
78
78
79
- Git 不按照以上方式对待或保存数据。 反之,Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 ** 快照流** 。
79
+ Git 不按照以上方式对待或保存数据。反之,Git 更像是把数据看作是对小型文件系统的一组快照。每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。Git 对待数据更像是一个 ** 快照流** 。
80
80
81
81
![ ] ( https://oss.javaguide.cn/github/javaguide/tools/git/2019-3snapshots.png )
82
82
@@ -88,7 +88,7 @@ Git 有三种状态,你的文件可能处于其中之一:
88
88
2 . ** 已修改(modified)** :已修改表示修改了文件,但还没保存到数据库中。
89
89
3 . ** 已暂存(staged)** :表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
90
90
91
- 由此引入 Git 项目的三个工作区域的概念:** Git 仓库( .git directory) ** 、** 工作目录( Working Directory) ** 以及 ** 暂存区域( Staging Area) ** 。
91
+ 由此引入 Git 项目的三个工作区域的概念:** Git 仓库( .git directory) ** 、** 工作目录( Working Directory) ** 以及 ** 暂存区域( Staging Area) ** 。
92
92
93
93
![ ] ( https://oss.javaguide.cn/github/javaguide/tools/git/2019-3areas.png )
94
94
@@ -104,18 +104,18 @@ Git 有三种状态,你的文件可能处于其中之一:
104
104
105
105
有两种取得 Git 项目仓库的方法。
106
106
107
- 1 . 在现有目录中初始化仓库: 进入项目目录运行 ` git init ` 命令, 该命令将创建一个名为 ` .git ` 的子目录。
108
- 2 . 从一个服务器克隆一个现有的 Git 仓库: ` git clone [url] ` 自定义本地仓库的名字: ` git clone [url] directoryname `
107
+ 1 . 在现有目录中初始化仓库: 进入项目目录运行 ` git init ` 命令, 该命令将创建一个名为 ` .git ` 的子目录。
108
+ 2 . 从一个服务器克隆一个现有的 Git 仓库: ` git clone [url] ` 自定义本地仓库的名字: ` git clone [url] directoryname ` 。
109
109
110
110
### 记录每次更新到仓库
111
111
112
- 1 . ** 检测当前文件状态** : ` git status `
113
- 2 . ** 提出更改(把它们添加到暂存区** ) :` git add filename ` ( 针对特定文件) 、` git add * ` ( 所有文件) 、` git add *.txt ` (支持通配符,所有 .txt 文件)
114
- 3 . ** 忽略文件** :` .gitignore ` 文件
115
- 4 . ** 提交更新: ** ` git commit -m "代码提交信息" ` (每次准备提交前,先用 ` git status ` 看下,是不是都已暂存起来了, 然后再运行提交命令 ` git commit ` )
116
- 5 . ** 跳过使用暂存区域更新的方式** : ` git commit -a -m "代码提交信息" ` 。 ` git commit ` 加上 ` -a ` 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 ` git add ` 步骤。
117
- 6 . ** 移除文件** :` git rm filename ` (从暂存区域移除,然后提交。)
118
- 7 . ** 对文件重命名** :` git mv README.md README ` ( 这个命令相当于` mv README.md README ` 、` git rm README.md ` 、` git add README ` 这三条命令的集合)
112
+ 1 . ** 检测当前文件状态** : ` git status ` 。
113
+ 2 . ** 提出更改(把它们添加到暂存区) ** :` git add filename ` ( 针对特定文件) 、` git add * ` ( 所有文件) 、` git add *.txt ` (支持通配符,所有 .txt 文件)。
114
+ 3 . ** 忽略文件** :` .gitignore ` 文件。
115
+ 4 . ** 提交更新** : ` git commit -m "代码提交信息" ` (每次准备提交前,先用 ` git status ` 看下,是不是都已暂存起来了,然后再运行提交命令 ` git commit ` )。
116
+ 5 . ** 跳过使用暂存区域更新的方式** : ` git commit -a -m "代码提交信息" ` 。` git commit ` 加上 ` -a ` 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 ` git add ` 步骤。
117
+ 6 . ** 移除文件** :` git rm filename ` (从暂存区域移除,然后提交)。
118
+ 7 . ** 对文件重命名** :` git mv README.md README ` ( 这个命令相当于 ` mv README.md README ` 、` git rm README.md ` 、` git add README ` 这三条命令的集合)。
119
119
120
120
### 一个好的 Git 提交消息
121
121
@@ -133,21 +133,21 @@ Git 有三种状态,你的文件可能处于其中之一:
133
133
134
134
### 推送改动到远程仓库
135
135
136
- - 如果你还没有克隆现有仓库,并欲将你的仓库连接到某个远程服务器,你可以使用如下命令添加:` git remote add origin <server> ` , 比如我们要让本地的一个仓库和 GitHub 上创建的一个仓库关联可以这样 ` git remote add origin https://github.com/Snailclimb/test.git `
137
- - 将这些改动提交到远端仓库:` git push origin master ` ( 可以把 _ master_ 换成你想要推送的任何分支)
136
+ - 如果你还没有克隆现有仓库,并欲将你的仓库连接到某个远程服务器,你可以使用如下命令添加:` git remote add origin <server> ` , 比如我们要让本地的一个仓库和 GitHub 上创建的一个仓库进行关联,可以这样: ` git remote add origin https://github.com/Snailclimb/test.git ` 。
137
+ - 将这些改动提交到远端仓库:` git push origin master ` ( 可以把 _ master_ 换成你想要推送的任何分支)。
138
138
139
139
如此你就能够将你的改动推送到所添加的服务器上去了。
140
140
141
141
### 远程仓库的移除与重命名
142
142
143
- - 将 test 重命名为 test1:` git remote rename test test1 `
144
- - 移除远程仓库 test1: ` git remote rm test1 `
143
+ - 将 test 重命名为 test1:` git remote rename test test1 ` 。
144
+ - 移除远程仓库 test1: ` git remote rm test1 ` 。
145
145
146
146
### 查看提交历史
147
147
148
- 在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。 完成这个任务最简单而又有效的工具是 ` git log ` 命令。` git log ` 会按提交时间列出所有的更新,最近的更新排在最上面。
148
+ 在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。完成这个任务最简单而又有效的工具是 ` git log ` 命令。` git log ` 会按提交时间列出所有的更新,最近的更新排在最上面。
149
149
150
- ** 可以添加一些参数来查看自己希望看到的内容: **
150
+ ** 可以添加一些参数来查看自己希望看到的内容** :
151
151
152
152
只看某个人的提交记录:
153
153
@@ -157,19 +157,19 @@ git log --author=bob
157
157
158
158
### 撤销操作
159
159
160
- 有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 ` --amend ` 选项的提交命令尝试重新提交:
160
+ 有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。此时,可以运行带有 ` --amend ` 选项的提交命令尝试重新提交:
161
161
162
162
``` shell
163
163
git commit --amend
164
164
```
165
165
166
- 取消暂存的文件
166
+ 取消暂存的文件:
167
167
168
168
``` shell
169
169
git reset filename
170
170
```
171
171
172
- 撤消对文件的修改:
172
+ 撤消对文件的修改:
173
173
174
174
``` shell
175
175
git checkout -- filename
@@ -188,39 +188,39 @@ git reset --hard origin/master
188
188
189
189
我们通常在开发新功能、修复一个紧急 bug 等等时候会选择创建分支。单分支开发好还是多分支开发好,还是要看具体场景来说。
190
190
191
- 创建一个名字叫做 test 的分支
191
+ 创建一个名字叫做 test 的分支:
192
192
193
193
``` shell
194
194
git branch test
195
195
```
196
196
197
- 切换当前分支到 test(当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。 Git 会自动添加、删除、修改文件以确保此时你的工作目录和这个分支最后一次提交时的样子一模一样)
197
+ 切换当前分支到 test(当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。Git 会自动添加、删除、修改文件以确保此时你的工作目录和这个分支最后一次提交时的样子一模一样):
198
198
199
199
``` shell
200
200
git checkout test
201
201
```
202
202
203
203
![ ] ( https://oss.javaguide.cn/github/javaguide/tools/git/2019-3%E5%88%87%E6%8D%A2%E5%88%86%E6%94%AF.png )
204
204
205
- 你也可以直接这样创建分支并切换过去( 上面两条命令的合写)
205
+ 你也可以直接这样创建分支并切换过去( 上面两条命令的合写):
206
206
207
207
``` shell
208
208
git checkout -b feature_x
209
209
```
210
210
211
- 切换到主分支
211
+ 切换到主分支:
212
212
213
213
``` shell
214
214
git checkout master
215
215
```
216
216
217
- 合并分支( 可能会有冲突)
217
+ 合并分支( 可能会有冲突):
218
218
219
219
``` shell
220
220
git merge test
221
221
```
222
222
223
- 把新建的分支删掉
223
+ 把新建的分支删掉:
224
224
225
225
``` shell
226
226
git branch -d feature_x
@@ -234,11 +234,11 @@ git push origin
234
234
235
235
## 学习资料推荐
236
236
237
- ** 在线演示学习工具: **
237
+ ** 在线演示学习工具** :
238
238
239
- 「补充,来自[ issue729] ( https://github.com/Snailclimb/JavaGuide/issues/729 ) 」Learn Git Branching < https://oschina.gitee.io/learn-git-branching/ > 。该网站可以方便的演示基本的 git 操作,讲解得明明白白。每一个基本命令的作用和结果 。
239
+ 「补充,来自 [ issue729] ( https://github.com/Snailclimb/JavaGuide/issues/729 ) 」Learn Git Branching < https://oschina.gitee.io/learn-git-branching/ > 。该网站可以方便的演示基本的 git 操作,每一个基本命令的作用和结果讲解得明明白白 。
240
240
241
- ** 推荐阅读: **
241
+ ** 推荐阅读** :
242
242
243
243
- [ Git 入门图文教程(1.5W 字 40 图)] ( https://www.cnblogs.com/anding/p/16987769.html ) :超用心的一篇文章,内容全面且附带详细的图解,强烈推荐!
244
244
- [ Git - 简明指南] ( https://rogerdudler.github.io/git-guide/index.zh.html ) :涵盖 Git 常见操作,非常清晰。
0 commit comments