检查 post-rewrite hook 是否有冲突
Check if there were conflicts in post-rewrite hook
在 post-rewrite
挂钩的上下文中,有什么方法可以知道导致挂钩触发的变基是否有需要解决的冲突,或者它是否只是重放更改?
简短的回答是 "no"。
更长的答案仍然是"No ... but you can get close, and maybe close enough, by repeating the operation. But see caveats."
泛化
让我们概括一下,这样我们就不仅仅是在谈论一个 post-rewrite 钩子,因为这个问题也适用于重复一个 git rebase
(re-rebasing 在修复日志消息后,例如)或执行 git rebase -p
(保留合并,必须重复较早的合并)。
每当 Git 调用它的 three-way-merge 机器时,它所做的相当于:
ours=$(git rev-parse HEAD) # or equivalent
theirs=$(git rev-parse $something_else)
base=$(something) # usually git merge-base, but this varies as well
git diff $base $ours > our.change
git diff $base $theirs > their.change
combine_our_change_and_their_change
(写在pseudo-shell).
对于常规 git merge
,base=$(something)
部分确实使用 git merge-base
,或者更确切地说,使用 git merge-base
允许您使用的相同代码。具体如何使用取决于合并策略:
- 对于
-s resolve
,Git 以与 git merge-base
相同的方式选择合并基础之一并使用它。
- 对于
-s recursive
,Git选择所有个合并基,合并它们(递归地,使用他们自己的合并基做这些 合并)得到一个"virtual merge base"。在大多数情况下,无论如何只有一个合并基础,这意味着无论如何这与 -s resolve
做同样的事情。在大多数剩余情况下,有两个合并基础,合并这两个合并基础产生与随机选择这两个合并基础之一相同的结果,这意味着这个 still与 -s resolve
相同。在少数情况下,合并合并基础会产生与任一合并基础略有不同的东西,并且在特别有问题的情况下,合并合并基础会导致合并冲突,并且在可能仅在 carefully-contrived 场景中出现的情况下使用 hand-constructed 多维提交图,我们可以拥有两个以上的合并基础,事情变得非常疯狂。但大多数情况下,这与 -s resolve
. 的作用相同
- 对于
-s ours
,Git 实际上根本没有进行合并,整个问题就消失了。
- 对于
-s octopus
,Git找到一个多路合并基,有冲突就中止合并,只有没有合并冲突才结束,这样问题就大同小异了-s ours
. 方式
这归结为我们需要知道使用了哪种策略。不幸的是,Git 无论如何都不会记录这些信息。所以这是警告#1。我们可以猜测,如果有两个合并 parent,策略是 -s recursive
,如果超过两个,则策略是 -s octopus
,但这只是一个猜测。1
如果合并是由 git merge
进行的,那么这一切都是有用的(在任何程度上),但是如果 three-way 合并操作是 git apply -3
或类似的结果呢?在这种情况下,base
可能根本不是 commit,而不是两个 git diff
命令来比较两个 commit,我们可能只想做相当于:
git merge-file $options $ourfile $basefile $theirfile
找到有问题的三个文件有点棘手。在简单的情况下,它们在三个提交中都具有相同的名称,and/or,我们有完整的 blob 哈希 ID,因此我们可以通过哈希 ID 提取文件内容,而不必担心 path-names。 $options
我们仍然有问题,它可以是以下之一:
- 无:正常合并:在发生冲突的情况下以冲突停止
--ours
:git merge
的 -X ours
参数或等效参数:在冲突的情况下支持 "ours"
--theirs
:git merge
的 -X theirs
参数或等效参数:在冲突的情况下支持 "theirs"
--union
: 做联合合并(目前只能通过.gitattributes
指定,联合合并总是成功,即使有冲突)
与策略一样,Git 未能记录此信息。这是警告 #2:重复操作时,您必须猜测额外的策略参数。
警告 #3 是 git rerere
:在发生冲突的情况下,Git 可能 re 使用 recorded re解决方案,因此您不会看到冲突。这对您是否重要取决于您的目标/需求。
应用于post-rewrite hook
现在让我们回到具体的 the post-rewrite hook。此挂钩当前仅在执行 git commit --amend
或 git rebase
后调用,并且它可以通过检查其参数来判断 使用了哪个命令。
然后它在标准输入上接收重写列表:
<old-sha1> SP <new-sha1> [ SP <extra-info> ] LF
The extra-info is again command-dependent. If it is empty, the
preceding SP is also omitted. Currently, no commands pass any
extra-info.
这 "extra info" 可能是我们可能获得一些缺失项目(例如战略和选项)的地方——但我们没有。然而,我们确实知道 rebase 本质上做了一系列 cherry-picks。它还默认使用完整的 commit-wide diff(-m
或 --merge
等)来检测重命名。换句话说,当我们找到要给 git merge-file
的文件名时,我们希望使用 two-git-diffs-against-a-base 方法,以便重复任何给定的 file-level 合并操作。
我们甚至不必走那么远。我们有两个提交哈希:原始提交是 cherry-picked,以及新的结果提交。我们可以挥动一下ch 到一个临时分支——例如一个匿名分离的 HEAD——和一个临时工作树,然后对原始提交(旧 SHA1)执行另一个 git cherry-pick
到新提交(新提交)的 parent SHA1加帽子后缀),使用-n
防止commit本身,看看有没有冲突。模数我们所有的警告,这可以让我们看看用户是否必须解决一些冲突。
但是,我们必须注意 git rebase
的另一个问题:interactive rebase 允许用户 squash
或 fixup
几个原始提交合并为一个最终的新提交。再次引用githooks文档:
For the squash and fixup operation, all commits that were squashed are listed as being rewritten to the squashed commit. This means that there will be several lines sharing the same new-sha1.
某些中间结果(来自应用一个或多个原始提交)可能有或没有一些冲突,这些冲突分别来自或现在出现在最终重写的提交中。我一点都不清楚你想为这些案例做什么。 (就此而言,我根本不清楚您想用 "there were/weren't conflicts to resolve" 做什么!)
1如果合并 commit 是由 git merge
、 和 创建的它有两个以上的 parent,它肯定来自 -s ours
或 -s octopus
。其他合并策略只能解决两个 "heads" (提交被视为分支的尖端)。但是我们可以创建 merge commits 而根本没有 运行 git merge
。例如,git stash
命令就是这样做的。这些都不是任何意义上的正常合并:git stash
只是(ab?)使用 Git 的各种机制以方便和紧凑的形式记录一堆信息以供以后使用 un-stashing.尽管如此,一些提交 objects git stash
是合并 commits.
在 post-rewrite
挂钩的上下文中,有什么方法可以知道导致挂钩触发的变基是否有需要解决的冲突,或者它是否只是重放更改?
简短的回答是 "no"。
更长的答案仍然是"No ... but you can get close, and maybe close enough, by repeating the operation. But see caveats."
泛化
让我们概括一下,这样我们就不仅仅是在谈论一个 post-rewrite 钩子,因为这个问题也适用于重复一个 git rebase
(re-rebasing 在修复日志消息后,例如)或执行 git rebase -p
(保留合并,必须重复较早的合并)。
每当 Git 调用它的 three-way-merge 机器时,它所做的相当于:
ours=$(git rev-parse HEAD) # or equivalent
theirs=$(git rev-parse $something_else)
base=$(something) # usually git merge-base, but this varies as well
git diff $base $ours > our.change
git diff $base $theirs > their.change
combine_our_change_and_their_change
(写在pseudo-shell).
对于常规 git merge
,base=$(something)
部分确实使用 git merge-base
,或者更确切地说,使用 git merge-base
允许您使用的相同代码。具体如何使用取决于合并策略:
- 对于
-s resolve
,Git 以与git merge-base
相同的方式选择合并基础之一并使用它。 - 对于
-s recursive
,Git选择所有个合并基,合并它们(递归地,使用他们自己的合并基做这些 合并)得到一个"virtual merge base"。在大多数情况下,无论如何只有一个合并基础,这意味着无论如何这与-s resolve
做同样的事情。在大多数剩余情况下,有两个合并基础,合并这两个合并基础产生与随机选择这两个合并基础之一相同的结果,这意味着这个 still与-s resolve
相同。在少数情况下,合并合并基础会产生与任一合并基础略有不同的东西,并且在特别有问题的情况下,合并合并基础会导致合并冲突,并且在可能仅在 carefully-contrived 场景中出现的情况下使用 hand-constructed 多维提交图,我们可以拥有两个以上的合并基础,事情变得非常疯狂。但大多数情况下,这与-s resolve
. 的作用相同
- 对于
-s ours
,Git 实际上根本没有进行合并,整个问题就消失了。 - 对于
-s octopus
,Git找到一个多路合并基,有冲突就中止合并,只有没有合并冲突才结束,这样问题就大同小异了-s ours
. 方式
这归结为我们需要知道使用了哪种策略。不幸的是,Git 无论如何都不会记录这些信息。所以这是警告#1。我们可以猜测,如果有两个合并 parent,策略是 -s recursive
,如果超过两个,则策略是 -s octopus
,但这只是一个猜测。1
如果合并是由 git merge
进行的,那么这一切都是有用的(在任何程度上),但是如果 three-way 合并操作是 git apply -3
或类似的结果呢?在这种情况下,base
可能根本不是 commit,而不是两个 git diff
命令来比较两个 commit,我们可能只想做相当于:
git merge-file $options $ourfile $basefile $theirfile
找到有问题的三个文件有点棘手。在简单的情况下,它们在三个提交中都具有相同的名称,and/or,我们有完整的 blob 哈希 ID,因此我们可以通过哈希 ID 提取文件内容,而不必担心 path-names。 $options
我们仍然有问题,它可以是以下之一:
- 无:正常合并:在发生冲突的情况下以冲突停止
--ours
:git merge
的-X ours
参数或等效参数:在冲突的情况下支持 "ours"--theirs
:git merge
的-X theirs
参数或等效参数:在冲突的情况下支持 "theirs"--union
: 做联合合并(目前只能通过.gitattributes
指定,联合合并总是成功,即使有冲突)
与策略一样,Git 未能记录此信息。这是警告 #2:重复操作时,您必须猜测额外的策略参数。
警告 #3 是 git rerere
:在发生冲突的情况下,Git 可能 re 使用 recorded re解决方案,因此您不会看到冲突。这对您是否重要取决于您的目标/需求。
应用于post-rewrite hook
现在让我们回到具体的 the post-rewrite hook。此挂钩当前仅在执行 git commit --amend
或 git rebase
后调用,并且它可以通过检查其参数来判断 使用了哪个命令。
然后它在标准输入上接收重写列表:
<old-sha1> SP <new-sha1> [ SP <extra-info> ] LF
The extra-info is again command-dependent. If it is empty, the preceding SP is also omitted. Currently, no commands pass any extra-info.
这 "extra info" 可能是我们可能获得一些缺失项目(例如战略和选项)的地方——但我们没有。然而,我们确实知道 rebase 本质上做了一系列 cherry-picks。它还默认使用完整的 commit-wide diff(-m
或 --merge
等)来检测重命名。换句话说,当我们找到要给 git merge-file
的文件名时,我们希望使用 two-git-diffs-against-a-base 方法,以便重复任何给定的 file-level 合并操作。
我们甚至不必走那么远。我们有两个提交哈希:原始提交是 cherry-picked,以及新的结果提交。我们可以挥动一下ch 到一个临时分支——例如一个匿名分离的 HEAD——和一个临时工作树,然后对原始提交(旧 SHA1)执行另一个 git cherry-pick
到新提交(新提交)的 parent SHA1加帽子后缀),使用-n
防止commit本身,看看有没有冲突。模数我们所有的警告,这可以让我们看看用户是否必须解决一些冲突。
但是,我们必须注意 git rebase
的另一个问题:interactive rebase 允许用户 squash
或 fixup
几个原始提交合并为一个最终的新提交。再次引用githooks文档:
For the squash and fixup operation, all commits that were squashed are listed as being rewritten to the squashed commit. This means that there will be several lines sharing the same new-sha1.
某些中间结果(来自应用一个或多个原始提交)可能有或没有一些冲突,这些冲突分别来自或现在出现在最终重写的提交中。我一点都不清楚你想为这些案例做什么。 (就此而言,我根本不清楚您想用 "there were/weren't conflicts to resolve" 做什么!)
1如果合并 commit 是由 git merge
、 和 创建的它有两个以上的 parent,它肯定来自 -s ours
或 -s octopus
。其他合并策略只能解决两个 "heads" (提交被视为分支的尖端)。但是我们可以创建 merge commits 而根本没有 运行 git merge
。例如,git stash
命令就是这样做的。这些都不是任何意义上的正常合并:git stash
只是(ab?)使用 Git 的各种机制以方便和紧凑的形式记录一堆信息以供以后使用 un-stashing.尽管如此,一些提交 objects git stash
是合并 commits.