简体   繁体   English

如何在运行服务器预接收钩子时有效地找到分支的最新共享提交?

[英]How to efficiently find the newest shared commit of a branch while running server pre-receive hook?

While the server pre-receive hook script is executing, suppose it receive a new branch called baz.当服务器预接收挂钩脚本正在执行时,假设它接收到一个名为 baz 的新分支。 Branch foo, qqaz and bar are existing branch.分支 foo、qqaz 和 bar 是现有分支。 How to efficiently find the newest shared commit of baz(It's commit 3 in the example below. not commit 2 and 1)?如何有效地找到 baz 的最新共享提交(在下面的示例中是提交 3。不是提交 2 和 1)?

---0---1                   foo
        \
         \   8---9---10    qqaz
          \ /
           2---3---4       bar
                \
                 5
                  \
                   6---7   baz

A bit complex, but should give the correct result:有点复杂,但应该给出正确的结果:

git rev-list baz | grep -Ff <(git rev-list foo qqax bar) | head -1

If your shell does not understand the <(...) operator, you can use a temp file:如果您的 shell 不理解<(...)运算符,您可以使用临时文件:

git rev-list foo qqax bar > /tmp/commit-list.txt
git rev-list baz | grep -Ff /tmp/commit-list.txt | head -1

Another way can be to successively take the merge-base of baz and the target branches, and keep the latest of those commits.另一种方法可以是依次获取baz和目标分支的merge-base ,并保留最新的提交。

To do this correctly in all cases is anywhere from "very tricky" to "impossible", because git push can receive requests to change, add, and/or multiple branch names all in one go.在所有情况下正确执行此操作从“非常棘手”到“不可能”,因为git push可以在一个 go 中接收更改、添加和/或多个分支名称的请求。 That is, just because your receiving Git has branches named foo , qqaz , and bar identifying commits 1, 10, and 4 respectively now does not mean that this will be the case when the push finishes: it might contain a request to set foo to commit 0, to add a new commit 11 and set qqaz to point to that commit, to add several new commits past 4 and update bar , and so on.也就是说,仅仅因为您的接收 Git 具有名为fooqqazbar的分支,分别标识提交 1、10 和 4现在并不意味着推送完成时会出现这种情况:它可能包含将foo设置为的请求提交 0,添加一个新的提交 11 并将qqaz设置为指向该提交,添加几个新的提交超过 4 和更新bar ,等等。

If, however, you limit a git push to a single branch-name update—which you can do if you control the various hooks—the problem becomes considerably more tractable.但是,如果您将git push限制为单个分支名称更新(如果您控制各种钩子,您可以这样做),那么问题变得更加容易处理。 The trick is to recognize that the pre-receive hook itself runs before any changes to any branch names are made .诀窍是在对任何分支名称进行任何更改之前识别预接收挂钩本身运行。 If baz is a new branch name , then there is no name yet, in all of the branch names in the repository, for commit 7. (There might already be a tag name for any of these commits, or a tag name creation or update in the list of references to be updated by this push: be sure to consider these when designing your algorithm.)如果baz是一个新的分支名称,则在存储库中的所有分支名称中还没有名称,用于提交 7。(可能已经有任何这些提交的标签名称,或者标签名称创建或更新在此推送要更新的参考列表中:在设计算法时一定要考虑这些。)

If we make use of that, then:如果我们利用它,那么:

  • the push request has a line of the form 000...000 <hash-of-7> refs/heads/baz in it, indicating that the branch name baz will be added, if this push is accepted; push请求中有一行格式为000...000 <hash-of-7> refs/heads/baz ,表示如果接受此推送,将添加分支名称baz
  • a git rev-list <hash-of-7> --not --branches --topo-order will list commits 7, 6, and 5, in that order; git rev-list <hash-of-7> --not --branches --topo-order列出提交 7、6 和 5;
  • the parent of 5 is 3. 5 的父母是 3。

Should there be any merge commits in the new commit sequence, things get more complicated:如果新提交序列中有任何合并提交,事情就会变得更加复杂:

---0---1                   foo
        \
         \   8---9---10    qqaz
          \ /
           2---3---4       bar
            \   \
             ----5
                  \
                   6---7   baz

Here commit 5 is a merge, combining commits 2 and 3. I'm not sure which commit(s) you want to find in this case.这里的提交 5 是一个合并,结合了提交 2 和 3。我不确定在这种情况下你想找到哪个提交。

In general, when you wish to examine the commit graph, git rev-list is the Git tool for doing so.通常,当您希望检查提交图时, git rev-list是用于执行此操作的 Git 工具。 The --branches option tells it to use all branch names; --branches选项告诉它使用所有分支名称; --tags tells it to use all tag names; --tags告诉它使用所有标签名称; --all tells it to use all references. --all告诉它使用所有引用。 A --not in the options causes subsequent references to be "negative references", as if you had listed, say, ^refs/heads/dev to exclude branch dev .选项中的--not会导致后续引用成为“负面引用”,就像您列出了^refs/heads/dev以排除分支dev一样。 1 You can list particular patterns to include or exclude as well. 1您也可以列出要包括或排除的特定模式。 See the git rev-list documentation for details.有关详细信息,请参阅git rev-list文档


1 A later --not cancels an earlier one, so that: 1 A later --not取消较早的,因此:

git rev-list HEAD --not br1 br2 br3 --not br4

means the same thing as:与以下内容相同:

git rev-list HEAD ^br1 ^br2 ^br3 br4

and in general the order of positive and negative references does not matter.通常,正面和负面参考的顺序并不重要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM