-
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 分支 - 分支简述
几乎所有版本控制系统都支持某种形式的分支功能。分支意味着你从主开发线分离出来,继续进行工作,而不会影响主开发线。在许多版本控制工具中,这是一个相当昂贵的过程,通常需要你创建源代码目录的新副本,对于大型项目来说,这可能需要很长时间。
有些人将 Git 的分支模型称为其“杀手级功能”,它确实使 Git 在版本控制系统社区中脱颖而出。为什么它如此特别?Git 分支的方式非常轻量级,使得分支操作几乎是瞬时的,在分支之间切换也通常同样快。与许多其他版本控制系统不同,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*(每个代表三个文件之一的内容)、一个 *tree*(列出目录的内容并指定哪些文件名存储为哪些 blob)以及一个 *commit*,其中包含指向该根树的指针以及所有提交元数据。
如果您进行一些更改并再次提交,下一个提交将存储指向紧接其前的提交的指针。
Git 中的分支只是一个指向这些提交之一的轻量级可移动指针。Git 中的默认分支名称为 master
。当您开始进行提交时,您会得到一个指向您最后一次提交的 master
分支。每次提交时,master
分支指针都会自动向前移动。
注意
|
Git 中的“master”分支不是一个特殊的分支。它与任何其他分支完全相同。几乎每个仓库都有一个的原因仅仅是因为 |
创建新分支
当您创建一个新分支时会发生什么?嗯,这样做会为您创建一个新的指针,您可以四处移动。假设您要创建一个名为 testing
的新分支。您可以使用 git branch
命令来完成此操作
$ git branch testing
这将创建一个新的指针指向您当前所在的同一个提交。
Git 如何知道您当前位于哪个分支?它保留了一个名为 HEAD
的特殊指针。请注意,这与您可能习惯使用的其他 VCS 中的 HEAD
概念大不相同,例如 Subversion 或 CVS。在 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 开始,您可以使用
|