Git - 重新融合

标签 git

merge 与 rebase 的主要缺点是 merge 会导致树困惑。如果 master 经常被更新,那么每次有重大更新时就 merge 到 mastery 中会产生一大堆不必要的提交。现在,大多数时候,实际上没有任何必要这样做。考虑存储库如下:

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

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

所以我们回到 o, merge E 并在顶部挑选 p 和 q:

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

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

如果我是第一个想到这种工作流程的人,我会感到很惊讶。是否有任何 git 扩展可以自动执行此操作?

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

最佳答案

编辑: To retroactively enable rerere, see here. .此答案中的脚本不会打开 rerere,并且在 E 的历史(见下文)到 n... D - 在 o 中解决了冲突的帅哥。 rerere 执行 n...E merge 并应用任何仍然有效的 n...D 分辨率,而此脚本执行标准o...E merge (但将父 o 替换为 o)

复制图表以供引用:

      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-reverted n...D 冲突,而脚本会将还原视为进一步的更改。这两个脚本都生成上面的“期望之后”图表,但它们通过不同的路径到达那里。

一个小时的尝试并没有产生比简单地查看图表和推论更容易理解的差异的进一步文本描述。

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


当我做的时候

go back to o, merge E

git checkout o
git merge E

我明白了

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

with o 作为 o' 的父级。您确实需要 o' 内容(以保留您已经在 o 中应用的任何冲突解决方案),但您希望它具有 nE 作为 parent ,而不是 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; }

编辑:update 以在没有什么可挑选时处理更新 merge 。

关于Git - 重新融合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21896171/

相关文章:

git - 创建 git 存储库时如何忽略所有 svn、bin 和 obj 文件夹?

git - VS Code 的 'Commit (Amend)' 和 'Commit (Signed Off)' 有什么区别?

git - 即使每个更改的文件都与其中一个父文件一致,如何 "git show"具有组合差异输出的 merge 提交?

git - 从 Git 部署到 Azure 网站时排除文件

git - 在 SourceTree 上切换分支名称

git - git cherry-pick 是否保存任何元数据?

regex - Jenkins 管道正则表达式模式不匹配

Git rebase 功能分支在向开发/主分支的 pull 请求中弄乱了提交

git difftool 突然停止工作/如何排除故障?

git - 在 git 的一个根文件夹中使用两个存储库?