diff --git a/book/07-git-tools/sections/advanced-merging.asc b/book/07-git-tools/sections/advanced-merging.asc index 051eb5c8..b186643f 100644 --- a/book/07-git-tools/sections/advanced-merging.asc +++ b/book/07-git-tools/sections/advanced-merging.asc @@ -1,19 +1,19 @@ [[_advanced_merging]] -=== Advanced Merging +=== 高级合并 -Merging in Git is typically fairly easy. Since Git makes it easy to merge another branch multiple times, it means that you can have a very long lived branch but you can keep it up to date as you go, solving small conflicts often, rather than be surprised by one enormous conflict at the end of the series. +在 Git 中合并是相当容易的。因为 Git 使多次合并另一个分支变得很容易,这意味着你可以有一个始终保持最新的长期分支,经常解决小的冲突,比在一系列提交后解决一个巨大的冲突要好。 -However, sometimes tricky conflicts do occur. Unlike some other version control systems, Git does not try to be overly clever about merge conflict resolution. Git's philosophy is to be smart about determining when a merge resolution is unambiguous, but if there is a conflict, it does not try to be clever about automatically resolving it. Therefore, if you wait too long to merge two branches that diverge quickly, you can run into some issues. +然而,有时也会有棘手的冲突。不像其他的版本控制系统,Git 并不会尝试过于聪明的合并冲突解决方案。Git 的哲学是聪明地决定无歧义的合并方案,但是如果有冲突,它不会尝试智能地自动解决它。因此,如果很久之后才合并两个分叉的分支,你可能会撞上一些问题。 -In this section, we'll go over what some of those issues might be and what tools Git gives you to help handle these more tricky situations. We'll also cover some of the different, non-standard types of merges you can do, as well as see how to back out of merges that you've done. +在本节中,我们将会仔细查看那些问题是什么以及 Git 给了我们什么工具来帮助我们处理这些更难办的情形。我们也会了解你可以做的不同的、非标准类型的合并,也会看到如何后退到合并之前。 -==== Merge Conflicts +==== 合并冲突 -While we covered some basics on resolving merge conflicts in <<_basic_merge_conflicts>>, for more complex conflicts, Git provides a few tools to help you figure out what's going on and how to better deal with the conflict. +我们在 <<_basic_merge_conflicts>> 介绍了解决合并冲突的一些基础知识,对于更复杂的冲突,Git 提供了几个工具来帮助你指出将会发生什么以及如何更好地处理冲突。 -First of all, if at all possible, try to make sure your working directory is clean before doing a merge that may have conflicts. If you have work in progress, either commit it to a temporary branch or stash it. This makes it so that you can undo *anything* you try here. If you have unsaved changes in your working directory when you try a merge, some of these tips may help you lose that work. +首先,在做一次可能有冲突的合并前尽可能保证工作目录是干净的。如果你有正在做的工作,要么提交到一个临时分支要么储藏它。这使你可以撤消在这里尝试做的*任何事情*。如果在你尝试一次合并时工作目录中有未保存的改动,下面的这些技巧可能会使你丢失那些工作。 -Let's walk through a very simple example. We have a super simple Ruby file that prints 'hello world'. +让我们通过一个非常简单的例子来了解一下。我们有一个超级简单的打印 'hello world' 的 Ruby 文件。 [source,ruby] ---- @@ -26,7 +26,7 @@ end hello() ---- -In our repository, we create a new branch named `whitespace` and proceed to change all the Unix line endings to DOS line endings, essentially changing every line of the file, but just with whitespace. Then we change the line ``hello world'' to ``hello mundo''. +在我们的仓库中,创建一个名为 `whitespace` 的新分支并将所有 Unix 换行符修改为 DOS 换行符,实质上虽然改变了文件的每一行,但改变的都只是空白字符。然后我们修改行 ``hello world'' 为 ``hello mundo''。 [source,console] ---- @@ -60,7 +60,7 @@ $ git commit -am 'hello mundo change' 1 file changed, 1 insertion(+), 1 deletion(-) ---- -Now we switch back to our `master` branch and add some documentation for the function. +现在我们切换回我们的 `master` 分支并为函数增加一些注释。 [source,console] ---- @@ -86,7 +86,7 @@ $ git commit -am 'document the function' 1 file changed, 1 insertion(+) ---- -Now we try to merge in our `whitespace` branch and we'll get conflicts because of the whitespace changes. +现在我们尝试合并入我们的 `whitespace` 分支,因为修改了空白字符,所以合并会出现冲突。 [source,console] ---- @@ -97,9 +97,9 @@ Automatic merge failed; fix conflicts and then commit the result. ---- [[_abort_merge]] -===== Aborting a Merge +===== 中断一次合并 -We now have a few options. First, let's cover how to get out of this situation. If you perhaps weren't expecting conflicts and don't want to quite deal with the situation yet, you can simply back out of the merge with `git merge --abort`. +我们现在有几个选项。首先,让我们介绍如何摆脱这个情况。你可能不想处理冲突这种情况,完全可以通过 `git merge --abort` 来简单地退出合并。 [source,console] ---- @@ -113,15 +113,15 @@ $ git status -sb ## master ---- -The `git merge --abort` option tries to revert back to your state before you ran the merge. The only cases where it may not be able to do this perfectly would be if you had unstashed, uncommitted changes in your working directory when you ran it, otherwise it should work fine. +`git merge --abort` 选项会尝试恢复到你运行合并前的状态。但当运行命令前,在工作目录中有未储藏、未提交的修改时它不能完美处理,除此之外它都工作地很好。 -If for some reason you find yourself in a horrible state and just want to start over, you can also run `git reset --hard HEAD` or wherever you want to get back to. Remember again that this will blow away your working directory, so make sure you don't want any changes there. +如果因为某些原因你发现自己处在一个混乱的状态中然后只是想要重来一次,也可以运行 `git reset --hard HEAD` 回到之前的状态或其他你想要恢复的状态。请牢记这会将清除工作目录中的所有内容,所以确保你不需要保存这里的任意改动。 -===== Ignoring Whitespace +===== 忽略空白 -In this specific case, the conflicts are whitespace related. We know this because the case is simple, but it's also pretty easy to tell in real cases when looking at the conflict because every line is removed on one side and added again on the other. By default, Git sees all of these lines as being changed, so it can't merge the files. +在这个特定的例子中,冲突与空白有关。我们知道这点是因为这个例子很简单,但是在实际的例子中发现这样的冲突也很容易,因为每一行都被移除而在另一边每一行又被加回来了。默认情况下,Git 认为所有这些行都改动了,所以它不会合并文件。 -The default merge strategy can take arguments though, and a few of them are about properly ignoring whitespace changes. If you see that you have a lot of whitespace issues in a merge, you can simply abort it and do it again, this time with `-Xignore-all-space` or `-Xignore-space-change`. The first option ignores changes in any **amount** of existing whitespace, the second ignores all whitespace changes altogether. +默认合并策略可以带有参数,其中的几个正好是关于忽略空白改动的。如果你看到在一次合并中有大量的空白问题,你可以简单地中止它并重做一次,这次使用 `-Xignore-all-space` 或 `-Xignore-space-change` 选项。第一个选项忽略任意 **数量** 的已有空白的修改,第二个选项忽略所有空白修改。 [source,console] ---- @@ -132,22 +132,22 @@ Merge made by the 'recursive' strategy. 1 file changed, 1 insertion(+), 1 deletion(-) ---- -Since in this case, the actual file changes were not conflicting, once we ignore the whitespace changes, everything merges just fine. +因为在本例中,实际上文件修改并没有冲突,一旦我们忽略空白修改,每一行都能被很好地合并。 -This is a lifesaver if you have someone on your team who likes to occasionally reformat everything from spaces to tabs or vice-versa. +如果你的团队中的某个人可能不小心重新格式化空格为制表符或者相反的操作,这会是一个救命稻草。 [[_manual_remerge]] -===== Manual File Re-merging +===== 手动文件再合并 -Though Git handles whitespace pre-processing pretty well, there are other types of changes that perhaps Git can't handle automatically, but are scriptable fixes. As an example, let's pretend that Git could not handle the whitespace change and we needed to do it by hand. +虽然 Git 对空白的预处理做得很好,还有很多其他类型的修改,Git 也许无法自动处理,但是脚本可以处理它们。例如,假设 Git 无法处理空白修改因此我们需要手动处理。 -What we really need to do is run the file we're trying to merge in through a `dos2unix` program before trying the actual file merge. So how would we do that? +我们真正想要做的是对将要合并入的文件在真正合并前运行 `dos2unix` 程序。所以如果那样的话,我们该如何做? -First, we get into the merge conflict state. Then we want to get copies of my version of the file, their version (from the branch we're merging in) and the common version (from where both sides branched off). Then we want to fix up either their side or our side and re-try the merge again for just this single file. +首先,我们进入到了合并冲突状态。然后我们想要我的版本的文件,他们的版本的文件(从我们将要合并入的分支)和共同的版本的文件(从分支叉开时的位置)的拷贝。然后我们想要修复任何一边的文件,并且为这个单独的文件重试一次合并。 -Getting the three file versions is actually pretty easy. Git stores all of these versions in the index under ``stages'' which each have numbers associated with them. Stage 1 is the common anecestor, stage 2 is your version and stage 3 is from the `MERGE_HEAD`, the version you're merging in (``theirs''). +获得这三个文件版本实际上相当容易。Git 在索引中存储了所有这些版本,在 ``stages'' 下每一个都有一个数字与它们关联。Stage 1 是它们共同的祖先版本,stage 2 是你的版本,stage 3 来自于 `MERGE_HEAD`,即你将要合并入的版本(``theirs'')。 -You can extract a copy of each of these versions of the conflicted file with the `git show` command and a special syntax. +通过 `git show` 命令与一个特别的语法,你可以将冲突文件的这些版本释放出一份拷贝。 [source,console] ---- @@ -156,7 +156,7 @@ $ git show :2:hello.rb > hello.ours.rb $ git show :3:hello.rb > hello.theirs.rb ---- -If you want to get a little more hard core, you can also use the `ls-files -u` plumbing command to get the actual SHAs of the Git blobs for each of these files. +如果你想要更专业一点,也可以使用 `ls-files -u` 底层命令来得到这些文件的 Git blob 对象的实际 SHA 值。 [source,console] ---- @@ -166,9 +166,9 @@ $ git ls-files -u 100755 e85207e04dfdd5eb0a1e9febbc67fd837c44a1cd 3 hello.rb ---- -The `:1:hello.rb` is just a shorthand for looking up that blob SHA. +`:1:hello.rb` 只是查找那个 blob 对象 SHA 值的简写。 -Now that we have the content of all three stages in our working directory, we can manually fix up theirs to fix the whitespace issue and re-merge the file with the little-known `git merge-file` command which does just that. +既然在我们的工作目录中已经有这所有三个阶段的内容,我们可以手工修复它们来修复空白问题,然后使用鲜为人知的 `git merge-file` 命令来重新合并那个文件。 [source,console] ---- @@ -195,11 +195,11 @@ index 36c06c8,e85207e..0000000 hello() ---- -At this point we have nicely merged the file. In fact, this actually works better than the `ignore-all-space` option because this actually fixes the whitespace changes before merge instead of simply ignoring them. In the `ignore-all-space` merge, we actually ended up with a few lines with DOS line endings, making things mixed. +在这时我们已经漂亮地合并了那个文件。实际上,这比使用 `ignore-all-space` 选项要更好,因为在合并前真正地修复了空白修改而不是简单地忽略它们。在使用 `ignore-all-space` 进行合并操作后,我们最终得到了有几行是 DOS 行尾的文件,从而使提交内容混乱了。 -If you want to get an idea before finalizing this commit about what was actually changed between one side or the other, you can ask `git diff` to compare what is in your working directory that you're about to commit as the result of the merge to any of these stages. Let's go through them all. +如果你想要在最终提交前看一下我们这边与另一边之间实际的修改,你可以使用 `git diff` 来比较将要提交作为合并结果的工作目录与其中任意一个阶段的文件差异。让我们看看它们。 -To compare your result to what you had in your branch before the merge, in other words, to see what the merge introduced, you can run `git diff --ours` +要在合并前比较结果与在你的分支上的内容,换一句话说,看看合并引入了什么,可以运行 `git diff --ours` [source,console] ---- @@ -220,9 +220,9 @@ index 36c06c8..44d0a25 100755 hello() ---- -So here we can easily see that what happened in our branch, what we're actually introducing to this file with this merge, is changing that single line. +这里我们可以很容易地看到在我们的分支上发生了什么,在这次合并中我们实际引入到这个文件的改动,是修改了其中一行。 -If we want to see how the result of the merge differed from what was on their side, you can run `git diff --theirs`. In this and the following example, we have to use `-w` to strip out the whitespace because we're comparing it to what is in Git, not our cleaned up `hello.theirs.rb` file. +如果我们想要查看合并的结果与他们那边有什么不同,可以运行 `git diff --theirs`。在本例及后续的例子中,我们会使用 `-w` 来去除空白,因为我们将它与 Git 中的,而不是我们清理过的 `hello.theirs.rb` 文件比较。 [source,console] ---- @@ -241,7 +241,7 @@ index e85207e..44d0a25 100755 end ---- -Finally, you can see how the file has changed from both sides with `git diff --base`. +最终,你可以通过 `git diff --base` 来查看文件在两边是如何改动的。 [source,console] ---- @@ -263,7 +263,7 @@ index ac51efd..44d0a25 100755 hello() ---- -At this point we can use the `git clean` command to clear out the extra files we created to do the manual merge but no longer need. +在这时我们可以使用 `git clean` 命令来清理我们为手动合并而创建但不再有用的额外文件。 [source,console] ---- @@ -274,11 +274,11 @@ Removing hello.theirs.rb ---- [[_checking_out_conflicts]] -===== Checking Out Conflicts +===== 检出冲突 -Perhaps we're not happy with the resolution at this point for some reason, or maybe manually editing one or both sides still didn't work well and we need more context. +也许有时我们并不满意这样的解决方案,或许有时还要手动编辑一边或者两边的冲突,但还是依旧无法正常工作,这时我们需要更多的上下文关联来解决这些冲突。 -Let's change up the example a little. For this example, we have two longer lived branches that each have a few commits in them but create a legitimate content conflict when merged. +让我们来稍微改动下例子。对于本例,我们有两个长期分支,每一个分支都有几个提交,但是在合并时却创建了一个合理的冲突。 [source,console] ---- @@ -293,7 +293,7 @@ $ git log --graph --oneline --decorate --all * b7dcc89 initial hello world code ---- -We now have three unique commits that live only on the `master` branch and three others that live on the `mundo` branch. If we try to merge the `mundo` branch in, we get a conflict. +现在有只在 `master` 分支上的三次单独提交,还有其他三次提交在 `mundo` 分支上。如果我们尝试将 `mundo` 分支合并入 `master` 分支,我们得到一个冲突。 [source,console] ---- @@ -303,7 +303,7 @@ CONFLICT (content): Merge conflict in hello.rb Automatic merge failed; fix conflicts and then commit the result. ---- -We would like to see what the merge conflict is. If we open up the file, we'll see something like this: +我们想要看一下合并冲突是什么。如果我们打开这个文件,我们将会看到类似下面的内容: [source,ruby] ---- @@ -320,20 +320,20 @@ end hello() ---- -Both sides of the merge added content to this file, but some of the commits modified the file in the same place that caused this conflict. +合并的两边都向这个文件增加了内容,但是导致冲突的原因是其中一些提交修改了文件的同一个地方。 -Let's explore a couple of tools that you now have at your disposal to determine how this conflict came to be. Perhaps it's not obvious how exactly you should fix this conflict. You need more context. +让我们探索一下现在你手边可用来查明这个冲突是如何产生的工具。应该如何修复这个冲突看起来或许并不明显。这时你需要更多上下文。 -One helpful tool is `git checkout` with the `--conflict' option. This will re-checkout the file again and replace the merge conflict markers. This can be useful if you want to reset the markers and try to resolve them again. +一个很有用的工具是带 `--conflict` 选项的 `git checkout`。这会重新检出文件并替换合并冲突标记。如果想要重置标记并尝试再次解决它们的话这会很有用。 -You can pass `--conflict` either `diff3` or `merge` (which is the default). If you pass it `diff3`, Git will use a slightly different version of conflict markers, not only giving you the ``ours'' and ``theirs'' versions, but also the ``base'' version inline to give you more context. +可以传递给 `--conflict` 参数 `diff3` 或 `merge`(默认选项)。如果传给它 `diff3`,Git 会使用一个略微不同版本的冲突标记:不仅仅只给你 ``ours'' 和 ``theirs'' 版本,同时也会有 ``base'' 版本在中间来给你更多的上下文。 [source,console] ---- $ git checkout --conflict=diff3 hello.rb ---- -Once we run that, the file will look like this instead: +一旦我们运行它,文件看起来会像下面这样: [source,ruby] ---- @@ -352,23 +352,23 @@ end hello() ---- -If you like this format, you can set it as the default for future merge conflicts by setting the `merge.conflictstyle` setting to `diff3`. +如果你喜欢这种格式,可以通过设置 `merge.conflictstyle` 选项为 `diff3` 来做为以后合并冲突的默认选项。 [source,console] ---- $ git config --global merge.conflictstyle diff3 ---- -The `git checkout` command can also take `--ours` and `--theirs` options, which can be a really fast way of just choosing either one side or the other without merging things at all. +`git checkout` 命令也可以使用 `--ours` 和 `--theirs` 选项,这是一种无需合并的快速方式,你可以选择留下一边的修改而丢弃掉另一边修改。 -This can be particularly useful for conflicts of binary files where you can simply choose one side, or where you only want to merge certain files in from another branch - you can do the merge and then checkout certain files from one side or the other before committing. +当有二进制文件冲突时这可能会特别有用,因为可以简单地选择一边,或者可以只合并另一个分支的特定文件 - 可以做一次合并然后在提交前检出一边或另一边的特定文件。 [[_merge_log]] -===== Merge Log +===== 合并日志 -Another useful tool when resolving merge conflicts is `git log`. This can help you get context on what may have contributed to the conflicts. Reviewing a little bit of history to remember why two lines of development were touching the same area of code can be really helpful sometimes. +另一个解决合并冲突有用的工具是 `git log`。这可以帮助你得到那些对冲突有影响的上下文。回顾一点历史来记起为什么两条线上的开发会触碰同一片代码有时会很有用。 -To get a full list of all of the unique commits that were included in either branch involved in this merge, we can use the ``triple dot'' syntax that we learned in <<_triple_dot>>. +为了得到此次合并中包含的每一个分支的所有独立提交的列表,我们可以使用之前在 <<_triple_dot>> 学习的 ``三点'' 语法。 [source,console] ---- @@ -381,9 +381,9 @@ $ git log --oneline --left-right HEAD...MERGE_HEAD > c3ffff1 changed text to hello mundo ---- -That's a nice list of the six total commits involved, as well as which line of development each commit was on. +这个漂亮的列表包含 6 个提交和每一个提交所在的不同开发路径。 -We can further simplify this though to give us much more specific context. If we add the `--merge` option to `git log`, it will only show the commits in either side of the merge that touch a file that's currently conflicted. +我们可以通过更加特定的上下文来进一步简化这个列表。如果我们添加 `--merge` 选项到 `git log` 中,它会只显示任何一边接触了合并冲突文件的提交。 [source,console] ---- @@ -392,13 +392,13 @@ $ git log --oneline --left-right --merge > c3ffff1 changed text to hello mundo ---- -If you run that with the `-p` option instead, you get just the diffs to the file that ended up in conflict. This can be **really** helpful in quickly giving you the context you need to help understand why something conflicts and how to more intelligently resolve it. +如果你运行命令时用 `-p` 选项代替,你会得到所有冲突文件的区别。快速获得你需要帮助理解为什么发生冲突的上下文,以及如何聪明地解决它,这会 **非常** 有用。 -===== Combined Diff Format +===== 组合式差异格式 -Since Git stages any merge results that are successful, when you run `git diff` while in a conflicted merge state, you only get what is currently still in conflict. This can be helpful to see what you still have to resolve. +因为 Git 暂存合并成功的结果,当你在合并冲突状态下运行 `git diff` 时,只会得到现在还在冲突状态的区别。当需要查看你还需要解决哪些冲突时这很有用。 -When you run `git diff` directly after a merge conflict, it will give you information in a rather unique diff output format. +在合并冲突后直接运行的 `git diff` 会给你一个相当独特的输出格式。 [source,console] ---- @@ -421,11 +421,11 @@ index 0399cd5,59727f0..0000000 hello() ---- -The format is called ``Combined Diff'' and gives you two columns of data next to each line. The first column shows you if that line is different (added or removed) between the ``ours'' branch and the file in your working directory and the second column does the same between the ``theirs'' branch and your working directory copy. +这种叫作 ``组合式差异'' 的格式会在每一行给你两列数据。第一列为你显示 ``ours'' 分支与工作目录的文件区别(添加或删除),第二列显示 ``theirs'' 分支与工作目录的拷贝区别。 -So in that example you can see that the `<<<<<<<` and `>>>>>>>` lines are in the working copy but were not in either side of the merge. This makes sense because the merge tool stuck them in there for our context, but we're expected to remove them. +所以在上面的例子中可以看到 `<<<<<<<` 与 `>>>>>>>` 行在工作拷贝中但是并不在合并的任意一边中。这很有意义,合并工具因为我们的上下文被困住了,它期望我们去移除它们。 -If we resolve the conflict and run `git diff` again, we'll see the same thing, but it's a little more useful. +如果我们解决冲突再次运行 `git diff`,我们将会看到同样的事情,但是它有一点帮助。 [source,console] ---- @@ -447,9 +447,9 @@ index 0399cd5,59727f0..0000000 hello() ---- -This shows us that ``hola world'' was in our side but not in the working copy, that ``hello mundo'' was in their side but not in the working copy and finally that ``hola mundo'' was not in either side but is now in the working copy. This can be useful to review before committing the resolution. +这里显示出 ``hola world'' 在我们这边但不在工作拷贝中,那个 ``hello mundo'' 在他们那边但不在工作拷贝中,最终 ``hola mundo'' 不在任何一边但是现在在工作拷贝中。在提交解决方案前这对审核很有用。 -You can also get this from the `git log` for any merge after the fact to see how something was resolved after the fact. Git will output this format if you run `git show` on a merge commit, or if you add a `--cc` option to a `git log -p` (which by default only shows patches for non-merge commits). +也可以在合并后通过 `git log` 来获取相同信息,并查看冲突是如何解决的。如果你对一个合并提交运行 `git show` 命令 Git 将会输出这种格式。 [source,console] ---- @@ -481,44 +481,44 @@ index 0399cd5,59727f0..e1d0799 ---- [[_undoing_merges]] -==== Undoing Merges +==== 撤消合并 -Now that you know how to create a merge commit, you'll probably make some by mistake. -One of the great things about working with Git is that it's okay to make mistakes, because it's possible (and in many cases easy) to fix them. +虽然你已经知道如何创建一个合并提交,但有时出错是在所难免的。 +使用 Git 最棒的一件事情是犯错是可以的,因为有可能(大多数情况下都很容易)修复它们。 -Merge commits are no different. -Let's say you started work on a topic branch, accidentally merged it into `master`, and now your commit history looks like this: +合并提交并无不同。 +假设现在在一个特性分支上工作,不小心将其合并到 `master` 中,现在提交历史看起来是这样: -.Accidental merge commit -image::images/undomerge-start.png[Accidental merge commit.] +.意外的合并提交 +image::images/undomerge-start.png[意外的合并提交] -There are two ways to approach this problem, depending on what your desired outcome is. +有两种方法来解决这个问题,这取决于你想要的结果是什么。 -===== Fix the references +===== 修复引用 -If the unwanted merge commit only exists on your local repository, the easiest and best solution is to move the branches so that they point where you want them to. -In most cases, if you follow the errant `git merge` with `git reset --hard HEAD~`, this will reset the branch pointers so they look like this: +如果这个不想要的合并提交只存在于你的本地仓库中,最简单且最好的解决方案是移动分支到你想要它指向的地方。 +大多数情况下,如果你在错误的 `git merge` 后运行 `git reset --hard HEAD~`,这会重置分支指向所以它们看起来像这样: -.History after `git reset --hard HEAD~` -image::images/undomerge-reset.png[History after `git reset --hard HEAD~`.] +.在 `git reset --hard HEAD~` 之后的历史 +image::images/undomerge-reset.png[在 `git reset --hard HEAD~` 之后的历史] -We covered `reset` back in <<_git_reset>>, so it shouldn't be too hard to figure out what's going on here. -Here's a quick refresher: `reset --hard` usually goes through three steps: +我们之前在 <<_git_reset>> 已经介绍了 `reset`,所以现在指出这里发生了什么并不是很困难。 +让我们快速复习下:`reset --hard` 通常会经历三步: -. Move the branch HEAD points to. - In this case, we want to move `master` to where it was before the merge commit (`C6`). -. Make the index look like HEAD. -. Make the working directory look like the index. +. 移动 HEAD 指向的分支。 + 在本例中,我们想要移动 `master` 到合并提交(`C6`)之前所在的位置。 +. 使索引看起来像 HEAD。 +. 使工作目录看起来像索引。 -The downside of this approach is that it's rewriting history, which can be problematic with a shared repository. -Check out <<_rebase_peril>> for more on what can happen; the short version is that if other people have the commits you're rewriting, you should probably avoid `reset`. -This approach also won't work if any other commits have been created since the merge; moving the refs would effectively lose those changes. +这个方法的缺点是它会重写历史,在一个共享的仓库中这会造成问题的。 +查阅 <<_rebase_peril>> 来了解更多可能发生的事情;用简单的话说就是如果其他人已经有你将要重写的提交,你应当避免使用 `reset`。 +如果有任何其他提交在合并之后创建了,那么这个方法也会无效;移动引用实际上会丢失那些改动。 [[_reverse_commit]] -===== Reverse the commit +===== 还原提交 -If moving the branch pointers around isn't going to work for you, Git gives you the option of making a new commit which undoes all the changes from an existing one. -Git calls this operation a ``revert'', and in this particular scenario, you'd invoke it like this: +如果移动分支指针并不适合你,Git 给你一个生成一个新提交的选项,提交将会撤消一个已存在提交的所有修改。 +Git 称这个操作为 ``还原'',在这个特定的场景下,你可以像这样调用它: [source,console] ---- @@ -526,17 +526,17 @@ $ git revert -m 1 HEAD [master b1d8379] Revert "Merge branch 'topic'" ---- -The `-m 1` flag indicates which parent is the ``mainline'' and should be kept. -When you invoke a merge into `HEAD` (`git merge topic`), the new commit has two parents: the first one is `HEAD` (`C6`), and the second is the tip of the branch being merged in (`C4`). -In this case, we want to undo all the changes introduced by merging in parent #2 (`C4`), while keeping all the content from parent #1 (`C6`). +`-m 1` 标记指出 ``mainline'' 需要被保留下来的父结点。 +当你引入一个合并到 `HEAD`(`git merge topic`),新提交有两个父结点:第一个是 `HEAD`(`C6`),第二个是将要合并入分支的最新提交(`C4`)。 +在本例中,我们想要撤消所有由父结点 #2(`C4`)合并引入的修改,同时保留从父结点 #1(`C4`)开始的所有内容。 -The history with the revert commit looks like this: +有还原提交的历史看起来像这样: -.History after `git revert -m 1` -image::images/undomerge-revert.png[History after `git revert -m 1`.] +.在 `git revert -m 1` 后的历史 +image::images/undomerge-revert.png[在 `git revert -m 1` 后的历史] -The new commit `^M` has exactly the same contents as `C6`, so starting from here it's as if the merge never happened, except that the now-unmerged commits are still in `HEAD`'s history. -Git will get confused if you try to merge `topic` into `master` again: +新的提交 `^M` 与 `C6` 有完全一样的内容,所以从这儿开始就像合并从未发生过,除了“现在还没合并”的提交依然在 `HEAD` 的历史中。 +如果你尝试再次合并 `topic` 到 `master` Git 会感到困惑: [source,console] ---- @@ -544,13 +544,13 @@ $ git merge topic Already up-to-date. ---- -There's nothing in `topic` that isn't already reachable from `master`. -What's worse, if you add work to `topic` and merge again, Git will only bring in the changes _since_ the reverted merge: +`topic` 中并没有东西不能从 `master` 中追踪到达。 +更糟的是,如果你在 `topic` 中增加工作然后再次合并,Git 只会引入被还原的合并 _之后_ 的修改。 -.History with a bad merge -image::images/undomerge-revert2.png[History with a bad merge.] +.含有坏掉合并的历史 +image::images/undomerge-revert2.png[含有坏掉合并的历史] -The best way around this is to un-revert the original merge, since now you want to bring in the changes that were reverted out, *then* create a new merge commit: +解决这个最好的方式是撤消还原原始的合并,因为现在你想要引入被还原出去的修改,*然后* 创建一个新的合并提交: [source,console] ---- @@ -559,25 +559,25 @@ $ git revert ^M $ git merge topic ---- -.History after re-merging a reverted merge -image::images/undomerge-revert3.png[History after re-merging a reverted merge.] +.在重新合并一个还原合并后的历史 +image::images/undomerge-revert3.png[在重新合并一个还原合并后的历史] -In this example, `M` and `^M` cancel out. -`^^M` effectively merges in the changes from `C3` and `C4`, and `C8` merges in the changes from `C7`, so now `topic` is fully merged. +在本例中,`M` 与 `^M` 抵消了。 +`^^M` 事实上合并入了 `C3` 与 `C4` 的修改,`C8` 合并了 `C7` 的修改,所以现在 `topic` 已经完全被合并了。 -==== Other Types of Merges +==== 其他类型的合并 -So far we've covered the normal merge of two branches, normally handled with what is called the ``recursive'' strategy of merging. There are other ways to merge branches together however. Let's cover a few of them quickly. +到目前为止我们介绍的都是通过一个叫作 ``recursive'' 的合并策略来正常处理的两个分支的正常合并。然而还有其他方式来合并两个分支到一起。让我们来快速介绍其中的几个。 -===== Our or Theirs Preference +===== 我们的或他们的偏好 -First of all, there is another useful thing we can do with the normal ``recursive'' mode of merging. We've already seen the `ignore-all-space` and `ignore-space-change` options which are passed with a `-X` but we can also tell Git to favor one side or the other when it sees a conflict. +首先,有另一种我们可以通过 ``recursive'' 合并模式做的有用工作。我们之前已经看到传递给 `-X` 的 `ignore-all-space` 与 `ignore-space-change` 选项,但是我们也可以告诉 Git 当它看见一个冲突时直接选择一边。 -By default, when Git sees a conflict between two branches being merged, it will add merge conflict markers into your code and mark the file as conflicted and let you resolve it. If you would prefer for Git to simply choose a specific side and ignore the other side instead of letting you manually merge the conflict, you can pass the `merge` command either a `-Xours` or `-Xtheirs`. +默认情况下,当 Git 看到两个分支合并中的冲突时,它会将合并冲突标记添加到你的代码中并标记文件为冲突状态来让你解决。如果你希望 Git 简单地选择特定的一边并忽略另外一边而不是让你手动合并冲突,你可以传递给 `merge` 命令一个 `-Xours` 或 `-Xtheirs` 参数。 -If Git sees this, it will not add conflict markers. Any differences that are mergable, it will merge. Any differences that conflict, it will simply choose the side you specify in whole, including binary files. +如果 Git 看到这个,它并不会增加冲突标记。任何可以合并的区别,它会直接合并。任何有冲突的区别,它会简单地选择你全局指定的一边,包括二进制文件。 -If we go back to the ``hello world'' example we were using before, we can see that merging in our branch causes conflicts. +如果我们回到之前我们使用的 ``hello world'' 例子中,我们可以看到合并入我们的分支时引发了冲突。 [source,console] ---- @@ -588,7 +588,7 @@ Resolved 'hello.rb' using previous resolution. Automatic merge failed; fix conflicts and then commit the result. ---- -However if we run it with `-Xours` or `-Xtheirs` it does not. +然而如果我们运行时增加 `-Xours` 或 `-Xtheirs` 参数就不会有冲突。 [source,console] ---- @@ -601,13 +601,13 @@ Merge made by the 'recursive' strategy. create mode 100644 test.sh ---- -In that case, instead of getting conflict markers in the file with ``hello mundo'' on one side and ``hola world'' on the other, it will simply pick ``hola world''. However, all the other non-conflicting changes on that branch are merged successfully in. +在上例中,它并不会为 ``hello mundo'' 与 ``hola world'' 标记合并冲突,它只会简单地选取 ``hola world''。然而,在那个分支上所有其他非冲突的改动都可以被成功地合并入。 -This option can also be passed to the `git merge-file` command we saw earlier by running something like `git merge-file --ours` for individual file merges. +这个选项也可以传递给我们之前看到的 `git merge-file` 命令,通过运行类似 `git merge-file --ours` 的命令来合并单个文件。 -If you want to do something like this but not have Git even try to merge changes from the other side in, there is a more draconian option, which is the ``ours'' merge _strategy_. This is different from the ``ours'' recursive merge _option_. +如果想要做类似的事情但是甚至并不想让 Git 尝试合并另外一边的修改,有一个更严格的选项,它是 ``ours'' 合并 _策略_。这与 ``ours'' recursive 合并 _选项_ 不同。 -This will basically do a fake merge. It will record a new merge commit with both branches as parents, but it will not even look at the branch you're merging in. It will simply record as the result of the merge the exact code in your current branch. +这本质上会做一次假的合并。它会记录一个以两边分支作为父结点的新合并提交,但是它甚至根本不关注你正合并入的分支。它只会简单地把当前分支的代码当作合并结果记录下来。 [source,console] ---- @@ -617,8 +617,8 @@ $ git diff HEAD HEAD~ $ ---- -You can see that there is no difference between the branch we were on and the result of the merge. +你可以看到合并后与合并前我们的分支并没有任何区别。 -This can often be useful to basically trick Git into thinking that a branch is already merged when doing a merge later on. For example, say you branched off a ``release'' branch and have done some work on it that you will want to merge back into your ``master'' branch at some point. In the meantime some bugfix on ``master'' needs to be backported into your `release` branch. You can merge the bugfix branch into the `release` branch and also `merge -s ours` the same branch into your `master` branch (even though the fix is already there) so when you later merge the `release` branch again, there are no conflicts from the bugfix. +当再次合并时从本质上欺骗 Git 认为那个分支已经合并过经常是很有用的。例如,假设你有一个分叉的 `release` 分支并且在上面做了一些你想要在未来某个时候合并回 `master` 的工作。与此同时 `master` 分支上的某些 bugfix 需要向后移植回 `release` 分支。你可以合并 bugfix 分支进入 `release` 分支同时也 `merge -s ours` 合并进入你的 `master` 分支(即使那个修复已经在那儿了)这样当你之后再次合并 `release` 分支时,就不会有来自 bugfix 的冲突。 include::subtree-merges.asc[]