-
1. 起步
-
2. Git 基础
-
3. Git 分支
-
4. 服务器上的 Git
- 4.1 协议
- 4.2 在服务器上部署 Git
- 4.3 生成 SSH 公钥
- 4.4 架设服务器
- 4.5 Git Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 第三方托管服务
- 4.10 小结
-
5. 分布式 Git
-
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 工具中,这是一个相对耗费资源的过程,通常需要你创建一个源代码目录的新副本,这对于大型项目来说可能需要很长时间。
有些人将 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”分支不是一个特殊分支。它与任何其他分支完全一样。几乎每个仓库都有它的唯一原因是 |
创建新分支
当你创建一个新分支时会发生什么?好吧,这样做会为你创建一个新的指针,供你移动。假设你想创建一个名为testing的新分支。你可以使用git branch命令来完成此操作。
$ git branch testing
这会创建一个指向你当前所在提交的新指针。
Git 如何知道你当前在哪一个分支上?它保留一个名为HEAD的特殊指针。请注意,这与你可能习惯的其他 VCS(例如 Subversion 或 CVS)中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 版本开始,你可以使用
|