如何在 运行 服务器预接收挂钩时有效地找到分支的最新共享提交?
How to efficiently find the newest shared commit of a branch while running server pre-receive hook?
当服务器预接收挂钩脚本正在执行时,假设它接收到一个名为baz 的新分支。分支 foo、qqaz 和 bar 是现有分支。如何高效地找到baz的最新共享提交(下例中是commit 3,不是commit 2和1)?
---0---1 foo
\
\ 8---9---10 qqaz
\ /
2---3---4 bar
\
5
\
6---7 baz
在任何情况下都正确地做到这一点是从“非常棘手”到“不可能”的任何地方,因为 git push
可以接收请求更改,添加,and/or 多个分支名称一次性完成.也就是说,仅仅因为您的接收 Git 具有名为 foo
、qqaz
和 bar
的分支,分别标识提交 1、10 和 4 now 并不意味着推送完成时会出现这种情况:它可能包含一个请求,将 foo
设置为提交 0,添加一个新的提交 11 并设置 qqaz
指向那个提交,在 4 之后添加几个新提交并更新 bar
,依此类推。
但是,如果您将 git push
限制为单个分支名称更新——如果您控制各种挂钩,您 可以 这样做——问题变得相当严重更容易处理。诀窍是识别预接收挂钩本身运行 在对任何分支名称进行任何更改之前 。如果 baz
是一个 新分支名称 ,那么在存储库的所有分支名称中还没有名称,用于提交 7。(可能已经有一个标签任何这些提交的名称,或标签名称的创建或在要通过此推送更新的引用列表中更新:请务必在设计算法时考虑这些。)
如果我们利用它,那么:
push
请求中有一行000...000 <hash-of-7> refs/heads/baz
形式,表示如果接受此推送,将添加分支名称baz
;
- a
git rev-list <hash-of-7> --not --branches --topo-order
将按顺序列出提交 7、6 和 5;
- 5 的父级是 3。
如果在新的提交序列中有任何合并提交,事情会变得更复杂:
---0---1 foo
\
\ 8---9---10 qqaz
\ /
2---3---4 bar
\ \
----5
\
6---7 baz
这里的提交 5 是一个合并,合并了提交 2 和 3。我不确定在这种情况下你想找到哪个提交。
一般来说,当您希望检查提交图时,git rev-list
是执行此操作的 Git 工具。 --branches
选项告诉它使用所有分支名称; --tags
告诉它使用所有标签名称; --all
告诉它使用所有引用。选项中的 --not
会导致后续引用成为“负面引用”,就好像您列出了 ^refs/heads/dev
以排除分支 dev
.1 您也可以列出要包含或排除的特定模式。有关详细信息,请参阅 the git rev-list
documentation。
1后面的 --not
取消前面的,所以:
git rev-list HEAD --not br1 br2 br3 --not br4
与以下意思相同:
git rev-list HEAD ^br1 ^br2 ^br3 br4
一般来说,正负引用的顺序无关紧要。
有点复杂,但应该给出正确的结果:
git rev-list baz | grep -Ff <(git rev-list foo qqax bar) | head -1
如果您的 shell 不理解 <(...)
运算符,您可以使用临时文件:
git rev-list foo qqax bar > /tmp/commit-list.txt
git rev-list baz | grep -Ff /tmp/commit-list.txt | head -1
另一种方法可以是连续获取 baz
的 merge-base
和目标分支,并保留这些提交中的最新提交。
当服务器预接收挂钩脚本正在执行时,假设它接收到一个名为baz 的新分支。分支 foo、qqaz 和 bar 是现有分支。如何高效地找到baz的最新共享提交(下例中是commit 3,不是commit 2和1)?
---0---1 foo
\
\ 8---9---10 qqaz
\ /
2---3---4 bar
\
5
\
6---7 baz
在任何情况下都正确地做到这一点是从“非常棘手”到“不可能”的任何地方,因为 git push
可以接收请求更改,添加,and/or 多个分支名称一次性完成.也就是说,仅仅因为您的接收 Git 具有名为 foo
、qqaz
和 bar
的分支,分别标识提交 1、10 和 4 now 并不意味着推送完成时会出现这种情况:它可能包含一个请求,将 foo
设置为提交 0,添加一个新的提交 11 并设置 qqaz
指向那个提交,在 4 之后添加几个新提交并更新 bar
,依此类推。
但是,如果您将 git push
限制为单个分支名称更新——如果您控制各种挂钩,您 可以 这样做——问题变得相当严重更容易处理。诀窍是识别预接收挂钩本身运行 在对任何分支名称进行任何更改之前 。如果 baz
是一个 新分支名称 ,那么在存储库的所有分支名称中还没有名称,用于提交 7。(可能已经有一个标签任何这些提交的名称,或标签名称的创建或在要通过此推送更新的引用列表中更新:请务必在设计算法时考虑这些。)
如果我们利用它,那么:
push
请求中有一行000...000 <hash-of-7> refs/heads/baz
形式,表示如果接受此推送,将添加分支名称baz
;- a
git rev-list <hash-of-7> --not --branches --topo-order
将按顺序列出提交 7、6 和 5; - 5 的父级是 3。
如果在新的提交序列中有任何合并提交,事情会变得更复杂:
---0---1 foo
\
\ 8---9---10 qqaz
\ /
2---3---4 bar
\ \
----5
\
6---7 baz
这里的提交 5 是一个合并,合并了提交 2 和 3。我不确定在这种情况下你想找到哪个提交。
一般来说,当您希望检查提交图时,git rev-list
是执行此操作的 Git 工具。 --branches
选项告诉它使用所有分支名称; --tags
告诉它使用所有标签名称; --all
告诉它使用所有引用。选项中的 --not
会导致后续引用成为“负面引用”,就好像您列出了 ^refs/heads/dev
以排除分支 dev
.1 您也可以列出要包含或排除的特定模式。有关详细信息,请参阅 the git rev-list
documentation。
1后面的 --not
取消前面的,所以:
git rev-list HEAD --not br1 br2 br3 --not br4
与以下意思相同:
git rev-list HEAD ^br1 ^br2 ^br3 br4
一般来说,正负引用的顺序无关紧要。
有点复杂,但应该给出正确的结果:
git rev-list baz | grep -Ff <(git rev-list foo qqax bar) | head -1
如果您的 shell 不理解 <(...)
运算符,您可以使用临时文件:
git rev-list foo qqax bar > /tmp/commit-list.txt
git rev-list baz | grep -Ff /tmp/commit-list.txt | head -1
另一种方法可以是连续获取 baz
的 merge-base
和目标分支,并保留这些提交中的最新提交。