使用Git将上游更改合并为工作副本的最有效方法是什么



当人们一起编写代码时,一个非常常见的场景是必须让自己(比如说,您正在开发的功能分支)处于最新状态。这可能会导致同一文件在上游和工作树中发生"冲突"。我的问题是,用Git解决这种冲突的最直接方法是什么。

具体来说,我正在寻找一些简洁、可见性好的东西(即知道发生了什么,因为有时冲突超出了自动解决的范围,人们希望在合并之前知道这一点)。比这更好的东西:

# I'm on a branch, have changes in working tree,
# have overlapping change in remote; for simplicity,
# assume no local (unpushed) commits have been made (clean HEAD)
git fetch
git difftool origin/<branchName> HEAD   # visually examine incoming changes, to understand them; conclude that the changes are automatically merge-able
#git merge              # fails: "Your local changes to the following files would be overwritten by merge:"
#git merge -s resolve   # fails: same
#git merge -s recursive # fails: same
#git mergetool          # no-op: "No files need merging" ?!
git stash
git pull
git stash pop
git difftool HEAD    # visually examine outcome - it worked, but does stashing really need to be involved?

其他答案包括在提取后提交,这对我来说是否定的,我在这个阶段没有什么可提交的。我想要的是让自己(即我的工作树)了解上游的最新情况。

我认为拒绝合并是个坏主意:

首先,提交你的东西可以让你轻松地恢复你的工作,而如果你在某个阶段甚至没有对你的东西进行git添加,这将是非常不可能的。如果你需要在后面重新修改补丁,你可以修改它们,这是一种标准的方法。

如果我很理解你,你的工作正在进行中,因此你希望你的补丁在合并后重新制作,因此我建议你重新调整补丁的基础。如果当时补丁很小:重新调整基础将指向最终的冲突,如果你的补丁很小,这些冲突可能很容易解决。

如果你有一个庞大的分支,即将有很多冲突,我建议在合并之前完成你正在处理的补丁,这样当你需要在公共git存储库上推送你的东西时,你的历史记录中就不会有"草稿"补丁了。

我个人使用选项1,即使我有很多补丁悬而未决。这使我能够在执行重新基础时重新验证每个补丁,并防止我不喜欢的"邪恶合并"(让git责怪痛苦,IMHO)。

总之,我认为你应该重新考虑这样一个事实:你不想做临时的事情;这为您提供了比隐藏更安全的方式,并允许您以您似乎已经知道的方式使用合并。

如果您有更改,即脏工作树,您就有东西要提交。最多,您有一些内容需要临时提交

在其他版本控制系统中,您可以通过在分支上提交它来实现这一点。在Git中,你也可以这样做:你不需要使用分支,但这可能也是Git中处理分支最方便的方法。

你提到了一个开始的场景,我有一个我觉得很有用的工作模式:

当人们一起编写代码时,一个非常常见的场景是必须让自己(比如说,您正在开发的功能分支)了解最新情况。

假设您正在开发feature-X,它最终将被放入dev(开发分支)中。其他开发人员一直在研究功能Y和Z,其中一个已经完成,dev现在已经更新,所以您可以运行:

$ git fetch

并看到您的dev现在落后于origin/dev。也就是说,您现在拥有:

...--C--D--H   <-- master

         I--J   <-- origin/dev
       /
E--F--G   <-- dev, feature-X (HEAD)

存储库中。您的工作树中还有一些与提交G中的文件不同的内容。(您可能还没有一个名为feature-X的分支,而是将HEAD附加到dev。如果是这种情况,您现在只需使用git checkout -b feature-X创建它,现在就可以匹配图片了。)

在这一点上,要做的是无论如何都要提交你正在做的事情。这使得一个新的提交K:

...--C--D--H   <-- master

         I--J   <-- origin/dev
       /
E--F--G   <-- dev

K feature-X (HEAD)

现在,您可以将自己的dev快进到origin/dev。基本的命令方法是:

$ git checkout dev                 # safe, since your work is committed
$ git merge --ff-only origin/dev   # or `git pull` if you really insist

图纸现在看起来是这样的:

...--C--D--H   <-- master



E--F--G--I--J   <-- dev (HEAD), origin/dev

K   <-- feature-X (HEAD)

这里是大多数人只运行git checkout feature-X; git rebase dev的地方,这很好,您应该可以随意使用该方法。(我经常这样做。考虑这样做,然后使用下面描述的git reset HEAD^技巧。)但有时我会简单地feature-X重命名为feature-X.0,然后用git checkout -b feature-X:创建新的feature-X

...--C--D--H   <-- master



E--F--G--I--J   <-- dev, origin/dev, feature-X (HEAD)

K   <-- feature-X.0

现在我已经准备好再次开始处理feature-X了,在这一点上,我只是挑选了所有的feature-X.0提交:

$ git cherry-pick dev..feature-X.0

其产生作为CCD_ 23:的副本的提交CCD_

...--C--D--H   <-- master

               K'  <-- feature-X (HEAD)
             /
E--F--G--I--J   <-- dev, origin/dev

K   <-- feature-X.0

即使feature-X.0:上有多个提交,这也能工作

...--C--D--H   <-- master

               K'-L'  <-- feature-X (HEAD)
             /
E--F--G--I--J   <-- dev, origin/dev

K--L   <-- feature-X.0

如果这个新feature-X的最后一次提交(这个版本中的L',只有一次提交的K')是真的,严重地还没有准备好提交,那么在这一点上,我只使用git reset HEAD^(如果更容易的话,你可以拼写为HEAD~,就像在Windows上一样)将分支名称向后移动一步。这将从当前分支中删除最末尾的提交,给出:

...--C--D--H   <-- master

               K'  <-- feature-X (HEAD)
             /
E--F--G--I--J   <-- dev, origin/dev

K--L   <-- feature-X.0

让工作树变得"脏",就像我开始整个过程之前一样。(一般来说,部分提交是可以的,因为我稍后会在feature-X基本就绪时使用git rebase -i来清理所有内容。)

无论出于何种原因,如果必须重复该过程,我都会将当前正在进行的feature-X重命名为feature-X.1feature-X.2或其他任何名称。我种了一小部分feature-X-es,偶尔会去枝花园修剪最杂草的最新的和最伟大的仍然命名为feature-X,但我可以根据需要提供我以前的所有工作,直到我将它们删除为止这比rebase存储更好,因为如果代码很复杂,并且我遗漏了一些东西,我仍然有旧版本,使用的名称是可识别的,而不仅仅是reflog中的一些无法理解的哈希ID,也不是一个与其他十几个存储无法区分的存储。

最新更新