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

时间:2018-09-29 17:09:49

标签: git merge git-merge merge-conflict-resolution git-merge-conflict

人们在一起编码时出现的一种非常普遍的情况是必须使自己更新(例如,您正在处理的功能分支)。这可能导致同一文件的“冲突”在上游和您的工作树中均已更改。我的问题是,解决与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?

其他答案涉及在获取后提交,这对我来说是一个不对,在此阶段我没有什么可提交的。我想要的是使自己(即我的工作树)与上游保持最新。

2 个答案:

答案 0 :(得分:1)

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

首先,提交内容可以使您轻松恢复工作,如果在某个阶段甚至不对内容进行git add都是不可能的。 如果您需要重新制作补丁,可以对其进行修改,这是一种标准方法。

如果我很了解您,那么您的工作仍在进行中,因此您希望补丁在合并后会得到重新处理,因此,我建议您对补丁进行重新定位,如果它们当时很小:重新定位将指出最终的冲突如果您的补丁很小,则可能很容易修复。

如果您的分支很大,实际上会有很多冲突,那么我建议您在合并之前完成正在处理的补丁程序,以免您的历史记录中出现“草稿”补丁程序我需要将您的内容推送到通用git存储库中。

我个人使用选项1,即使我有很多补丁程序待处理。这使我可以在执行重新设置基准时重新验证每个补丁,还可以防止我不喜欢的“恶意合并”(使git怪罪痛苦,恕我直言)。

总而言之,我认为您应该重新考虑以下事实:您不想提交临时的东西。与隐藏相比,这提供了更多的安全性,并允许您以似乎已经知道的方式使用合并。

答案 1 :(得分:0)

如果您进行更改(即,工作树很脏),您就有东西要提交。最多,您想要临时

提交一些东西。

在其他版本控制系统中,您可以通过在分支上提交来实现。在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改成{{1 }},然后使用feature-X.0创建一个 new feature-X

git checkout -b 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 ,它是K'的副本:

K

即使...--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^(在Windows上显然是这样))将分支名称移回一步。这将从当前分支中删除最末端的提交,给出:

HEAD~

并完全按照我开始整个过程​​之前的方式离开工作树。 (一般来说,部分提交是可以的,因为稍后...--C--D--H <-- master \ \ K' <-- feature-X (HEAD) \ / E--F--G--I--J <-- dev, origin/dev \ K--L <-- feature-X.0 准备就绪时,我将使用git rebase -i清理所有内容。)

如果出于任何原因必须重复该过程,则将当前进行中的feature-X重命名为feature-Xfeature-X.1或其他名称。我种了一小堆feature-X.2-es,偶尔去树枝花园修剪最杂草。 最新和最好的名称仍为feature-X,但是我可以根据需要提供所有以前的工作,直到我将它们淘汰为止。或存储,因为如果代码很复杂并且我错过了某些东西,那么我仍然拥有较旧的版本,其名称可以识别,不仅是引用日志中一些难以理解的哈希ID,而且与其他存储也没有区别其他藏匿处。