交互式教程!带你深入理解 Git 原理

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"综述"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如下图所示,有四个模块。这个独立的我们称之为 "},{"type":"text","marks":[{"type":"italic"}],"text":"远程仓库"},{"type":"text","text":",其他靠在一起的三个我们称之为 "},{"type":"text","marks":[{"type":"italic"}],"text":"开发环境"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/24\/2464da0c7d496218ce816e2bb90970d6.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从独立的这个模块开始说,"},{"type":"text","marks":[{"type":"italic"}],"text":"远程仓库"},{"type":"text","text":" 是你推送你的改动并且希望与他人共享的地方,同样你也可以从中获取到他人的改动。如果你使用过其他的版本控制系统,那这个概念并没有什么新鲜的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"开发环境"},{"type":"text","text":" 是在你本地计算机上的。它的三个部分指的是你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 、"},{"type":"text","marks":[{"type":"italic"}],"text":"暂存区"},{"type":"text","text":" 和 "},{"type":"text","marks":[{"type":"italic"}],"text":"本地仓库"},{"type":"text","text":" 。在开始使用 Git 前,我们将先了解有关这些概念的知识。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"请选择一个要放置你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"开发环境"},{"type":"text","text":" 的位置。可以转到你的主目录,或者你想放你的项目的任何地方,不需要为你的开发环境创建新文件夹。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"创建一个远程仓库"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在我们要创建一个 "},{"type":"text","marks":[{"type":"italic"}],"text":"远程仓库"},{"type":"text","text":" 并将仓库的内容导入你的计算机。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我建议使用这个 Git 仓库:(https:\/\/github.com\/UnseenWizzard\/git_training.git)."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里可以使用这条命令拉取仓库代码:"},{"type":"codeinline","content":[{"type":"text","text":"git clone https:\/\/github.com\/UnseenWizzard\/git_training.git"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来的操作需要你将本地开发环境的改动提交到远程仓库,由于这个示例仓库不允许他人操作,因此建议你将这个仓库fork到你个人的github下,这个fork按钮位于页面的右上角."}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,在你自己的github中已经有了这个_远程仓库_的副本,然后就可以将它克隆到你本地开发环境中了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们可以使用这条命令:"},{"type":"codeinline","content":[{"type":"text","text":"git clone https:\/\/github.com\/{YOUR USERNAME}\/git_training.git"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如下图所示,这个远程仓库已经被拷贝到了两个位置,即你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 和 "},{"type":"text","marks":[{"type":"italic"}],"text":"本地仓库"},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在你可以看到git是如何进行 "},{"type":"text","marks":[{"type":"italic"}],"text":"分布式"},{"type":"text","text":" 版本控制的。"},{"type":"text","marks":[{"type":"italic"}],"text":"本地仓库"},{"type":"text","text":" 是 "},{"type":"text","marks":[{"type":"italic"}],"text":"远程仓库"},{"type":"text","text":" 的副本,其行为与之类似,唯一的区别是你没有与任何人分享。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"git clone"}]},{"type":"text","text":" 还在你执行该命令的目录下创建了一个新的文件夹. 这里对应的应该是 "},{"type":"codeinline","content":[{"type":"text","text":"git_training"}]},{"type":"text","text":",打开它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/62\/62ebdc54ee3d31f1d1b06905d858511c.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"添加一些新内容"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有人已经把"},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"添加进了 "},{"type":"text","marks":[{"type":"italic"}],"text":"远程仓库"},{"type":"text","text":" 。她看上去有点孤单,所以让我们创建一个"},{"type":"codeinline","content":[{"type":"text","text":"Bob.txt"}]},{"type":"text","text":"去陪陪她。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这一步就是将文件添加到你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在在你的工作目录中有两种文件,一种是git知道的 "},{"type":"text","marks":[{"type":"italic"}],"text":"跟踪文件"},{"type":"text","text":" ,另一种是git不知道的 "},{"type":"text","marks":[{"type":"italic"}],"text":"未跟踪文件"},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要查看你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 中发生了什么,请运行"},{"type":"codeinline","content":[{"type":"text","text":"git status"}]},{"type":"text","text":",它将告诉你当前所处的分支、你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"本地仓库"},{"type":"text","text":" 是否与 "},{"type":"text","marks":[{"type":"italic"}],"text":"远程仓库"},{"type":"text","text":" 不同,以及 "},{"type":"text","marks":[{"type":"italic"}],"text":"跟踪"},{"type":"text","text":" 和 "},{"type":"text","marks":[{"type":"italic"}],"text":"未跟踪"},{"type":"text","text":" 文件的状态。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你会看到"},{"type":"codeinline","content":[{"type":"text","text":"Bob.txt"}]},{"type":"text","text":"是未跟踪的,此外"},{"type":"codeinline","content":[{"type":"text","text":"git status"}]},{"type":"text","text":" 还会告诉你如何更改它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在下面的图片中,你可以看到当你根据建议执行"},{"type":"codeinline","content":[{"type":"text","text":"git add Bob.txt"}]},{"type":"text","text":"时会发生什么:你已将该文件添加到 "},{"type":"text","marks":[{"type":"italic"}],"text":"暂存区"},{"type":"text","text":" ,该区域用于存放你希望放入仓库的所有更改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/d8\/d87ac83f07b726fd244cb048a991e241.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当你添加了所有的更改(现在只添加了Bob)后,就可以将刚才所做的操作提交到 "},{"type":"text","marks":[{"type":"italic"}],"text":"本地仓库"},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你提交的改动具有一定的意义,因此当你执行"},{"type":"codeinline","content":[{"type":"text","text":"git commit"}]},{"type":"text","text":"时,文本编辑器将打开,并允许你编写一条消息,告诉他人你刚才所做改动的意义。当你保存并关闭时,你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"提交"},{"type":"text","text":" 将被添加到 "},{"type":"text","marks":[{"type":"italic"}],"text":"本地仓库"},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/3c\/3c58b6d5fb3ff41eac24152f8825facb.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同样你也可以直接在命令行中编辑你的commit 信息,比如:"},{"type":"codeinline","content":[{"type":"text","text":"git commit -m \"Add Bob\""}]},{"type":"text","text":"。如果你想写出更规范化的commit信息,你可以花时间看看 good commit messages。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,你的更改已经提交到了本地仓库中,只要没有其他人需要这些更改,或者你还没有准备共享它们,就可以将这些更改保存在本地仓库中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果需要通过 "},{"type":"text","marks":[{"type":"italic"}],"text":"远程仓库"},{"type":"text","text":" 共享你提交的改动,你需要"},{"type":"codeinline","content":[{"type":"text","text":"push"}]},{"type":"text","text":"它们。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/71\/71c19b278565334a7f2d8666612f94ef.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一旦运行"},{"type":"codeinline","content":[{"type":"text","text":"git push"}]},{"type":"text","text":",你的改动将被推送到   "},{"type":"text","marks":[{"type":"italic"}],"text":"远程仓库"},{"type":"text","text":" ,"},{"type":"codeinline","content":[{"type":"text","text":"push"}]},{"type":"text","text":"之后的状态如下图所示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/0c\/0ce50d30c14bb2e0a76af1b27022b027.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"修改内容"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到目前为止,我们只是添加了一个新文件。显然,版本控制中更有趣的部分是更改文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"中包含了一些文本,但是 "},{"type":"codeinline","content":[{"type":"text","text":"Bob.txt"}]},{"type":"text","text":"是空的,所以让我们在"},{"type":"codeinline","content":[{"type":"text","text":"Bob.txt"}]},{"type":"text","text":"中加上"},{"type":"codeinline","content":[{"type":"text","text":"Hi!! I'm Bob. I'm new here."}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你现在执行 "},{"type":"codeinline","content":[{"type":"text","text":"git status"}]},{"type":"text","text":", 你会看到 "},{"type":"codeinline","content":[{"type":"text","text":"Bob.txt"}]},{"type":"text","text":" 已经更新了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此时这些改动仅仅位于你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你想看看你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 中发生了什么更改,运行"},{"type":"codeinline","content":[{"type":"text","text":"git diff"}]},{"type":"text","text":",便可以看到如下内容:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"diff --git a\/Bob.txt b\/Bob.txt\nindex e69de29..3ed0e1b 100644\n--- a\/Bob.txt\n+++ b\/Bob.txt\n@@ -0,0 +1 @@\n+Hi!! I'm Bob. I'm new here."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"像之前那样继续执行 "},{"type":"codeinline","content":[{"type":"text","text":"git add Bob.txt"}]},{"type":"text","text":" 。这些改动则被移到了 "},{"type":"text","marks":[{"type":"italic"}],"text":"暂存区"},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果想看看刚刚发生的改动,再次执行"},{"type":"codeinline","content":[{"type":"text","text":"git diff"}]},{"type":"text","text":" !你会注意到这次控制台输出是空的。这是因为"},{"type":"codeinline","content":[{"type":"text","text":"git diff"}]},{"type":"text","text":"只会展示你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 中的更改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了显示哪些更改已经被暂存,执行"},{"type":"codeinline","content":[{"type":"text","text":"git diff --staged"}]},{"type":"text","text":",我们将看到与之前类似的输出。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我刚注意到我们在\"Hi\"后面加了两个感叹号“!!”。我不喜欢这样,让我们再次修改"},{"type":"codeinline","content":[{"type":"text","text":"Bob.txt"}]},{"type":"text","text":"文件将它改成'Hi!'。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果现在运行 "},{"type":"codeinline","content":[{"type":"text","text":"git status"}]},{"type":"text","text":",我们将看到有两个更改:一个是我们已经 "},{"type":"text","marks":[{"type":"italic"}],"text":"暂存"},{"type":"text","text":" 的,另一个是我们刚刚做的,它还只保存在 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们可以执行"},{"type":"codeinline","content":[{"type":"text","text":"git diff"}]},{"type":"text","text":"来查看一下 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 和 "},{"type":"text","marks":[{"type":"italic"}],"text":"暂存区"},{"type":"text","text":" 在我们修改后都发生了什么变化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"diff --git a\/Bob.txt b\/Bob.txt\nindex 8eb57c4..3ed0e1b 100644\n--- a\/Bob.txt\n+++ b\/Bob.txt\n@@ -1 +1 @@\n-Hi!! I'm Bob. I'm new here.\n+Hi! I'm Bob. I'm new here.\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于这是我们想要的改动,因此这里执行"},{"type":"codeinline","content":[{"type":"text","text":"git add Bob.txt"}]},{"type":"text","text":" 去暂存这个文件当前的状态。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在我们可以提交刚刚的改动了。这里我选择直接命令行执行 "},{"type":"codeinline","content":[{"type":"text","text":"git commit -m \"Add text to Bob\""}]},{"type":"text","text":"。对这种小的变化,写一行commit信息就够了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如我们所知,这些改动现在是在 "},{"type":"text","marks":[{"type":"italic"}],"text":"本地仓库"},{"type":"text","text":" 中。我们可能还想知道我们刚刚的提交,以及之前在 "},{"type":"text","marks":[{"type":"italic"}],"text":"本地仓库"},{"type":"text","text":" 提交过的改动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们可以通过比较这些commit来做到这一点。git中的每个commit都有一个唯一的哈希值作为他的引用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我们执行一下 "},{"type":"codeinline","content":[{"type":"text","text":"git log"}]},{"type":"text","text":",我们不仅可以看到所有提交的列表,包括它们的hash、Author和Date,还可以看到本地仓库的状态和远程分支的最新本地信息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"执行"},{"type":"codeinline","content":[{"type":"text","text":"git log"}]},{"type":"text","text":"后结果如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"commit 87a4ad48d55e5280aa608cd79e8bce5e13f318dc (HEAD -> master)\nAuthor: {YOU} \nDate: Sun Jan 27 14:02:48 2019 +0100\n\n Add text to Bob\n\ncommit 8af2ff2a8f7c51e2e52402ecb7332aec39ed540e (origin\/master, origin\/HEAD)\nAuthor: {YOU} \nDate: Sun Jan 27 13:35:41 2019 +0100\n\n Add Bob\n\ncommit 71a6a9b299b21e68f9b0c61247379432a0b6007c\nAuthor: UnseenWizzard \nDate: Fri Jan 25 20:06:57 2019 +0100\n\n Add Alice\n\ncommit ddb869a0c154f6798f0caae567074aecdfa58c46\nAuthor: Nico Riedmann \nDate: Fri Jan 25 19:25:23 2019 +0100\n\n Add Tutorial Text\n\n Changes to the tutorial are all squashed into this commit on master, to keep the log free of clutter that distracts from the tutorial\n\n See the tutorial_wip branch for the actual commit history"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里我们看到一些有趣的事情:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前两次提交是我做的。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在远程仓库中,你最初添加Bob的提交是在HEAD指针指向的master分支上。在讨论分支和获取远程更改时,我们将再次讨论这个问题。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本地仓库中最新的提交是我们刚刚做的,现在我们知道了它的哈希值。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"请注意,你实际的commit的哈希值可能与之不同。如果你想知道git具体是如何精确制定这些IDs的,请阅读 this interesting article."}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要比较这个提交和之前的一个提交,可以执行 "},{"type":"codeinline","content":[{"type":"text","text":"git diff ^!"}]},{"type":"text","text":" (这里的"},{"type":"codeinline","content":[{"type":"text","text":"^!"}]},{"type":"text","text":" 会告诉git去和它之前的一个提交比较)。在这里我们执行 "},{"type":"codeinline","content":[{"type":"text","text":"git diff "}]},{"type":"codeinline","content":[{"type":"text","text":"87a4ad48d55e5280aa608cd79e8bce5e13f318dc^!"}]},{"type":"text","text":"."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们也可以执行 "},{"type":"codeinline","content":[{"type":"text","text":"git diff 8af2ff2a8f7c51e2e52402ecb7332aec39ed540e 87a4ad48d55e5280aa608cd79e8bce5e13f318dc"}]},{"type":"text","text":" 去比较任意两个commit提交。请注意这里的格式是"},{"type":"codeinline","content":[{"type":"text","text":"git diff "}]},{"type":"text","text":", 也就是说我们新的提交是后面这个。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在下面的图表中,你会再次看到在不同git命令操作时,当前文件的的不同状态。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/3c\/3ceb2cb62e13d8c8b1cc8319112eef52.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"既然我们确定这些改动是符合预期的,那么请继续执行"},{"type":"codeinline","content":[{"type":"text","text":"git push"}]},{"type":"text","text":"吧。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"分支"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"git中分支的应用是不可或缺的一部分,这同时也是git之所以强大的原因之一。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其实从一开始,我们就已经在使用分支了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当你将远程仓库"},{"type":"codeinline","content":[{"type":"text","text":"clone"}]},{"type":"text","text":"到你的开发环境时,它已经自动创建了一个主分支:"},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当你将改动 "},{"type":"codeinline","content":[{"type":"text","text":"合并"}]},{"type":"text","text":" 到 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 之前,git的大多数工作流程都是在一个分支上进行更改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常你都会在自己的分支上工作,直到你完成工作并确认无误后,再将这些更改合并到 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"许多git仓库管理器如 "},{"type":"text","marks":[{"type":"italic"}],"text":"GitLab"},{"type":"text","text":" 和 "},{"type":"text","marks":[{"type":"italic"}],"text":"GitHub"},{"type":"text","text":" 还支持将分支保护起来, 即不允许任何人在此分支上提交改动。通常 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":"  分支是默认受保护的。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"别担心,当我们需要这些内容时,我们再更详细地讨论。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当你只想自己尝试一些事情,而不是修改 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 分支的状态,或者是你无权修改 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 时,你可以自己创建一个分支并对其进行一些更改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分支是位于 "},{"type":"text","marks":[{"type":"italic"}],"text":"本地仓库"},{"type":"text","text":" 和 "},{"type":"text","marks":[{"type":"italic"}],"text":"远程仓库"},{"type":"text","text":" 中的。当你创建新分支时,分支的内容将是当前正在处理的分支的提交状态的副本。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"让我们在 "},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"做一些改动! 不如在第二行放些文字怎么样?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们希望共享这个改动,但不希望提交到 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" ,因此可执行命令"},{"type":"codeinline","content":[{"type":"text","text":"git branch "}]},{"type":"text","text":" 来创建一个分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"创建一个名为 "},{"type":"codeinline","content":[{"type":"text","text":"change_alice"}]},{"type":"text","text":"的分支,你可以执行"},{"type":"codeinline","content":[{"type":"text","text":"git branch change_alice"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这条命令会在你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"本地仓库"},{"type":"text","text":" 下添加一个新分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虽然你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 和 "},{"type":"text","marks":[{"type":"italic"}],"text":"暂存区"},{"type":"text","text":" 并不真正关心分支,但其实你的"},{"type":"codeinline","content":[{"type":"text","text":"commit"}]},{"type":"text","text":"操作总是将改动提交到当前所在的分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以将git中的 "},{"type":"text","marks":[{"type":"italic"}],"text":"分支"},{"type":"text","text":" 视作很多指针,他们指向一系列的提交。当 执行"},{"type":"codeinline","content":[{"type":"text","text":"commit"}]},{"type":"text","text":"时,会添加到你当前指针指向的地方。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"只是添加一个分支,并不能直接把你带到那里,它只是创建一个这样的指针。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"实际上,你当前所处的 "},{"type":"text","marks":[{"type":"italic"}],"text":"本地仓库"},{"type":"text","text":" 可以看作另一个指针,称为 "},{"type":"text","marks":[{"type":"italic"}],"text":"HEAD"},{"type":"text","text":",它指向你当前所在的分支和提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果这听起来很复杂,下面的图表将有助于理清一些思绪:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/43\/43124969178a4a80ee8785e1a2308feb.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要切换到我们的新分支,你必须使用"},{"type":"codeinline","content":[{"type":"text","text":"git checkout change_alice"}]},{"type":"text","text":"。这只需将 "},{"type":"text","marks":[{"type":"italic"}],"text":"HEAD"},{"type":"text","text":" 移动到你所指定的分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常你都希望在创建分支后立即切换到该分支,因此"},{"type":"codeinline","content":[{"type":"text","text":"checkout"}]},{"type":"text","text":" 命令有一个方便的"},{"type":"codeinline","content":[{"type":"text","text":"-b"}]},{"type":"text","text":" 选项,它允许你直接"},{"type":"codeinline","content":[{"type":"text","text":"checkout"}]},{"type":"text","text":"一个新的分支而不必事先创建它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此,创建并切换一个 "},{"type":"codeinline","content":[{"type":"text","text":"change_alice"}]},{"type":"text","text":" 分支, 我们可以执行 "},{"type":"codeinline","content":[{"type":"text","text":"git checkout -b change_alice"}]},{"type":"text","text":"."}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/23\/232d0fdd6a29d2a591e40504cbc979e7.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你将发现,你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 没有改变, 实际上我们 "},{"type":"text","marks":[{"type":"italic"}],"text":"修改"},{"type":"text","text":" "},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":" 与当前分支无关。现在你可以像之前在master分支上那样,执行 "},{"type":"codeinline","content":[{"type":"text","text":"add"}]},{"type":"text","text":" 及 "},{"type":"codeinline","content":[{"type":"text","text":"commit"}]},{"type":"text","text":"去提交我们在"},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"上的改动,这将会暂存并最终将改动提交到"},{"type":"codeinline","content":[{"type":"text","text":"change_alice"}]},{"type":"text","text":"分支上。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在只有一件事你还没有做,就是去执行"},{"type":"codeinline","content":[{"type":"text","text":"git push"}]},{"type":"text","text":"将你的改动提交到远程仓库。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你将会看到下面的错误日志以及解决这个问题的建议:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"fatal: The current branch change_alice has no upstream branch.\nTo push the current branch and set the remote as upstream, use\n(当前分支change_alice没有上游分支,要推送当前分支的改动并设置远端作为上游分支,可以使用如下命令行:)\n\n git push --set-upstream origin change_alice\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到这里,我们不想根据提示盲目照做。我们是来了解实际情况的,那么什么是 上游分支 和 远端分支?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"还记得之前我们 "},{"type":"codeinline","content":[{"type":"text","text":"cloned"}]},{"type":"text","text":"远程仓库吗?当时仓库内容不仅包含本教程和 "},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"文件 ,实际上还有两个分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一个是我们刚开始在上面操作的 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" ,还有一个我命名为\"tutorial_wip\"的分支,在这个分支上,我提交了本教程所做的所有更改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当我们将远程仓库中的内容复制到你的开发环境中时,还进行了一些额外的操作,如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Git将本地仓库的远程仓库设置为克隆的远程仓库,默认名称为 "},{"type":"codeinline","content":[{"type":"text","text":"origin"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你的本地仓库可以跟踪多个远程,它们可以有不同的名称,但本教程中我们使用的是"},{"type":"codeinline","content":[{"type":"text","text":"origin"}]},{"type":"text","text":" 。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后它将两个远程分支复制到你的本地仓库,最后为你切换到了master。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这时候另一个隐含的步骤发生了。当你"},{"type":"codeinline","content":[{"type":"text","text":"切换"}]},{"type":"text","text":" 一个与远程分支完全匹配的分支名称时,你将获得一个新的链接到远程分支的本地分支。这个远程分支就是你这个本地分支的 "},{"type":"text","marks":[{"type":"italic"}],"text":"上游分支"},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在前面的图表中,你可以看到你拥有的本地分支。你可以通过运行"},{"type":"codeinline","content":[{"type":"text","text":"git branch"}]},{"type":"text","text":"查看本地分支的列表。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果还想查看你本地仓库知道的远程分支,可以使用 "},{"type":"codeinline","content":[{"type":"text","text":"git branch -a"}]},{"type":"text","text":" 列出所有分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/11\/11f6de2133fb295e1f5d4fd4f1b73ed7.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在我们可以执行之前命令窗口建议的"},{"type":"codeinline","content":[{"type":"text","text":"git push --set-upstream origin change_alice"}]},{"type":"text","text":"命令了,将分支上的更改 "},{"type":"codeinline","content":[{"type":"text","text":"push"}]},{"type":"text","text":" 到一个新的远程节点上。这将在远程仓库上创建一个 "},{"type":"codeinline","content":[{"type":"text","text":"change_alice"}]},{"type":"text","text":"分支,并会将我们的本地的 "},{"type":"codeinline","content":[{"type":"text","text":"change_alice"}]},{"type":"text","text":"分支跟踪该新的远程分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我们希望本地分支跟踪远程仓库上已经存在一个分支,还有另一个选择。也许一位同事已经推动了一些改动,而我们本地分支也解决了一些问题,我们希望将两者结合起来。那么,我们就可以通过使用"},{"type":"codeinline","content":[{"type":"text","text":"git branch --set-upstream-to=origin\/change_alice"}]},{"type":"text","text":",将"},{"type":"codeinline","content":[{"type":"text","text":"change_alice"}]},{"type":"text","text":"分支的上游分支设置为一个新的远程分支,然后跟踪这个远程分支。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在这之后,在github上查看一下你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"远程仓库"},{"type":"text","text":" ,你的分支已经在那里了,可以供其他人查看和使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来,我们将了解如何将其他人的改动引入到你的开发环境中,但首先我们需要更多地练习使用分支,学习从远程仓库获取改动也会用到的一些概念。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"合并"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于你和其他人通常都在各自分支上工作,所以我们需要讨论如何通过合并这些分支来将更改从一个分支转移到另一个分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们刚刚在"},{"type":"codeinline","content":[{"type":"text","text":"change_alice"}]},{"type":"text","text":" 分支上改变了 "},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":", 我对我们做的改动很满意."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你执行"},{"type":"codeinline","content":[{"type":"text","text":"git checkout master"}]},{"type":"text","text":", 那么我们在另一个分支上的"},{"type":"codeinline","content":[{"type":"text","text":"commit"}]},{"type":"text","text":" 将不存在。要将这些改动更新到 master中,我们需要将"},{"type":"codeinline","content":[{"type":"text","text":"change_alice"}]},{"type":"text","text":"分支合并进 master."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"请注意,合并总是会将一个特定的分支合并到你当前所在的分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"快进合并"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我们已经执行"},{"type":"codeinline","content":[{"type":"text","text":"checked out"}]},{"type":"text","text":"切换到了 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":", 那便可以合并分支了:"},{"type":"codeinline","content":[{"type":"text","text":"git merge change_alice"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在"},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"文件中没有其他的冲突改动,并且我们在master上也没有任何改动,这便称之为快进合并。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在下面的图表中,你可以看到快进合并意味着 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 的指针简单地移动到"},{"type":"codeinline","content":[{"type":"text","text":"change_alice"}]},{"type":"text","text":"所在的位置。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一张图显示了"},{"type":"codeinline","content":[{"type":"text","text":"merge"}]},{"type":"text","text":"之前的状态,"},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 仍然处于最初的提交状态,而在另一个分支上,我们又进行了一次提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/28\/288f5d99a92d8678c52998c1fea5edd3.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二张图显示了 "},{"type":"codeinline","content":[{"type":"text","text":"merge"}]},{"type":"text","text":"之后的状态。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/8b\/8b0422b704097644515b31e5dc31b577.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"合并冲突分支"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"让我们试试更复杂的东西。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 分支上给 "},{"type":"codeinline","content":[{"type":"text","text":"Bob.txt"}]},{"type":"text","text":"添加一些文本并提交它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后执行"},{"type":"codeinline","content":[{"type":"text","text":"git checkout change_alice"}]},{"type":"text","text":"切换分支修改"},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"文件并提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在下面的图表中,你可以看到我们的提交历史。"},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"change_alice"}]},{"type":"text","text":" 都源于同一个commit,但从那时起,它们就分开了,每个分支都有自己的附加提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/01\/017decc398c5ec9927beed29f6132f96.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果现在切换回master(git checkout master)并执行"},{"type":"codeinline","content":[{"type":"text","text":"git merge change_alice"}]},{"type":"text","text":" 则无法进行快速合并。此时,文本编辑器将打开并需要你更改"},{"type":"codeinline","content":[{"type":"text","text":"merge commit"}]},{"type":"text","text":"的消息,以便将两个分支重新合并在一起,这里你可以使用默认消息。下面的图表显示了我们在 "},{"type":"codeinline","content":[{"type":"text","text":"merge"}]},{"type":"text","text":"之后git历史的状态。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/03\/0391f2eac0eb44e53f8958abf39987c3.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这个新提交将我们在"},{"type":"codeinline","content":[{"type":"text","text":"change_alice"}]},{"type":"text","text":"分支上所做的更改引入到了master中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如你以前所记得的,git中的修订不仅是文件的快照,还包含文件的来源信息。每个 "},{"type":"codeinline","content":[{"type":"text","text":"commit"}]},{"type":"text","text":"都有一个或多个父提交。我们新的 "},{"type":"codeinline","content":[{"type":"text","text":"commit"}]},{"type":"text","text":"提交,既有来自_master_ 的最后一次提交,也有我们在另一个分支上作为其父级所做的提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"解决冲突"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到目前为止,我们的改动还没有相互干扰。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"让我们制造一个 "},{"type":"text","marks":[{"type":"italic"}],"text":"冲突"},{"type":"text","text":" 然后 "},{"type":"text","marks":[{"type":"italic"}],"text":"解决"},{"type":"text","text":" 它吧。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"创建并切换到一个新的分支,这个你已经知道怎么做了,也可以简单地执行"},{"type":"codeinline","content":[{"type":"text","text":"git checkout -b"}]},{"type":"text","text":",这里我们将新分支命名为"},{"type":"codeinline","content":[{"type":"text","text":"bobby_branch"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基于这个分支,我们在"},{"type":"codeinline","content":[{"type":"text","text":"Bob.txt"}]},{"type":"text","text":"文件上做一些改动。现在第一行应该还是"},{"type":"codeinline","content":[{"type":"text","text":"Hi!! I'm Bob. I'm new here."}]},{"type":"text","text":",我们将它改成 "},{"type":"codeinline","content":[{"type":"text","text":"Hi!! I'm Bobby. I'm new here."}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"保留并提交你的改动之后,切换到master分支。我们修改同样的这行内容为 "},{"type":"codeinline","content":[{"type":"text","text":"Hi!! I'm Bob. I've been here for a while now."}]},{"type":"text","text":",然后提交这个改动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在再将你的新分支合到master分支上。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当你尝试这么做的时候,你会看到下面这样的提示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"Auto-merging Bob.txt\nCONFLICT (content): Merge conflict in Bob.txt\nAutomatic merge failed; fix conflicts and then commit the result.\n(自动合并Bob.txt发生冲突(内容):Bob.txt文件冲突自动合并失败;修复冲突然后提交结果。)\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"两个分支都修改了同样的一行内容,git无法自动处理这样的改动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你执行"},{"type":"codeinline","content":[{"type":"text","text":"git status"}]},{"type":"text","text":" ,你将会得到如何继续下一步的一些有帮助的指令。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先我们必须手动解决冲突。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于像这样简单的冲突来说,你的文本编辑器就可以做到。但对于有大量文件的改动来说,一个功能强大的编辑工具会让你的工作变的更简单,我建议你的编辑器最好带有版本控制工具和很好的合并视图窗口。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你在编辑器中打开文件,你会看到类似下面这样的内容(这里我只截取了之前在第二行中添加的内容):"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"<<<<<<>>>>>> bobby_branch\n[... whatever you've put on line 2]\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在最上面你可以看到 "},{"type":"codeinline","content":[{"type":"text","text":"Bob.txt"}]},{"type":"text","text":" 在当前 HEAD上的改动,下面是我们合并进的分支中发生的改动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要手动解决冲突,你只需要确保最终保留你所需的内容,并且删掉git在文件中引入的特殊行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以继续把文件改成这样:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"Hi! I'm Bobby. I've been here for a while now.\n[...]\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里所做的改动是我们所需要的,因此执行"},{"type":"codeinline","content":[{"type":"text","text":"add Bob.txt"}]},{"type":"text","text":"暂存这个文件,再 "},{"type":"codeinline","content":[{"type":"text","text":"commit"}]},{"type":"text","text":"提交."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们已经知道这次提交是为解决冲突所做的。这就是在合并提交时经常出现的 "},{"type":"text","marks":[{"type":"italic"}],"text":"merge commit"},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你在解决冲突的过程中意识到你实际上不想使用"},{"type":"codeinline","content":[{"type":"text","text":"merge"}]},{"type":"text","text":",你可以通过运行 "},{"type":"codeinline","content":[{"type":"text","text":"git merge --abort"}]},{"type":"text","text":"来 "},{"type":"codeinline","content":[{"type":"text","text":"中止"}]},{"type":"text","text":" 它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"变基"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Git有另一种清晰的方法来集成两个分支之间的更改,称为 "},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"变基。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们仍然记得,一个分支总是基于另一个分支。当你创建它的时候,你就从某个地方检出了一个分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在刚刚简单合并的示例中,我们在一个特定的commit节点从 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 检出了一个分支,然后在 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"change_alice"}]},{"type":"text","text":" 分支上都提交了一些更改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当一个分支与它所基于的分支发生分歧,并且你希望将最新的更改重新集成到当前分支中时, "},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":" 提供了一种比 "},{"type":"codeinline","content":[{"type":"text","text":"merge"}]},{"type":"text","text":" 更简洁的方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如我们所看到的,"},{"type":"codeinline","content":[{"type":"text","text":"merge"}]},{"type":"text","text":" 引入了一个 "},{"type":"text","marks":[{"type":"italic"}],"text":"merge commit"},{"type":"text","text":" ,其中两个历史记录再次集成在一起。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"简单地说,变基只会改变分支所基于的历史点(commit节点)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为此,让我们再次切换到master分支,然后基于它创建\/签出一个新分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们给它取名 "},{"type":"codeinline","content":[{"type":"text","text":"add_patrick"}]},{"type":"text","text":",在这个分支上加了一个新的文件"},{"type":"codeinline","content":[{"type":"text","text":"Patrick.txt"}]},{"type":"text","text":"然后提交,并附上提交信息:'Add Patrick'。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当你在这个分支添加提交后,切回到 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" ,同样进行更改并提交。这里我在"},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"文件里添加了一些文本。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就像在我们的合并示例中,这两个分支的历史在一个共同的祖先处发生了分歧,如下图所示。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/84\/841ce1b655a645c900193d34342fcf65.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在让我再次执行"},{"type":"codeinline","content":[{"type":"text","text":"checkout add_patrick"}]},{"type":"text","text":", 并将在 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 上所做的更改添加到我们正在处理的分支中!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当我们执行 "},{"type":"codeinline","content":[{"type":"text","text":"git rebase master"}]},{"type":"text","text":", 我们将"},{"type":"codeinline","content":[{"type":"text","text":"add_patrick"}]},{"type":"text","text":"分支修改为基于当前 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 分支的状态。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"命令行的输出提示很好的告诉了我们发生了什么:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"First, rewinding head to replay your work on top of it...\n(首先,倒回头部,重新将你的工作放置在他的开头...)\nApplying: Add Patrick\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们还记得,在我们的开发环境中, "},{"type":"text","marks":[{"type":"italic"}],"text":"HEAD"},{"type":"text","text":" 指向的是当前提交的指针。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在变基开始之前,它和"},{"type":"codeinline","content":[{"type":"text","text":"add_patrick"}]},{"type":"text","text":" 指向的是同一个地方。变基的过程中,它首先移动回共同的祖先处,然后移动到我们想要重新建立的分支的头部。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此, "},{"type":"text","marks":[{"type":"italic"}],"text":"HEAD"},{"type":"text","text":" 从 "},{"type":"text","marks":[{"type":"italic"}],"text":"0cfc1d2"},{"type":"text","text":" 的提交移动到位于 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 头部的 "},{"type":"text","marks":[{"type":"italic"}],"text":"7639f4b"},{"type":"text","text":" 提交处。然后变基会将我们在 "},{"type":"codeinline","content":[{"type":"text","text":"add_patrick"}]},{"type":"text","text":" 分支上所做的每一次提交都应用到该分支中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更确切地说, "},{"type":"text","marks":[{"type":"italic"}],"text":"git"},{"type":"text","text":" 在 "},{"type":"text","marks":[{"type":"italic"}],"text":"HEAD"},{"type":"text","text":" 移回分支的共同祖先之后,会存储你在分支上所做的每一次提交(包括不同的改动,以及提交文本、作者等)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之后,它会切换到你基于它创建的新分支,然后将存储的所有改动在你的新分支上作一个新的提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此,在我们最初的简化视图中,可以假设在 "},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":" 之后, "},{"type":"text","marks":[{"type":"italic"}],"text":"0cfc1d2"},{"type":"text","text":" 提交不再指向历史上的共同祖先,而是指向了 master的头部。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事实上,"},{"type":"text","marks":[{"type":"italic"}],"text":"0cfc1d2"},{"type":"text","text":" 提交已经不存在了,"},{"type":"codeinline","content":[{"type":"text","text":"add_patrick"}]},{"type":"text","text":" 分支以一个新的 "},{"type":"text","marks":[{"type":"italic"}],"text":"0ccaba8"},{"type":"text","text":" 提交开始,它的祖先是 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 的最新提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它看起来,就像我们的 "},{"type":"codeinline","content":[{"type":"text","text":"add_patrick"}]},{"type":"text","text":" 是基于当前的 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":",而不是它的旧版本,其实在这里我们重新书写了分支的历史。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在本教程的最后,我们将学习更多关于重写历史的知识,以及什么时候适合这样做,什么时候不适合。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/f3\/f3335758205d9b3b8ea999753270a4ae.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当你基于一个共享的分支,如 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 检出一个自己的开发分支,在上面工作时,"},{"type":"codeinline","content":[{"type":"text","text":"Rebase"}]},{"type":"text","text":" 是一个非常强大的工具。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用"},{"type":"codeinline","content":[{"type":"text","text":"Rebase"}]},{"type":"text","text":",有助于你经常整合其他人所做的更改并将其推送到 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" ,同时保持一个清晰的线性历史记录,以便在需要将你的改动作放入共享分支时进行 "},{"type":"codeinline","content":[{"type":"text","text":"fast-forward merge"}]},{"type":"text","text":" 快进合并。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"保持线性的历史记录在查看提交日志时比使用 "},{"type":"text","marks":[{"type":"italic"}],"text":"merge commits"},{"type":"text","text":" 混合提交(通常只使用默认文本)更有用。(可以尝试 "},{"type":"codeinline","content":[{"type":"text","text":"git log --graph"}]},{"type":"text","text":" 或查看 "},{"type":"text","marks":[{"type":"italic"}],"text":"GitHub"},{"type":"text","text":" or "},{"type":"text","marks":[{"type":"italic"}],"text":"GitLab"},{"type":"text","text":" 的分支视图)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"解决冲突"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在使用"},{"type":"codeinline","content":[{"type":"text","text":"merge"}]},{"type":"text","text":"合并分支时,如果两个提交更改了文件的同一个部分,则可能会发生冲突。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,当你在"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":" 时遇到冲突时,你不会在额外的 "},{"type":"text","marks":[{"type":"italic"}],"text":"merge commit"},{"type":"text","text":" 中修复它,而是在当前所在的提交中简单地解决它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同样地,直接根据原始分支的当前状态进行更改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"实际上,"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":" 解决冲突与 "},{"type":"codeinline","content":[{"type":"text","text":"merge"}]},{"type":"text","text":"的方法非常相似,如果你不记得怎么做了,请回顾该部分内容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"唯一的区别是,由于你没有引入 "},{"type":"text","marks":[{"type":"italic"}],"text":"merge commit"},{"type":"text","text":" (合并提交),因此没有必要提交你的解决方案。只需使用"},{"type":"codeinline","content":[{"type":"text","text":"add"}]},{"type":"text","text":" 将改动添加到暂存区,然后执行 "},{"type":"codeinline","content":[{"type":"text","text":"git rebase --continue"}]},{"type":"text","text":",冲突将在刚刚的提交中解决。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在合并时,当你执行 "},{"type":"codeinline","content":[{"type":"text","text":"git rebase --abort"}]},{"type":"text","text":"之前,你始终可以停止并放弃到目前为止所做的一切。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"根据远程仓库更新本地开发环境"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到目前为止,我们只学会了如何创建和分享我们的改动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你刚刚做的所有操作通常也是其他人会做的,因此我们需要知道怎么从远程仓库获取到他们提交的改动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"过了这么久,让我们再来回顾一下git的组件:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/24\/2464da0c7d496218ce816e2bb90970d6.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就像你的开发环境一样,其他人也都有他们的开发环境。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/c0\/c0529bf5cf59898521c2a948f0830711.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有这些开发环境中保留着他们自己的本地改动和暂存的改动,这些更改在某个时刻提交到他们的本地仓库,最后被推送到远程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在下面的例子中,我们将使用 "},{"type":"text","marks":[{"type":"italic"}],"text":"GitHub"},{"type":"text","text":" 提供的在线工具来模拟其他人在我们工作时对 "},{"type":"text","marks":[{"type":"italic"}],"text":"remote"},{"type":"text","text":" (远端)进行的更改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"进入你通过 "},{"type":"codeinline","content":[{"type":"text","text":"fork"}]},{"type":"text","text":" 拷贝了此资源的github.com地址并且打开"},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":" 文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"找到编辑按钮,进行更改,并直接通过网站提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/43\/4301a7038b07c6dc28ba78d1e9156e4d.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在这个库中,我已在一个名为"},{"type":"codeinline","content":[{"type":"text","text":"fetching_changes_sample"}]},{"type":"text","text":"的分支上将远程更改添加到了"},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":" ,但是在你的仓库中,你可以直接在 "},{"type":"codeinline","content":[{"type":"text","text":"master"}]},{"type":"text","text":"分支上更改文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"获取改动"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们还记得,当你使用 "},{"type":"codeinline","content":[{"type":"text","text":"git push"}]},{"type":"text","text":"时,你会将本地仓库的更改同步到远程仓库中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要把 "},{"type":"text","marks":[{"type":"italic"}],"text":"远程"},{"type":"text","text":" 所做的更改保存到本地仓库中,请使用 "},{"type":"codeinline","content":[{"type":"text","text":"git fetch"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这会将远程服务器上的任何更改(包括提交和分支)保存到你的本地仓库中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意,此时,更改还没有集成到本地分支中,因此也还没有集成到你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 和 "},{"type":"text","marks":[{"type":"italic"}],"text":"暂存区"},{"type":"text","text":" 中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/c8\/c8ee8e6acbdd41f7be59e5fe1cc33399.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果现在运行 "},{"type":"codeinline","content":[{"type":"text","text":"git status"}]},{"type":"text","text":" ,你将看到另一个很好的git命令示例,它告诉你到底发生了什么:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"> git status\nOn branch fetching_changes_sample\nYour branch is behind 'origin\/fetching_changes_sample' by 1 commit, and can be fast-forwarded.\n (use \"git pull\" to update your local branch)\n\n在fetching_changes_sample分支上,你的分支落后于远程的'origin\/fetching_changes_sample'一个提交,可以被快进合并\n(使用“git pull”命令去更新你的本地分支)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"拉取改动"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在我们的 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 或 "},{"type":"text","marks":[{"type":"italic"}],"text":"暂存区"},{"type":"text","text":" 没有改动时,我们可以执行 "},{"type":"codeinline","content":[{"type":"text","text":"git pull"}]},{"type":"text","text":" 将来自远程的更改拉取到我们的工作区。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Pulling拉取操作也会隐式地"},{"type":"codeinline","content":[{"type":"text","text":"fetch"}]},{"type":"text","text":"拉取 远程仓库,但有时单独执行"},{"type":"codeinline","content":[{"type":"text","text":"fetch"}]},{"type":"text","text":"是个好主意。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如,当你要同步任何新的远程分支时,或者你希望你在 "},{"type":"codeinline","content":[{"type":"text","text":"origin\/master"}]},{"type":"text","text":"分支上执行"},{"type":"codeinline","content":[{"type":"text","text":"git rebase"}]},{"type":"text","text":" 之前确保你的本地仓库是最新的时候。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/99\/99df5c0b7832212a4f611cb34a282f1b.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在我们执行 "},{"type":"codeinline","content":[{"type":"text","text":"pull"}]},{"type":"text","text":"之前,让我们改动一个本地文件看看会发生什么。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里我们同样在本地工作区修改 "},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":" 文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你现在试着执行 "},{"type":"codeinline","content":[{"type":"text","text":"git pull"}]},{"type":"text","text":",你会看到下面这样的报错:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"> git pull\nUpdating df3ad1d..418e6f0\nerror: Your local changes to the following files would be overwritten by merge:\n Alice.txt\nPlease commit your changes or stash them before you merge.\n(错误:被跟踪的本地文件alice.txt在合并时会被重写,请在合并提交(commit)或存储(stash)你的改动)\nAborting\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你无法拉取任何改动,因为在你执行pull操作时,你的本地工作区间文件的改动会被这次提交重写。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此时其中一个方法是,在你最终提交这些本地改动前,将他们添加到一个储藏区,这里有一个好用的工具: "},{"type":"codeinline","content":[{"type":"text","text":"git stash"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"存储改动"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果某些时候,你有不想放入commit中的本地改动,或者在尝试以不同的角度解决问题时希望将其存储在某个位置,则可以使用 "},{"type":"codeinline","content":[{"type":"text","text":"stash"}]},{"type":"text","text":"将这些更改储藏起来。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"git stash"}]},{"type":"text","text":" 储藏的基本上是你在 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 中做的一些更改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以使用命令 "},{"type":"codeinline","content":[{"type":"text","text":"git stash"}]},{"type":"text","text":"将工作目录的任何修改放入储藏区,然后使用命令"},{"type":"codeinline","content":[{"type":"text","text":"git stash pop"}]},{"type":"text","text":" 将最近储藏的改动取出并再次应用在你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"工作目录"},{"type":"text","text":" 上。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如stash命令这个堆栈名字一样,"},{"type":"codeinline","content":[{"type":"text","text":"git stash pop"}]},{"type":"text","text":" 命令会在再次应用之前删除最新存储的改动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你希望保留储藏的改动,你可以使用"},{"type":"codeinline","content":[{"type":"text","text":"git stash apply"}]},{"type":"text","text":",这样在你应用更改之前便不会将其从堆栈中删除。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要检查当前已有的存储,你可以使用 "},{"type":"codeinline","content":[{"type":"text","text":"git stash list"}]},{"type":"text","text":" 列出所有储藏的条目,使用 "},{"type":"codeinline","content":[{"type":"text","text":"git stash show"}]},{"type":"text","text":" 显示储藏的最新的更改。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另一个很便捷的命令是 "},{"type":"codeinline","content":[{"type":"text","text":"git stash branch {BRANCH NAME}"}]},{"type":"text","text":",它会从保存更改时的HEAD处创建一个分支,并将你准备储藏的改动应用在这个分支上。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在我们知道了 "},{"type":"codeinline","content":[{"type":"text","text":"git stash"}]},{"type":"text","text":"的作用,让我们运行它来删除本地工作目录中 "},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"的改动,这样我们就可以继续使用"},{"type":"codeinline","content":[{"type":"text","text":"git pull"}]},{"type":"text","text":" 从远程获取更改了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后,再执行"},{"type":"codeinline","content":[{"type":"text","text":"git stash pop"}]},{"type":"text","text":" 把本地的改动取回。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因为我们pull的远程仓库和储藏的更改都修改了 "},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":" 文件,所以这里需要解决冲突,就像前面 "},{"type":"codeinline","content":[{"type":"text","text":"merge"}]},{"type":"text","text":" 或"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"提到的那样的做法来解决,然后通过 "},{"type":"codeinline","content":[{"type":"text","text":"add"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"commit"}]},{"type":"text","text":"提交这次改动就好了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"拉取有冲突的内容"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在我们已经理解了怎么通过 "},{"type":"codeinline","content":[{"type":"text","text":"fetch"}]},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"pull"}]},{"type":"text","text":"去拉取远程改动到我们的开发环境,接下来让我们去制造一些冲突。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不用推送你改动的"},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"文件,直接回到你的github.com上的远程仓库。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里我们继续修改"},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"文件然后提交这次改动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在,实际上在我们的本地和远程仓库已经有两了个冲突。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不要忘了执行"},{"type":"codeinline","content":[{"type":"text","text":"git fetch"}]},{"type":"text","text":"来查看远程修改,而不是直接执行"},{"type":"codeinline","content":[{"type":"text","text":"pull"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你现在执行"},{"type":"codeinline","content":[{"type":"text","text":"git status"}]},{"type":"text","text":" 你会看到,这两个分支上都有一处与另一个分支不同的改动"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"> git status\nOn branch fetching_changes_sample\nYour branch and 'origin\/fetching_changes_sample' have diverged,\nand have 1 and 1 different commits each, respectively.\n (use \"git pull\" to merge the remote branch into yours)\n分支fetching_changes_sample与origin\/fetching_changes_sample发生分歧,各自都有一次提交。\n(使用git pull 将远程的分支合并进你的分支)\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此外,我们在两个分支上都对同一个文件做了修改,从而制造出了这个需要我们解决的合并冲突。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当你执行"},{"type":"codeinline","content":[{"type":"text","text":"git pull"}]},{"type":"text","text":",本地和远程仓库会发生冲突,这和之前合并两个分支时出现的情况一样。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由此,你可以将远程仓库和本地仓库出现的冲突当成是基于一个分支创建了另一个分支,这两个分支出现冲突。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本地分支是基于你上次 "},{"type":"codeinline","content":[{"type":"text","text":"fetched"}]},{"type":"text","text":" 拉取远程分支时的状态。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从这个角度看,从获取远程改动要做的这两个选择是很有意义的:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当你执行"},{"type":"codeinline","content":[{"type":"text","text":"git pull"}]},{"type":"text","text":"时,本地和远程分支会被合并。就像合并分支一样,这里会创建一个合并的commit提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因为任何一个本地分支都是基于其各自的远程版本,所以我们也可以对它进行"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"变基,这样我们在本地所做的任何更改都会看起来像是基于远程仓库的最新改动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为此,我们可以执行 "},{"type":"codeinline","content":[{"type":"text","text":"git pull --rebase"}]},{"type":"text","text":" (或者简写成 "},{"type":"codeinline","content":[{"type":"text","text":"git pull -r"}]},{"type":"text","text":")。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如变基一节中阐述的,保持一个清晰的历史记录很重要,这就是为什么我建议无论何时你执行"},{"type":"codeinline","content":[{"type":"text","text":"git pull"}]},{"type":"text","text":",最好选择执行"},{"type":"codeinline","content":[{"type":"text","text":"git pull -r"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你也可以告诉git在使用"},{"type":"codeinline","content":[{"type":"text","text":"git pull"}]},{"type":"text","text":"用 "},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"而不是"},{"type":"codeinline","content":[{"type":"text","text":"merge"}]},{"type":"text","text":" 作为它的默认方式,通过"},{"type":"codeinline","content":[{"type":"text","text":"pull.rebase"}]},{"type":"text","text":"指令如"},{"type":"codeinline","content":[{"type":"text","text":"git config --global pull.rebase true"}]},{"type":"text","text":"可完成设置。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你在我前几段第一次提到获取更新时已经执行了 "},{"type":"codeinline","content":[{"type":"text","text":"git pull"}]},{"type":"text","text":",那么现在可以运行"},{"type":"codeinline","content":[{"type":"text","text":"git pull -r"}]},{"type":"text","text":"来获取远程更改,这样我们新的提交看起来像是在拉取了远程之后才更新的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当然,就像使用"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"(或 "},{"type":"codeinline","content":[{"type":"text","text":"merge"}]},{"type":"text","text":")一样,你也需要在 "},{"type":"codeinline","content":[{"type":"text","text":"git pull"}]},{"type":"text","text":"前解决引入的冲突。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"拣选"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"恭喜你!你已经完成了更高级的功能!现在你已经了解了如何使用所有典型的git命令,以及它们是如何工作的。希望这有助于你理解下面要说的概念。接下来,让我们开始学习如何拣选我们的提交吧!"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"回顾前面的章节内容,你应该还大致记得什么是 "},{"type":"codeinline","content":[{"type":"text","text":"commit"}]},{"type":"text","text":" 提交,对吧?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以及当你"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"一个分支时,你的提交是怎么带着同样的改动和信息作为一个新的commit提交的?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"无论何时,只要你想从一个分支获取一些更改并将它们应用到另一个分支,你都需要"},{"type":"codeinline","content":[{"type":"text","text":"cherry-pick"}]},{"type":"text","text":"拣选这些提交并将它们放到你的分支上。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"git cherry-pick"}]},{"type":"text","text":" 允许你对单个提交或一系列提交执行操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就像 "},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":" 的过程一样,这实际上会将这些提交的更改放入当前分支的新提交中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"让我们看一下有一个及多个提交时,执行"},{"type":"codeinline","content":[{"type":"text","text":"cherry-pick"}]},{"type":"text","text":"的示例。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下图显示了我们做任何操作之前的三个分支。假设我们想从"},{"type":"codeinline","content":[{"type":"text","text":"add_patrick"}]},{"type":"text","text":"分支获取一些更改到 "},{"type":"codeinline","content":[{"type":"text","text":"change_alice"}]},{"type":"text","text":"分支。遗憾的是,它们都还没有合进master,所以我们不能仅仅通过 "},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"来获得这些更改(我们可能也不需要其他分支的其余的改动)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/65\/655556ff431d7b1506ca86aae8955ea6.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此让我们执行 "},{"type":"codeinline","content":[{"type":"text","text":"git cherry-pick"}]},{"type":"text","text":" 拣选 "},{"type":"text","marks":[{"type":"italic"}],"text":"63fc421"},{"type":"text","text":" 的这次提交. 下图清晰地显示了运行 "},{"type":"codeinline","content":[{"type":"text","text":"git cherry-pick 63fc421"}]},{"type":"text","text":"时发生的事情:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/cf\/cf02c3faa54d766ee85241c0f802a36d.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如你所见,分支上出现了一个新的带着我们需要改动的commit提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这时请注意,就像我们之前所见的合并分支改动一样,在执行下一步命令前,我们需要解决执行"},{"type":"codeinline","content":[{"type":"text","text":"cherry-pick"}]},{"type":"text","text":"时出现的任何冲突。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"解决完冲突后,和之前一样,你可以继续执行拣选"},{"type":"codeinline","content":[{"type":"text","text":"cherry-pick"}]},{"type":"text","text":",或者决定直接"},{"type":"codeinline","content":[{"type":"text","text":"--abort"}]},{"type":"text","text":"中止该命令。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下图显示了使用拣选("},{"type":"codeinline","content":[{"type":"text","text":"cherry-pick"}]},{"type":"text","text":")选择一系列提交而不是单个提交。你只需调用形式为"},{"type":"codeinline","content":[{"type":"text","text":"git cherry-pick .."}]},{"type":"text","text":"的命令,或者如下面的示例调用"},{"type":"codeinline","content":[{"type":"text","text":"git cherry-pick 0cfc1d2..41fbfa7"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/45\/4582d5684e19da0101eb0bd582305a26.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"改写历史"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我现在再问一遍,你还记得"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":" 吗?不记得的话可以跳回去快速浏览一遍,因为下面学习如何改写历史需要使用到这部分知识哦!"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你已经知道了一个基础的commit提交包含了你的改动、提交信息和一些其他的内容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所谓分支的'历史' 就是由所有提交组成的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是假设你刚刚提交了一个"},{"type":"codeinline","content":[{"type":"text","text":"commit"}]},{"type":"text","text":",然后发现你忘了添加一个文件,或者你犯了一个错误,而这个改变给你留下了一个坏的代码,怎么办呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这种情况下,我们将简要介绍两种解决方式,使它看起来像从未发生过一样。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"让我们执行"},{"type":"codeinline","content":[{"type":"text","text":"git checkout -b rewrite_history"}]},{"type":"text","text":",切换到一个新的分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在让我们在 "},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"Bob.txt"}]},{"type":"text","text":"文件中做一些改动,然后执行 "},{"type":"codeinline","content":[{"type":"text","text":"git add Alice.txt"}]},{"type":"text","text":"。然后带上信息\"This is history\" 提交("},{"type":"codeinline","content":[{"type":"text","text":"git commit"}]},{"type":"text","text":")。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"等等,我刚刚做了什么?你应该知道我刚刚做错什么了吧!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我忘了添加"},{"type":"codeinline","content":[{"type":"text","text":"Bob.txt"}]},{"type":"text","text":"文件。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我也没有提交一个友好的commit信息。(友好的书写commit可见:good commit message)"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"修改最后一次提交"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一次性解决这两个问题的办法是是修改("},{"type":"codeinline","content":[{"type":"text","text":"amend"}]},{"type":"text","text":" )我们刚刚提交的commit。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修改最新的提交基本上就像创建一个新的提交一样。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在我们做任何事情之前,先看看你最近的提交("},{"type":"codeinline","content":[{"type":"text","text":"git show {COMMIT}"}]},{"type":"text","text":")。COMMIT中输入commit的hash值(hash值可以通过调用命令行 "},{"type":"codeinline","content":[{"type":"text","text":"git commit"}]},{"type":"text","text":"或"},{"type":"codeinline","content":[{"type":"text","text":"git log"}]},{"type":"text","text":"看到),或者直接使用 "},{"type":"text","marks":[{"type":"italic"}],"text":"HEAD"},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就像"},{"type":"codeinline","content":[{"type":"text","text":"git log"}]},{"type":"text","text":"打印出的结果一样,你会看到消息、作者、日期,当然还有改动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在让我们修正我们在那次提交中所做的改动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"执行"},{"type":"codeinline","content":[{"type":"text","text":"git add Bob.txt"}]},{"type":"text","text":"将改动添加到暂存区, 然后执行 "},{"type":"codeinline","content":[{"type":"text","text":"git commit --amend"}]},{"type":"text","text":"."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来你会看到最新的提交被展开,新的改动被添加到暂存区已有的改动中。commit提交消息的编辑器被打开。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在编辑器中,你将看到前面的提交消息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以把它修改成更友好的提交信息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"做完这些之后,执行"},{"type":"codeinline","content":[{"type":"text","text":"git show HEAD"}]},{"type":"text","text":"看一下最新的提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如你预期的那样,提交的哈希改变了。原来的提交不见了,取而代之的是一个新的提交,它包含了组合后的更改和新的提交消息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"请注意其他提交数据(如author和date)与原始提交相比是保持不变的。如果需要,你也可以在修改时使用"},{"type":"codeinline","content":[{"type":"text","text":"--author={AUTHOR}"}]},{"type":"text","text":" 和"},{"type":"codeinline","content":[{"type":"text","text":"--date={DATE}"}]},{"type":"text","text":" 来修改。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"祝贺你!你第一次成功地改写了历史!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"交互式变基"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常,当我们执行"},{"type":"codeinline","content":[{"type":"text","text":"git rebase"}]},{"type":"text","text":"时,我们变基到一个分支上。当我们执行"},{"type":"codeinline","content":[{"type":"text","text":"git rebase origin\/master"}]},{"type":"text","text":"之类的操作时,实际是将一个指针重新定位到到该分支的 "},{"type":"text","marks":[{"type":"italic"}],"text":"HEAD"},{"type":"text","text":" 指针上。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事实上只要我们喜欢,我们可以 "},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"变基到任何的commit上"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"请记住,提交包含了之前的历史记录"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"和许多其他命令一样,"},{"type":"codeinline","content":[{"type":"text","text":"git rebase"}]},{"type":"text","text":"有一个“交互式”模式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"与其他大多数命令不同的是,交互式的 "},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"可能是你经常使用的东西,因为它允许你随意改变历史。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"尤其是如果你的更改有许多的小提交,这样一旦你犯了错误,你可以很容易地跳回原位,那么交互式的 "},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":" 将是你最亲密的盟友。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"话不多说!让我们做点什么吧!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"切换回你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 分支,然后执行"},{"type":"codeinline","content":[{"type":"text","text":"git checkout"}]},{"type":"text","text":" 检出一个新分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"像之前一样,我们会在"},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"和 "},{"type":"codeinline","content":[{"type":"text","text":"Bob.txt"}]},{"type":"text","text":"文件上做一些更改,然后添加"},{"type":"codeinline","content":[{"type":"text","text":"git add Alice.txt"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然后我们执行 "},{"type":"codeinline","content":[{"type":"text","text":"git commit"}]},{"type":"text","text":"来提交它,提交信息可以是\"Add text to Alice\""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在不改变那次提交,我们继续执行"},{"type":"codeinline","content":[{"type":"text","text":"git add Bob.txt"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"git commit"}]},{"type":"text","text":",这里我用的提交信息是\"Add Bob.txt\""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了让事情变得更有趣,我们在"},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"文件上做一些改动,然后提交:执行"},{"type":"codeinline","content":[{"type":"text","text":"git add"}]},{"type":"text","text":" 和"},{"type":"codeinline","content":[{"type":"text","text":"git commit"}]},{"type":"text","text":",带上提交信息\"Add more text to Alice\"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我们现在执行"},{"type":"codeinline","content":[{"type":"text","text":"git log"}]},{"type":"text","text":"查看一下分支历史(或者也可以用"},{"type":"codeinline","content":[{"type":"text","text":"git log --oneline"}]},{"type":"text","text":"快速的查看一下),我们将看到在 "},{"type":"text","marks":[{"type":"italic"}],"text":"master"},{"type":"text","text":" 上的三个提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我这边看起来是这样的:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"> git log --oneline\n0b22064 (HEAD -> interactiveRebase) Add more text to Alice\n062ef13 Add Bob.txt\n9e06fca Add text to Alice\ndf3ad1d (origin\/master, origin\/HEAD, master) Add Alice\n800a947 Add Tutorial Text\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们想做下面两件事情,为了学习不同的内容,这将有点不同于上一节的"},{"type":"codeinline","content":[{"type":"text","text":"amend"}]},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"将"},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"的两次改动作为一次commit提交"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"保持修改bob文件那次commit信息中bob的名字,将"},{"type":"codeinline","content":[{"type":"text","text":"Bob.txt"}]},{"type":"text","text":"后面的 "},{"type":"text","marks":[{"type":"italic"}],"text":".txt"},{"type":"text","text":" 信息去掉。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要更改这三个新提交,我们需要重新定位到它们之前的提交。对我来说,这个提交hash值是"},{"type":"codeinline","content":[{"type":"text","text":"df3ad1d"}]},{"type":"text","text":",但是我们也可以引用当前 "},{"type":"text","marks":[{"type":"italic"}],"text":"HEAD"},{"type":"text","text":" 的往前第三次提交:"},{"type":"codeinline","content":[{"type":"text","text":"HEAD~3"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要启动一个交互式的"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":",我们使用"},{"type":"codeinline","content":[{"type":"text","text":"git rebase -i {COMMIT}"}]},{"type":"text","text":", 这里执行的是 "},{"type":"codeinline","content":[{"type":"text","text":"git rebase -i HEAD~3"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你将看到你选择的编辑器显示如下内容:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"pick 9e06fca Add text to Alice\npick 062ef13 Add Bob.txt\npick 0b22064 Add more text to Alice\n\n# Rebase df3ad1d..0b22064 onto df3ad1d (3 commands)\n#\n# Commands:\n# p, pick = use commit\n# r, reword = use commit, but edit the commit message\n# e, edit = use commit, but stop for amending\n# s, squash = use commit, but meld into previous commit\n# f, fixup = like \"squash\", but discard this commit's log message\n# x, exec = run command (the rest of the line) using shell\n# d, drop = remove commit\n#\n# These lines can be re-ordered; they are executed from top to bottom.\n#\n# If you remove a line here THAT COMMIT WILL BE LOST.\n#\n# However, if you remove everything, the rebase will be aborted.\n#\n# Note that empty commits are commented out\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每次调用命令时请留意,git有关于你如何执行这些操作的说明。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最常用的命令是"},{"type":"codeinline","content":[{"type":"text","text":"reword"}]},{"type":"text","text":"、 "},{"type":"codeinline","content":[{"type":"text","text":"squash"}]},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"drop"}]},{"type":"text","text":"。(还有"},{"type":"codeinline","content":[{"type":"text","text":"pick"}]},{"type":"text","text":",默认情况下是这些。)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"花点时间想想你看到了什么,以及我们将用什么实现我们的两个目标,等你思考一下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有计划了吗?完美!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在开始进行更改之前,请注意这样一个事实,提交是从最旧到最新的顺序列出的,因此 "},{"type":"codeinline","content":[{"type":"text","text":"git log"}]},{"type":"text","text":" 输出的方向相反。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我将从简单的更改开始进行,因此我们先来修改中间这次提交的提交信息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"pick 9e06fca Add text to Alice\nreword 062ef13 Add Bob.txt\npick 0b22064 Add more text to Alice\n\n# Rebase df3ad1d..0b22064 onto df3ad1d (3 commands)\n[...]\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在把 "},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"的两次改动改为一次commit提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"显然,我们要做的是将最后一个提交 "},{"type":"codeinline","content":[{"type":"text","text":"压缩"}]},{"type":"text","text":" 到第一个中,所以让我们"},{"type":"codeinline","content":[{"type":"text","text":"pick"}]},{"type":"text","text":"那次提交后,用"},{"type":"codeinline","content":[{"type":"text","text":"squash"}]},{"type":"text","text":"命令更改 "},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"的提交。在我这个例子中hash是 "},{"type":"text","marks":[{"type":"italic"}],"text":"0b22064"},{"type":"text","text":" 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"pick 9e06fca Add text to Alice\nreword 062ef13 Add Bob.txt\nsquash 0b22064 Add more text to Alice\n\n# Rebase df3ad1d..0b22064 onto df3ad1d (3 commands)\n[...]\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"结束了吗?这能满足我们的要求吗?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不会的,对吧?文件中的说明告诉我们:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"# s, squash = use commit, but meld into previous commit\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以我们到目前为止所做的,将会合并第二次Alice提交和Bob提交的更改。这不是我们想要的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"交互式"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"另一个强大的功能就是改变提交的顺序。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你仔细阅读了评论告诉你的内容,你已经知道怎么做了:简单地移动行!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"谢天谢地,你使用了你最喜欢的文本编辑器,所以请继续将第二个Alice commit移到第一个之后。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"pick 9e06fca Add text to Alice\nsquash 0b22064 Add more text to Alice\nreword 062ef13 Add Bob.txt\n\n# Rebase df3ad1d..0b22064 onto df3ad1d (3 commands)\n[...]\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这样就可以了,然后关闭编辑器,告诉 "},{"type":"codeinline","content":[{"type":"text","text":"git"}]},{"type":"text","text":" 开始执行命令。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来发生的事情就像一个普通的"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":":从启动时引用的提交开始,列出的每个提交都将一个接一个地被应用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在没有发生这样一种情况,但是当你重新排序实际的代码改动时,可能会发生,即你在"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"期间遇到冲突。毕竟你们可能混淆了彼此之间的变化。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"像平常一样解决它们就好。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在应用第一次提交后,编辑器将打开,并允许你为"},{"type":"codeinline","content":[{"type":"text","text":"Alice.txt"}]},{"type":"text","text":"的合并改动添加一条新的提交消息。我已经删掉了两个旧提交的文本,换成了\"Add a lot of very important text to Alice\"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关闭编辑器完成提交后,它将再次打开以允许你更改"},{"type":"codeinline","content":[{"type":"text","text":"Add Bob.txt"}]},{"type":"text","text":"的提交信息。这里我们把“.txt”删除然后关闭编辑器继续。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"就这样!你再次改写了历史。这一次比"},{"type":"codeinline","content":[{"type":"text","text":"amend"}]},{"type":"text","text":"的改动要大得多!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你再次查看"},{"type":"codeinline","content":[{"type":"text","text":"git log"}]},{"type":"text","text":",你将看到有两个新提交取代了我们之前的三个提交。现在你已经熟练使用了 "},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"的操作,并且达到了我们的预期。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"> git log --oneline\n105177b (HEAD -> interactiveRebase) Add Bob\ned78fa1 Add a lot very important text to Alice\ndf3ad1d (origin\/master, origin\/HEAD, master) Add Alice\n800a947 Add Tutorial Text"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"公共历史,为什么你不应该重写它,万一要重写如何保证安全"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如上所述,如果你的工作流程涉及大量小改动的提交,更改分支历史是非常有用的一部分。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虽然所有的小改动有助于简化你的工作,例如,验证你的测试组件是否通过时,如果不通过,只删除或修改这些特定的更改。而你在"},{"type":"codeinline","content":[{"type":"text","text":"HelloWorld.java"}]},{"type":"text","text":" 上面做的100个提交可能不是你想和别人分享的东西。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你最想与他们分享的,是一些带有很好的提交信息的格式良好的更改,告诉你的同事你为此做了什么。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"只要所有这些小的提交只存在于你的开发环境中,你就完全可以安全地执行"},{"type":"codeinline","content":[{"type":"text","text":"git rebase -i"}]},{"type":"text","text":"并将历史更改为你想要的内容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当涉及到改变公共历史时,事情会变得麻烦。这意味着改动任何已经进入远程仓库的内容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这时改动已经成为公共的了,而其他人的分支可能是基于那段历史。你通常是不想改变远程分支的内容的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常的建议是“永远不要重写公共历史!”而当我在这里重复这一点时,我必须承认,在一些情况下,你可能仍然想重写公共历史。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般这种情况,针对的是历史并不是“真正”公开的。你当然不想在开源项目的主分支上重写历史,或者类似于你公司的发布分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可能想重写历史的地方是你刚刚"},{"type":"codeinline","content":[{"type":"text","text":"push"}]},{"type":"text","text":"出来,只是为了和同事们分享的分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可能正在进行基于主干的开发,但是希望共享一些甚至还没有编译的东西,所以你显然不希望有意地将其放在主分支上。或者你可能有一个工作流,在其中共享功能分支。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"特别是功能分支,你希望能经常将它们"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"到当前的主分支上。但正如我们所知 "},{"type":"codeinline","content":[{"type":"text","text":"git rebase"}]},{"type":"text","text":" 会将分支的提交作为新提交添加到我们要合的主分支上。这其实改写了历史。在共享了这个功能分支的情况下,它更是重写了公共历史。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那么,如果我们遵循“永远不重写公共历史”的准则,我们该怎么办?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"怎么做到不"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"我们的分支但是希望它仍然可以将改动合并到我们主分支的末尾呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不使用共享的功能分支?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"诚然,这个答案实际上是一个合理的答案,但你可能仍然无法做到这一点。因此,你唯一能做的就是接受重写公共历史并将更改后的历史“推送”到远程仓库。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你只是做了一个"},{"type":"codeinline","content":[{"type":"text","text":"git push"}]},{"type":"text","text":",你会被通知不允许这样做,因为你的本地分支已经与远程分支分离了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你需要"},{"type":"codeinline","content":[{"type":"text","text":"强制"}]},{"type":"text","text":"推送更改,并用本地版本覆盖远程版本。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如我强调的那样,你现在可能已经准备好尝试"},{"type":"codeinline","content":[{"type":"text","text":"git push --force"}]},{"type":"text","text":" 。如果你想安全地重写公共历史,你真的不应该这么做!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你最好使用 "},{"type":"codeinline","content":[{"type":"text","text":"--force"}]},{"type":"text","text":" 的一个更谨慎的用法:"},{"type":"codeinline","content":[{"type":"text","text":"--force-with-lease"}]},{"type":"text","text":" !"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"--force-with-lease"}]},{"type":"text","text":"将在 "},{"type":"codeinline","content":[{"type":"text","text":"push"}]},{"type":"text","text":"之前,检查你本地版本的远程分支与实际的远程分支是否匹配。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这样你就可以确保,你不会在重写历史的时候无意中抹去可能是别人"},{"type":"codeinline","content":[{"type":"text","text":"push"}]},{"type":"text","text":"的任何改动!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/f1\/f1c5f6585e6206ef07ab8d1bf8dee289.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里我给你留下一句箴言:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"除非你真的确定你在做什么,否则不要重写公共历史。如果你非得这样做,就要保证安全和强制"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"阅读历史"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"了解了你的 "},{"type":"text","marks":[{"type":"italic"}],"text":"开发环境"},{"type":"text","text":" (尤其是 "},{"type":"text","marks":[{"type":"italic"}],"text":"本地仓库"},{"type":"text","text":" )中不同区域之间的差异,以及提交和历史记录是如何工作的,执行"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"应该不会让你感到畏惧了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有时事情还是会出问题。在解决冲突时,你可能执行了"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":" 操作,意外地接受了错误的文件版本。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可能不是你添加的功能,而是你的同事在文件中添加了一行日志记录。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"幸运的是, "},{"type":"codeinline","content":[{"type":"text","text":"git"}]},{"type":"text","text":" 有一个内置的安全功能,称为 "},{"type":"text","marks":[{"type":"italic"}],"text":"Reference Logs"},{"type":"text","text":" ,又名 "},{"type":"codeinline","content":[{"type":"text","text":"reflog"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每当在本地仓库中更新任何引用(如分支的提示)时,就会添加一个引用日志条目。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以无论什么时候,在你 "},{"type":"codeinline","content":[{"type":"text","text":"commit"}]},{"type":"text","text":" 改动,"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":" 或以其他方式移动 "},{"type":"codeinline","content":[{"type":"text","text":"HEAD"}]},{"type":"text","text":"时,都会有一个记录。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本教程看到这里,你应该已经知道当我们搞乱了一个"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"时,这个方法是如何派上用场的,对吧?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们知道"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"会将分支的"},{"type":"codeinline","content":[{"type":"text","text":"HEAD"}]},{"type":"text","text":" 移动到我们所基于的位置,然后应用我们的更改。交互式 "},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"的工作原理与之类似,但可能会对提交执行某些操作,例如“压缩”或“重写”。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你没有在前面提到交互式变基时在分支上练习过,请再次切换回那部分,我们在那里有很多的练习和说明。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"让我们看看通过运行 "},{"type":"codeinline","content":[{"type":"text","text":"git reflog"}]},{"type":"text","text":",我们在这个分支上所做的事情。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可能会看到很多输出,但顶部的前几行应该与以下内容类似:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"> git reflog\n105177b (HEAD -> interactiveRebase) HEAD@{0}: rebase -i (finish): returning to refs\/heads\/interactiveRebase\n105177b (HEAD -> interactiveRebase) HEAD@{1}: rebase -i (reword): Add Bob\ned78fa1 HEAD@{2}: rebase -i (squash): Add a lot very important text to Alice\n9e06fca HEAD@{3}: rebase -i (start): checkout HEAD~3\n0b22064 HEAD@{4}: commit: Add more text to Alice\n062ef13 HEAD@{5}: commit: Add Bob.txt\n9e06fca HEAD@{6}: commit: Add text to Alice\ndf3ad1d (origin\/master, origin\/HEAD, master) HEAD@{7}: checkout: moving from master to interactiveRebase\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"情况正是如此,这里有我们所做的每一件事,从切换分支到"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到我们所做的事情很酷,但是如果我们在某个地方搞砸了,如果不是因为每行开头的hash值,那就没办法了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果将"},{"type":"codeinline","content":[{"type":"text","text":"reflog"}]},{"type":"text","text":"与上一次查看"},{"type":"codeinline","content":[{"type":"text","text":"log"}]},{"type":"text","text":" 时的输出进行比较,就会发现这些节点就是提交时引用的,我们可以像之前那样去调用它们。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但假设我们实际上不想使用"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"。我们要如何删除这次所做的改变呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们可以将 "},{"type":"codeinline","content":[{"type":"text","text":"HEAD"}]},{"type":"text","text":" 移到"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"之前的点,执行"},{"type":"codeinline","content":[{"type":"text","text":"git reset 0b22064"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在这个例子中, "},{"type":"codeinline","content":[{"type":"text","text":"0b22064"}]},{"type":"text","text":" 是"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"之前的提交。通常,你也可以通过"},{"type":"codeinline","content":[{"type":"text","text":"HEAD@{4}"}]},{"type":"text","text":"取“HEAD四次前”的提交。请注意,如果你在这两者之间切换了分支,或者执行了创建日志项的任何其他操作,则可能会有一个更大的数字。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果现在查看 "},{"type":"codeinline","content":[{"type":"text","text":"log"}]},{"type":"text","text":",你将看到三个提交前的原始状态。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但这也许并不是我们想要的。"},{"type":"codeinline","content":[{"type":"text","text":"rebase"}]},{"type":"text","text":"很好,我们只是不喜欢这种更改Bob commit信息的方式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那么我们可以在当前状态下再执行一次"},{"type":"codeinline","content":[{"type":"text","text":"rebase -i"}]},{"type":"text","text":",就像我们一开始那样。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"或使用"},{"type":"codeinline","content":[{"type":"text","text":"reflog"}]},{"type":"text","text":"重新跳回rebase之后并从那里“修改”提交。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"现在你已经知道怎么做了,自己来试试吧。除此之外,你还知道,有一个"},{"type":"codeinline","content":[{"type":"text","text":"reflog"}]},{"type":"text","text":"命令,它允许你撤销大多数错误的改动。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"头图:Unsplash"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者:刘诗琴"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文:https:\/\/mp.weixin.qq.com\/s\/Kxlipuppuzcb9qKx8ZE_sQ"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文:交互式教程!带你深入理解 Git 原理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"来源:微医大前端技术 - 微信公众号 [ID:wed_fed]"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章