-
A1. 附录 A: Git 在其他环境
- A1.1 图形界面
- A1.2 Visual Studio 中的 Git
- A1.3 Visual Studio Code 中的 Git
- A1.4 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中的 Git
- A1.5 Sublime Text 中的 Git
- A1.6 Bash 中的 Git
- A1.7 Zsh 中的 Git
- A1.8 PowerShell 中的 Git
- A1.9 小结
-
A2. 附录 B: 在应用程序中嵌入 Git
-
A3. 附录 C: Git 命令
3.1 Git 分支 - 分支简述
几乎每个版本控制系统(VCS)都某种形式的分支支持。分支意味着你脱离主开发线,继续工作而不会干扰主线。在许多VCS工具中,这是一个相对昂贵的过程,通常需要你创建源代码目录的新副本,这对于大型项目可能需要很长时间。
有些人将Git的分支模型称为其“杀手级功能”,它确实使Git在VCS社区中脱颖而出。为什么它如此特别?Git分支的方式极其轻量级,使得分支操作几乎是瞬时完成的,并且在分支之间切换通常也同样快速。与许多其他VCS不同,Git鼓励频繁地进行分支和合并工作流,甚至一天内多次。理解和掌握此功能为你提供了强大而独特的工具,并可以完全改变你的开发方式。
分支简述
要真正理解Git分支的方式,我们需要退一步,检查Git如何存储其数据。
正如你可能从什么是Git?中记住的那样,Git不是将数据存储为一系列变更集或差异,而是存储为一系列快照。
当你进行一次提交时,Git会存储一个提交对象,其中包含指向你已暂存内容的快照的指针。此对象还包含作者的姓名和电子邮件地址,你输入的提交信息,以及指向此提交直接之前的一个或多个提交的指针(它的父提交或父提交们):初始提交有零个父提交,普通提交有一个父提交,以及由两个或多个分支合并产生的提交有多个父提交。
为了形象化地说明这一点,假设你有一个包含三个文件的目录,你将它们全部暂存并提交。暂存文件会计算每个文件的校验和(我们在什么是Git?中提到的SHA-1哈希),将该文件版本存储在Git仓库中(Git将它们称为blob对象),并将该校验和添加到暂存区
$ git add README test.rb LICENSE
$ git commit -m 'Initial commit'
当你运行git commit
创建提交时,Git会为每个子目录(在本例中,只是根项目目录)计算校验和,并将它们作为树对象存储在Git仓库中。然后,Git会创建一个提交对象,其中包含元数据和指向根项目树的指针,以便在需要时重新创建该快照。
你的Git仓库现在包含五个对象:三个blob对象(每个代表三个文件之一的内容),一个树对象,它列出目录内容并指定哪些文件名存储为哪些blob对象,以及一个提交对象,它包含指向该根树的指针和所有提交元数据。

如果你进行一些更改并再次提交,下一个提交会存储一个指向紧接在其之前的提交的指针。

Git中的分支只是一个轻量级的可移动指针,指向这些提交之一。Git中默认的分支名称是master
。当你开始提交时,你会得到一个指向你所做最后一个提交的master
分支。每次提交时,master
分支指针都会自动向前移动。
注意
|
Git中的“master”分支不是一个特殊分支。它与任何其他分支完全一样。几乎每个仓库都有一个master分支的唯一原因是 |

创建新分支
当你创建一个新分支时会发生什么?这样做会为你创建一个新的可移动指针。假设你想创建一个名为testing
的新分支。你可以使用git branch
命令完成此操作
$ git branch testing
这会创建一个指向你当前所在提交的新指针。

Git如何知道你当前在哪个分支上?它保留一个名为HEAD
的特殊指针。请注意,这与你可能习惯的Subversion或CVS等其他VCS中HEAD
的概念有很大不同。在Git中,这是一个指向你当前所在本地分支的指针。在这种情况下,你仍然在master
分支上。git branch
命令只是创建了一个新分支,但没有切换到该分支。

你可以通过运行一个简单的git log
命令来轻松查看这一点,它会显示分支指针指向的位置。这个选项称为--decorate
。
$ git log --oneline --decorate
f30ab (HEAD -> master, testing) Add feature #32 - ability to add new formats to the central interface
34ac2 Fix bug #1328 - stack overflow under certain conditions
98ca9 Initial commit
你可以看到master
和testing
分支就在f30ab
提交旁边。
切换分支
要切换到现有分支,请运行git checkout
命令。让我们切换到新的testing
分支
$ git checkout testing
这会将HEAD
移动指向testing
分支。

这有什么意义呢?好吧,让我们再进行一次提交
$ vim test.rb
$ git commit -a -m 'Make a change'

这很有趣,因为现在你的testing
分支已经向前移动,但你的master
分支仍然指向你在运行git checkout
切换分支时所处的提交。让我们切换回master
分支
$ git checkout master
注意
|
git log 不会一直显示所有分支如果你现在运行 该分支并没有消失;Git只是不知道你对该分支感兴趣,它正在尝试显示它认为你感兴趣的内容。换句话说,默认情况下, 要显示所需分支的提交历史,你必须明确指定它: |

该命令做了两件事。它将HEAD指针移回指向master
分支,并将你的工作目录中的文件还原到master
所指向的快照。这也意味着你从此时起所做的更改将与项目的旧版本有所不同。它本质上是回溯你在testing
分支中所做的工作,以便你可以走向不同的方向。
注意
|
切换分支会更改你工作目录中的文件
值得注意的是,当你在Git中切换分支时,你工作目录中的文件会发生变化。如果你切换到旧的分支,你的工作目录将恢复到你上次在该分支上提交时的样子。如果Git无法干净地完成此操作,它将根本不允许你切换。 |
让我们再进行一些更改并再次提交
$ vim test.rb
$ git commit -a -m 'Make other changes'
现在你的项目历史已经分叉(参见分叉历史)。你创建并切换到一个分支,在上面做了一些工作,然后切换回主分支并做了其他工作。所有这些更改都隔离在单独的分支中:你可以在分支之间来回切换,并在准备好时将它们合并。而你所有这些操作都只用了简单的branch
、checkout
和commit
命令就完成了。

你也可以使用git log
命令轻松查看这一点。如果你运行git log --oneline --decorate --graph --all
,它将打印出你的提交历史,显示你的分支指针在哪里以及你的历史是如何分叉的。
$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) Make other changes
| * 87ab2 (testing) Make a change
|/
* f30ab Add feature #32 - ability to add new formats to the central interface
* 34ac2 Fix bug #1328 - stack overflow under certain conditions
* 98ca9 Initial commit of my project
因为Git中的分支实际上是一个简单的文件,其中包含它所指向的提交的40个字符的SHA-1校验和,所以创建和销毁分支的成本很低。创建一个新分支就像向文件写入41个字节(40个字符加一个换行符)一样快速和简单。
这与大多数旧VCS工具的分支方式形成鲜明对比,后者涉及将项目的所有文件复制到第二个目录中。这可能需要几秒甚至几分钟,具体取决于项目的大小,而在Git中,这个过程总是瞬时完成的。此外,由于我们在提交时记录了父提交,因此自动为我们找到了合适的合并基础,并且通常非常容易操作。这些功能有助于鼓励开发人员频繁创建和使用分支。
让我们看看为什么要这样做。
注意
|
同时创建新分支并切换到它
通常会同时创建一个新分支并希望切换到该新分支——这可以通过一个操作完成: |
注意
|
从Git 2.23版本开始,你可以使用
|