Git - 重新进一步下线

时间:2014-02-20 01:21:34

标签: git

合并与变基的主要缺点是合并导致混乱的树。如果主服务器经常被更新,那么每次有重大更新时合并掌握将创建大量不必要的提交。现在,大多数时候,实际上并没有任何需要。如下所示考虑存储库:

Master A\--B--C\--D\---------E
Branch   l--m---n---o--p--q

这里的n是一个合并,我们必须解决大量的冲突,我们希望避免再次解决。我们希望在不创建新的合并提交的情况下将E合并回来

所以我们回到o,合并E和cherry-pick p和q在上面:

Master A\--B--C\--D\--------E\
Branch   l--m---n\--o--p--q   \
Tmp               -------------o'-p-q

如果这没有错误,我们就可以删除旧分支。

如果我是第一个想到这种工作流程的人,我会感到惊讶。有没有可以自动化的git扩展?

我已经开始编写脚本来执行此操作,但主要问题是选择o。 “:/ ^ Merge” - 在大多数情况下会选择o,但是如果没有更好的方法可以避免依赖于不以Merge一词开头的提交消息,那将会感到惊讶。

3 个答案:

答案 0 :(得分:7)

编辑: To retroactively enable rerere, see here.。此答案中的脚本不会重新启动,并且在E的历史记录(见下文)中,n...D中存在进一步更改时,其行为将与rerere不同 - 冲突的帅哥在orerere执行n...E合并并应用仍然有效的任何n...D个解决方案,而此脚本执行标准o...E合并(但将o替换为o {1}})

复制图表以供参考:

      before                                 desired after
Master A\--B--C\--D\....E             \  Master A\--B--C\-D...E\
Branch   l--m---n---o--p--q         /    Branch   l--m---n------o'--p'--q'

rerere将绕过E - 还原n...D冲突,而脚本会将回复视为进一步的更改。这两个脚本都生成了上面的“所需后”图,但它们通过不同的路径到达那里。

一小时的尝试并没有比仅仅查看图表和演绎更能理解这些差异的进一步文本描述。

我不清楚rerere或脚本的行为总是更好。


当我这样做时

  

回到o,合并E

git checkout o
git merge E

我得到了

Master A\--B--C\--D\---------E
Branch   l--m---n---o\-p--q   \
HEAD                  `--------o'

oo'的父级。您确实需要o'内容(以保留您已在o中应用的任何冲突解决方案),但您希望将nE作为父级,而非oE。这实际上相当容易。

所以完整的程序是,

#!/bin/sh

# 1. identify the most recent merge on $1
# 2. verify that it's from $2
# 3. update the merge to $2's current state, 
# 4. preserving any conflict resolutions that were already done
# 5. and cherry-pick everything since.

# most recent merge in $1
update=`git rev-list -1 --min-parents=2 $1`

# verify most recent merge is from correct merge base
test "`git merge-base $1 $2`" = "`git rev-parse $update^2`" \
|| { echo "most recent merge isn't of $1 from $2"; exit 1; }

set -e  # exit immediately on error
git checkout $update
git merge $2 
git checkout $(
      # this makes a new commit with HEAD's commit message and tree
      # but with $update^ and $2 as parents
      git cat-file -p HEAD \
      | sed 1,/^$/d \
      | git commit-tree -p $update^ -p $2 HEAD^{tree}
   )
test $update = `git rev-parse $1` || git cherry-pick $update..$1
git checkout -B $1

如果您愿意丢失在o中完成的任何冲突解决方案,您可以改为(来自set -e

set -e
git checkout $update^
git merge $2
test $update = `git rev-parse $1` || git cherry-pick $update..$1
git checkout -B $1

如果在子shell中工作并从中退出并继续下去并不是什么麻烦,那么你可以通过这样做来减少脆弱性

git merge $2 \
|| { echo "Resolve conflicts and \`exit\`, or \`exit 1\` to not continue"; $SHELL; }

编辑:更新以处理当没有什么可以挑选时更新合并。

答案 1 :(得分:3)

正如我提到in the comments,创建n时激活的git rerere有助于避免在执行以下操作时处理相同的冲突:

git rebase --onto E n q
  

我已经开始编写一个脚本来执行此操作,

说到脚本,处理复杂rebase的最复杂的一个叫做:

git-imerge -- incremental merge and rebase for gitMichael Haggerty

查看comparison matrix here

这将使冲突的大小保持最小(和don't have to rely on git rerere

答案 2 :(得分:1)

你得到了相同的旧冲突,因为你应该将分支重新绑定到主干,而不是将主干从主干合并到分支(这是一种错误的合并方式)。

如果点“n”是一个rebase,那么它就完成了。 l-m-n将在C之上重放,因此分支将从C开始.C之前的变化不再需要重新定位。

当你准备让分支进入主干时,你再次重新绑定它,以便分支从HEAD修订版的trunk开始。此时,分支可以替换主干:您可以检出主干并重置到分支。