用Git找到分支点?

时间:2009-10-06 18:22:29

标签: git branch

我有一个存储库,其中包含分支主服务器和A以及两者之间的大量合并活动。当基于master创建分支A时,如何在我的存储库中找到提交?

我的存储库基本上是这样的:

-- X -- A -- B -- C -- D -- F  (master) 
          \     /   \     /
           \   /     \   /
             G -- H -- I -- J  (branch A)

我正在寻找修订版A,这不是git merge-base (--all)找到的版本。

23 个答案:

答案 0 :(得分:448)

我正在寻找同样的事情,我发现了这个问题。谢谢你的提问!

然而,我发现我在这里看到的答案似乎相当给出了你要求的答案(或者我正在寻找) - 他们似乎给了{{1提交,而不是G提交。

所以,我创建了以下树(按时间顺序分配的字母),所以我可以测试一下:

A

这看起来与你的有点不同,因为我想确保我得到(指的是这个图,而不是你的)B,但不是A(而不是D或E)。以下是附加到SHA前缀和提交消息的字母(我的repo可以从here克隆,如果这对任何人都有意义的话):

A - B - D - F - G   <- "master" branch (at G)
     \   \     /
      C - E --'     <- "topic" branch (still at E)

所以, 目标:找到B 。经过一些修补之后,我发现了以下三种方式:


1。在视觉上,用gitk:

您应该直观地看到这样的树(从主人看来):

gitk screen capture from master

或此处(从主题上看):

gitk screen capture from topic

在这两种情况下,我都在图表中选择了G: a9546a2 merge from topic back to master F: e7c863d commit on master after master was merged to topic E: 648ca35 merging master onto topic D: 37ad159 post-branch commit on master C: 132ee2a first commit on topic branch B: 6aafd7f second commit on master before branching A: 4112403 initial commit on master 的提交。单击它后,其完整的SHA将显示在图表下方的文本输入字段中。


2。视觉上,但从终端:

B

(编辑/侧注:添加git log --graph --oneline --all也很有意思;它添加了分支名称,标签等的指示。不将此添加到上面的命令行,因为下面的输出不是不反映它的用途。)

显示(假设--decorate):

output of git log --graph --oneline --all

或者,直接文字:

*   a9546a2 merge from topic back to master
|\  
| *   648ca35 merging master onto topic
| |\  
| * | 132ee2a first commit on topic branch
* | | e7c863d commit on master after master was merged to topic
| |/  
|/|   
* | 37ad159 post-branch commit on master
|/  
* 6aafd7f second commit on master before branching
* 4112403 initial commit on master

在任何一种情况下,我们都将6aafd7f提交视为最低公共点,即我的图表中的git config --global color.ui auto,或者您的B


3。有了shell魔法:

你没有在你的问题中指明你是否想要像上面这样的东西,或者只是一个命令,它只会让你获得一个修订,而不是其他任何东西。嗯,这是后者:

A

您也可以将(注意:尾随短划线很重要;感谢Brian引起注意):

diff -u <(git rev-list --first-parent topic) \
             <(git rev-list --first-parent master) | \
     sed -ne 's/^ //p' | head -1
6aafd7ff98017c816033df18395c5c1e7829960d

可以通过以下方式(使用引号进行复杂化)命令行来完成:

[alias]
    oldest-ancestor = !zsh -c 'diff -u <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | sed -ne \"s/^ //p\" | head -1' -

注意:git config --global alias.oldest-ancestor '!zsh -c '\''diff -u <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | sed -ne "s/^ //p" | head -1'\'' -' 可能很容易zsh,但bash 工作 - sh语法没有存在于香草<()中。 (再次感谢@conny,在本页的另一个答案的评论中让我意识到这一点!)

注:以上的替代版本:

感谢lioripointing out,在比较相同的分支时会出现上述问题,并提出另一种差异形式,从混合中删除sed形式,并使其“更安全” (即,即使您将master与master进行比较,它也会返回一个结果(即最近的提交)):

作为.git-config行:

sh

来自shell:

[alias]
    oldest-ancestor = !zsh -c 'diff --old-line-format='' --new-line-format='' <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | head -1' -

所以,在我的测试树中(暂时不可用,抱歉;它回来了),现在可以在master和topic上工作(分别提交G和B)。再次感谢liori,以替代形式。


所以,这就是我[和liori]提出来的。它似乎对我有用。它还允许另外几个可能非常方便的别名:

git config --global alias.oldest-ancestor '!zsh -c '\''diff --old-line-format='' --new-line-format='' <(git rev-list --first-parent "${1:-master}") <(git rev-list --first-parent "${2:-HEAD}") | head -1'\'' -'

快乐的giting!

答案 1 :(得分:116)

您可能正在寻找git merge-base

  

git merge-base 在两次提交之间找到最佳共同祖先,以便在三向合并中使用。如果后者是前者的祖先,一个共同的祖先更好比另一个共同的祖先。没有任何更好的共同祖先的共同祖先是最佳共同祖先,即合并基础。请注意,一对提交可以有多个合并基础。

答案 2 :(得分:37)

我已经将git rev-list用于此类事情。例如,(注意 3 点)

$ git rev-list --boundary branch-a...master | grep "^-" | cut -c2-

将吐出分支点。现在,它并不完美;既然你已经将master合并到分支A中几次,那么就会分成几个可能的分支点(基本上是原始分支点,然后是你将master合并到分支A的每个点) )。但是,它至少应该缩小可能性。

我已将该命令添加到~/.gitconfig中的别名中:

[alias]
    diverges = !sh -c 'git rev-list --boundary $1...$2 | grep "^-" | cut -c2-'

所以我可以称之为:

$ git diverges branch-a master

答案 3 :(得分:28)

如果您喜欢简洁的命令,

git rev-list $(git rev-list --first-parent ^branch_name master | tail -n1)^^! 

这是一个解释。

以下命令为您提供创建branch_name后发生的master中所有提交的列表

git rev-list --first-parent ^branch_name master 

由于您只关心最早的提交,因此您需要输出的最后一行:

git rev-list ^branch_name --first-parent master | tail -n1

根据定义,不是“branch_name”的祖先的最早提交的父级是 in “branch_name”,并且在“master”中,因为它是“master”中的某个东西的祖先。 “所以你得到了两个分支中最早的提交。

命令

git rev-list commit^^!

只是一种显示父提交引用的方法。你可以用

git log -1 commit^

或其他什么。

PS:我不同意祖先秩序无关紧要的说法。这取决于你想要什么。例如,在这种情况下

_C1___C2_______ master
  \    \_XXXXX_ branch A (the Xs denote arbitrary cross-overs between master and A)
   \_____/ branch B

输出C2作为“分支”提交是完全合理的。这是开发人员从“主人”扩展出来的时候。当他分支时,分支“B”甚至没有合并在他的分支中!这就是本文中的解决方案。

如果您想要的是最后一次提交C,使得从原点到分支“A”上的最后一次提交的所有路径都通过C,那么您希望忽略祖先顺序。这纯粹是拓扑,并且让您了解自从何时有两个版本的代码同时出现。那就是当你使用基于合并库的方法时,它会在我的例子中返回C1。

答案 4 :(得分:19)

鉴于此线程中的许多答案都没有给出问题所要求的答案,这里是每个解决方案的结果摘要,以及我用来复制问题中给出的存储库的脚本。

日志

使用给定的结构创建存储库,我们得到git日志:

$ git --no-pager log --graph --oneline --all --decorate
* b80b645 (HEAD, branch_A) J - Work in branch_A branch
| *   3bd4054 (master) F - Merge branch_A into branch master
| |\  
| |/  
|/|   
* |   a06711b I - Merge master into branch_A
|\ \  
* | | bcad6a3 H - Work in branch_A
| | * b46632a D - Work in branch master
| |/  
| *   413851d C - Merge branch_A into branch master
| |\  
| |/  
|/|   
* | 6e343aa G - Work in branch_A
| * 89655bb B - Work in branch master
|/  
* 74c6405 (tag: branch_A_tag) A - Work in branch master
* 7a1c939 X - Work in branch master

我唯一的补充,是标签,它明确了我们创建分支的点,从而我们希望找到的提交。

有效的解决方案

唯一有效的解决方案是lindes正确返回A提供的解决方案:

$ diff -u <(git rev-list --first-parent branch_A) \
          <(git rev-list --first-parent master) | \
      sed -ne 's/^ //p' | head -1
74c6405d17e319bd0c07c690ed876d65d89618d5

正如Charles Bailey指出的那样,这个解决方案非常脆弱。

如果您branch_A进入master,然后将master合并到branch_A而不干预提交,则lindes'解决方案只会为您提供最近的第一次优势

这意味着对于我的工作流程,我认为我将不得不坚持标记长期运行分支的分支点,因为我无法保证以后可以可靠地找到它们。

这一切都归结为git缺少hg调用命名分支的内容。博主jhw在他的文章Why I Like Mercurial More Than Git及其后续文章More On Mercurial vs. Git (with Graphs!)中称这些血统家庭。我建议人们阅读它们,看看为什么有些mercurial转换器错过git中没有命名分支

不起作用的解决方案

mipadi提供的解决方案会返回两个答案IC

$ git rev-list --boundary branch_A...master | grep ^- | cut -c2-
a06711b55cf7275e8c3c843748daaa0aa75aef54
413851dfecab2718a3692a4bba13b50b81e36afc

Greg Hewgill提供的解决方案返回I

$ git merge-base master branch_A
a06711b55cf7275e8c3c843748daaa0aa75aef54
$ git merge-base --all master branch_A
a06711b55cf7275e8c3c843748daaa0aa75aef54

Karl提供的解决方案会返回X

$ diff -u <(git log --pretty=oneline branch_A) \
          <(git log --pretty=oneline master) | \
       tail -1 | cut -c 2-42
7a1c939ec325515acfccb79040b2e4e1c3e7bbe5

脚本

mkdir $1
cd $1
git init
git commit --allow-empty -m "X - Work in branch master"
git commit --allow-empty -m "A - Work in branch master"
git branch branch_A
git tag branch_A_tag     -m "Tag branch point of branch_A"
git commit --allow-empty -m "B - Work in branch master"
git checkout branch_A
git commit --allow-empty -m "G - Work in branch_A"
git checkout master
git merge branch_A       -m "C - Merge branch_A into branch master"
git checkout branch_A
git commit --allow-empty -m "H - Work in branch_A"
git merge master         -m "I - Merge master into branch_A"
git checkout master
git commit --allow-empty -m "D - Work in branch master"
git merge branch_A       -m "F - Merge branch_A into branch master"
git checkout branch_A
git commit --allow-empty -m "J - Work in branch_A branch"

我怀疑git版本对此有很大影响,但是:

$ git --version
git version 1.7.1

感谢Charles Bailey向我展示了一种更简洁的方法来编写示例存储库。

答案 5 :(得分:10)

一般来说,这是不可能的。在分支历史记录中,在命名分支之前的分支和合并被分支,并且两个命名分支的中间分支看起来相同。

在git中,分支只是历史部分提示的当前名称。他们并没有真正的强烈身份。

这通常不是一个大问题,因为两个提交的合并基础(参见Greg Hewgill的回答)通常更有用,给出了两个分支共享的最新提交。

依赖于提交父母顺序的解决方案显然不适用于分支在历史中某个时刻完全集成的情况。

git commit --allow-empty -m root # actual branch commit
git checkout -b branch_A
git commit --allow-empty -m  "branch_A commit"
git checkout master
git commit --allow-empty -m "More work on master"
git merge -m "Merge branch_A into master" branch_A # identified as branch point
git checkout branch_A
git merge --ff-only master
git commit --allow-empty -m "More work on branch_A"
git checkout master
git commit --allow-empty -m "More work on master"

如果已经与父对象进行了集成合并,这种技术也会失败(例如,临时分支用于执行测试合并到主服务器,然后快速转发到功能分支以进一步构建)。

git commit --allow-empty -m root # actual branch point
git checkout -b branch_A
git commit --allow-empty -m  "branch_A commit"
git checkout master
git commit --allow-empty -m "More work on master"
git merge -m "Merge branch_A into master" branch_A # identified as branch point
git checkout branch_A
git commit --allow-empty -m "More work on branch_A"

git checkout -b tmp-branch master
git merge -m "Merge branch_A into tmp-branch (master copy)" branch_A
git checkout branch_A
git merge --ff-only tmp-branch
git branch -d tmp-branch

git checkout master
git commit --allow-empty -m "More work on master"

答案 6 :(得分:5)

这样的东西
git log --pretty=oneline master > 1
git log --pretty=oneline branch_A > 2

git rev-parse `diff 1 2 | tail -1 | cut -c 3-42`^

答案 7 :(得分:5)

肯定我错过了什么,但IMO,上面的所有问题都是因为我们总是试图找到历史中的分支点,并且由于可用的合并组合而导致各种问题。 / p>

相反,我采用了不同的方法,基于两个分支共享大量历史的事实,分支前的所有历史记录都是100%相同的,所以我的提议不是回头,而是关于前进(从第1次提交),寻找两个分支的第一个差异。简单地说,分支点将是找到的第一个差异的父级。

在实践中:

#!/bin/bash
diff <( git rev-list "${1:-master}" --reverse --topo-order ) \
     <( git rev-list "${2:-HEAD}" --reverse --topo-order) \
--unified=1 | sed -ne 's/^ //p' | head -1

它解决了我所有常见的情况。当然有没有覆盖的边境但是...... ciao: - )

答案 8 :(得分:4)

我最近也需要解决这个问题,最后为此编写了一个Ruby脚本:https://github.com/vaneyckt/git-find-branching-point

答案 9 :(得分:3)

以上是我之前回答previous answer的改进版本。它依赖于来自合并的提交消息来查找首次创建分支的位置。

它适用于此处提到的所有存储库,我甚至已经解决了一些spawned on the mailing list的棘手问题。我也是wrote tests

find_merge ()
{
    local selection extra
    test "$2" && extra=" into $2"
    git rev-list --min-parents=2 --grep="Merge branch '$1'$extra" --topo-order ${3:---all} | tail -1
}

branch_point ()
{
    local first_merge second_merge merge
    first_merge=$(find_merge $1 "" "$1 $2")
    second_merge=$(find_merge $2 $1 $first_merge)
    merge=${second_merge:-$first_merge}

    if [ "$merge" ]; then
        git merge-base $merge^1 $merge^2
    else
        git merge-base $1 $2
    fi
}

答案 10 :(得分:3)

经过大量的研究和讨论后,很明显没有任何神奇的子弹可以在所有情况下使用,至少在当前版本的Git中没有。

这就是为什么我写了几个添加tail分支概念的补丁。每次创建分支时,也会创建指向原始点的指针tail ref。每次分支重新定位时,此引用都会更新。

要找出devel分支的分支点,你所要做的就是使用devel@{tail},就是这样。

https://github.com/felipec/git/commits/fc/tail

答案 11 :(得分:3)

一种简单的方法,可以更轻松地查看git log --graph中的分支点,就是使用选项--first-parent

例如,从repo中提取accepted answer

$ git log --all --oneline --decorate --graph

*   a9546a2 (HEAD -> master, origin/master, origin/HEAD) merge from topic back to master
|\  
| *   648ca35 (origin/topic) merging master onto topic
| |\  
| * | 132ee2a first commit on topic branch
* | | e7c863d commit on master after master was merged to topic
| |/  
|/|   
* | 37ad159 post-branch commit on master
|/  
* 6aafd7f second commit on master before branching
* 4112403 initial commit on master

现在添加--first-parent

$ git log --all --oneline --decorate --graph --first-parent

* a9546a2 (HEAD -> master, origin/master, origin/HEAD) merge from topic back to master
| * 648ca35 (origin/topic) merging master onto topic
| * 132ee2a first commit on topic branch
* | e7c863d commit on master after master was merged to topic
* | 37ad159 post-branch commit on master
|/  
* 6aafd7f second commit on master before branching
* 4112403 initial commit on master

这更容易!

请注意,如果仓库有很多分支,您将要指定要比较的2个分支,而不要使用--all

$ git log --decorate --oneline --graph --first-parent master origin/topic

答案 12 :(得分:2)

我好像很高兴

git rev-list branch...master

你得到的最后一行是分支上的第一个提交,所以这是获得它的父级的问题。所以

git rev-list -1 `git rev-list branch...master | tail -1`^

似乎适合我,不需要差异等等(这是有帮助的,因为我们没有那个版本的差异)

更正:如果您在主分支上,这不起作用,但我在脚本中执行此操作,这样就不会出现问题

答案 13 :(得分:2)

要从分支点查找提交,您可以使用此功能。

git log --ancestry-path master..topicbranch

答案 14 :(得分:2)

以下命令将显示Commit A的SHA1

git merge-base --fork-point A

答案 15 :(得分:2)

有时它实际上是不可能的(除了你可能有幸获得额外数据的一些例外),这里的解决方案不会有效。

Git不会保留引用历史记录(包括分支)。它仅存储每个分支(头部)的当前位置。这意味着随着时间的推移你可能会失去一些git中的分支历史。例如,每当你进行分支时,它会立即丢失哪个分支是原始分支。所有分支都是:

git checkout branch1    # refs/branch1 -> commit1
git checkout -b branch2 # branch2 -> commit1

您可能认为第一个提交的是分支。这往往是这种情况,但并非总是如此。在上述操作之后,没有什么能阻止你首先进入任何一个分支。此外,git时间戳不保证可靠。直到你承诺它们才真正成为结构上的分支。

虽然在图中我们倾向于在概念上对提交进行编号,但是当提交树分支时,git没有真正稳定的序列概念。在这种情况下,您可以假设数字(表示顺序)由时间戳确定(当您将所有时间戳设置为相同时,查看git UI如何处理事情可能会很有趣。)

这是人类在概念上的期望:

After branch:
       C1 (B1)
      /
    -
      \
       C1 (B2)
After first commit:
       C1 (B1)
      /
    - 
      \
       C1 - C2 (B2)

这是你实际得到的:

After branch:
    - C1 (B1) (B2)
After first commit (human):
    - C1 (B1)
        \
         C2 (B2)
After first commit (real):
    - C1 (B1) - C2 (B2)

你会假设B1是原始分支,但它实际上可能只是一个死分支(有人做了结账-b但从未承诺过它)。直到您同时承诺在git中获得合法的分支结构:

Either:
      / - C2 (B1)
    -- C1
      \ - C3 (B2)
Or:
      / - C3 (B1)
    -- C1
      \ - C2 (B2)

你总是知道C1在C2和C3之前出现但是你从来没有可靠地知道C2是否在C3之前出现在C2之前(因为你可以将工作站上的时间设置为任何东西)。 B1和B2也具有误导性,因为您无法知道哪个分支首先出现。在许多情况下,你可以做出非常好的,通常是准确的猜测。这有点像赛道。所有事情通常都与汽车相同,那么你可以假设一辆后排的汽车开始落后一圈。我们也有非常可靠的约定,例如,主人几乎总是代表最长寿的分支,虽然遗憾的是我看到的情况甚至不是这样。

此处给出的示例是历史保留示例:

Human:
    - X - A - B - C - D - F (B1)
           \     / \     /
            G - H ----- I - J (B2)
Real:
            B ----- C - D - F (B1)
           /       / \     /
    - X - A       /   \   /
           \     /     \ /
            G - H ----- I - J (B2)

真实的这里也有误导性,因为我们作为人类从左到右,从根到叶(ref)读它。 Git不这样做。我们在头部git做的(A&gt; B)(A&lt; -B或B-> A)。它从ref读到root。 Refs可以在任何地方,但往往是叶子,至少对于活跃的分支。 ref指向一个提交,并且提交只包含他们父母的喜欢,而不是他们的孩子。当提交是合并提交时,它将具有多个父级。第一个父项始终是合并到的原始提交。其他父母总是提交并合并到原始提交中。

Paths:
    F->(D->(C->(B->(A->X)),(H->(G->(A->X))))),(I->(H->(G->(A->X))),(C->(B->(A->X)),(H->(G->(A->X)))))
    J->(I->(H->(G->(A->X))),(C->(B->(A->X)),(H->(G->(A->X)))))

这不是一个非常有效的表示,而是git可以从每个ref(B1和B2)中获取的所有路径的表达式。

Git的内部存储看起来更像是这样(不是A作为父级出现两次):

    F->D,I | D->C | C->B,H | B->A | A->X | J->I | I->H,C | H->G | G->A

如果转储原始git提交,您将看到零个或多个父字段。如果为零,则表示没有父,并且提交是根(实际上可以有多个根)。如果有,那就意味着没有合并,而且它不是root提交。如果有多个,则意味着提交是合并的结果,并且第一个之后的所有父项都是合并提交。

Paths simplified:
    F->(D->C),I | J->I | I->H,C | C->(B->A),H | H->(G->A) | A->X
Paths first parents only:
    F->(D->(C->(B->(A->X)))) | F->D->C->B->A->X
    J->(I->(H->(G->(A->X))) | J->I->H->G->A->X
Or:
    F->D->C | J->I | I->H | C->B->A | H->G->A | A->X
Paths first parents only simplified:
    F->D->C->B->A | J->I->->G->A | A->X
Topological:
    - X - A - B - C - D - F (B1)
           \
            G - H - I - J (B2)

当两者都击中A时,他们的链将是相同的,在此之前他们的链将完全不同。第一个提交另外两个提交的共同点是共同的祖先,从那里他们分歧。这里可能存在一些混淆,包括commit,branch和ref。实际上,您可以合并提交。这就是合并的真正作用。 ref只是指向一个提交,而一个分支只不过是文件夹.git / refs / heads中的ref,文件夹位置决定了ref是一个分支而不是其他东西,比如标签。

如果您丢失了历史记录,那么合并将根据具体情况执行以下两种操作之一。

考虑:

      / - B (B1)
    - A
      \ - C (B2)

在这种情况下,在任一方向上的合并将创建一个新的提交,其中第一个父级作为当前签出分支指向的提交,第二个父级作为提交,位于您合并到当前分支的分支的顶端。它必须创建一个新的提交,因为两个分支都有变化,因为它们的共同祖先必须合并。

      / - B - D (B1)
    - A      /
      \ --- C (B2)

此时D(B1)现在具有来自两个分支(本身和B2)的两组变化。但是第二个分支没有B1的变化。如果你将B1中的变化合并到B2中以便它们同步,那么你可能会期望看起来像这样(你可以强制git merge这样做,但是使用--no-ff):

Expected:
      / - B - D (B1)
    - A      / \
      \ --- C - E (B2)
Reality:
      / - B - D (B1) (B2)
    - A      /
      \ --- C

即使B1有其他提交,你也会得到它。只要B1没有发生B1的变化,两个分支就会被合并。它做了一个快速前进,就像一个rebase(rebases也吃或线性化历史),除了一个rebase,因为只有一个分支有一个变更集,它不必从一个分支应用变更集来自另一个。

From:
      / - B - D - E (B1)
    - A      /
      \ --- C (B2)
To:
      / - B - D - E (B1) (B2)
    - A      /
      \ --- C

如果你停止在B1工作,那么从长远来看,保存历史的事情基本上是好的。只有B1(可能是主人)通常会前进,因此B2在B2历史中的位置成功地代表了它被合并到B1的点。这就是git期望你做的事情,从A分支B,然后你可以随着变量累积将A合并到B中,但是当将B合并回A时,预计你不会工作在B和更进一步。如果您在快速前进后将其合并到您正在处理的分支中继续工作,那么您每次都会删除B以前的历史记录。每次快进提交到源然后提交分支后,您真的创建了一个新分支。最后,当你快进提交时,你可以在历史和结构中看到很多分支/合并但是没有能力确定该分支的名称是什么,或者看起来像两个独立的分支是真的是同一个分支

         0   1   2   3   4 (B1)
        /-\ /-\ /-\ /-\ /
    ----   -   -   -   -
        \-/ \-/ \-/ \-/ \
         5   6   7   8   9 (B2)

1到3和5到8是结构分支,如果你按照4或9的历史记录显示。在git中没有办法知道这些未命名和未引用的结构分支属于哪个named和references分支作为结构的结尾。你可以假设从这张图中0到4属于B1而4到9属于B2但是除了4和9之外我不知道哪个分支属于哪个分支,我只是在一个分支中绘制它给出幻觉的方式。 0可能属于B2,5可能属于B1。在这种情况下有16种不同的可能性,其中每个结构分支都可以属于命名分支。这假设这些结构分支都不是来自已删除的分支,或者是从主服务器中提取时将分支合并到自身中(两个存储库上的相同分支名称实际上是两个分支,一个单独的存储库就像分支所有分支一样)

有很多git策略可以解决这个问题。您可以强制git merge永远不会快进并始终创建合并分支。根据您选择的某种惯例,保留分支历史记录的一种可怕方法是使用标记和/或分支(真的建议使用标记)。我真的不会在你要合并的分支中推荐虚拟空提交。一个非常常见的约定是在您想要真正关闭分支之前不要合并到集成分支。这是一种人们应该坚持的做法,否则你就会在拥有分支机构的过程中工作。然而,在现实世界中,理想并不总是实用的,这意味着做正确的事情对每种情况都不可行。如果你在分支机构上做的事情是孤立的,可以工作,但是你可能会遇到这样的情况:当多个开发人员正在做一件事情时,他们需要快速分享他们的变化(理想情况下你可能真的想要在一个分支,但并非所有情况都适合这种情况,通常两个人在分支机构工作是你想要避免的事情。)

答案 16 :(得分:1)

这个问题不是解决方案,但我认为值得注意的是当我有一个长寿分支时使用的方法:

在创建分支的同时,我还创建了一个名称相同但后缀为-init的代码,例如feature-branchfeature-branch-init

(这是一个很难回答的问题!)

答案 17 :(得分:0)

问题似乎是在一侧的两个分支之间找到最近的单一提交 cut ,另一方面找到最早的共同祖先(可能是回购的初始提交)。这符合我对&#34;分支&#34;点是。

考虑到这一点,使用普通的git shell命令计算起来并不容易,因为WHERE table2.start <= table1.end AND table1.start <= table2.end - 我们最强大的工具 - 并没有让我们限制的路径。 达成了提交。我们最接近的是git rev-list,它可以为我们提供一组阻止我们方式的所有提交&#34;。 (注意:git rev-list --boundary很有意思,但我不知道如何在这里使它变得有用。)

以下是脚本:https://gist.github.com/abortz/d464c88923c520b79e3d。它相对简单,但由于循环的复杂性足以保证要点。

请注意,此处提出的大多数其他解决方案可能无法在所有情况下工作,原因很简单:git rev-list --ancestry-path在线性化历史记录时不可靠,因为可以与任何一个排序合并。

另一方面,

git rev-list --first-parent 非常非常有用 - 用于按地形顺序进行提交 - 但做差异很脆弱:给定图形有多种可能的地形排序,所以你依赖于一定的稳定性。也就是说,强大的solution大部分时间都可能运作得很好。然而,由于必须走两次回购的整个历史,我的速度会慢一些。 : - )

答案 18 :(得分:0)

以下实现了 svn log --stop-on-copy 的git等价物,也可用于查找分支原点。

<强>方法

  1. 领导所有分支机构
  2. 为目标分支收集mergeBase彼此分支
  3. git.log并迭代
  4. 在第一次提交时停止显示在mergeBase列表中
  5. 像所有奔向大海的河流一样,所有的分支都是掌握的,因此我们在看似无关的分支之间找到合并基础。当我们从分支头回到祖先时,我们可以停在第一个潜在的合并基地,因为理论上它应该是这个分支的起点。

    备注

    • 我没有尝试过这种方法,兄弟姐妹和堂兄的分支在彼此之间融合。
    • 我知道必须有更好的解决方案。

    详细信息:https://stackoverflow.com/a/35353202/9950

答案 19 :(得分:0)

似乎使用 reflog 解决了这个问题 git reflog <branchname> 显示分支的所有提交,包括分支创建。

这是来自一个在合并回 master 之前有 2 个提交的分支。

git reflog june-browser-updates
b898b15 (origin/june-browser-updates, june-browser-updates) june-browser-updates@{0}: commit: sorted cve.csv
467ae0e june-browser-updates@{1}: commit: browser updates and cve additions
d6a37fb june-browser-updates@{2}: branch: Created from HEAD

答案 20 :(得分:-1)

您可以使用以下命令返回branch_a中最旧的提交,该提交无法从master访问:

git rev-list branch_a ^master | tail -1

也许通过额外的健全性检查,该提交的父级 实际上可以从主...

答案 21 :(得分:-2)

我相信我找到了一种方法来处理这里提到的所有角落案例:

branch=branch_A
merge=$(git rev-list --min-parents=2 --grep="Merge.*$branch" --all | tail -1)
git merge-base $merge^1 $merge^2

Charles Bailey非常正确,基于祖先顺序的解决方案只有有限的价值;在一天结束时,你需要某种记录“这个提交来自分支X”,但这样的记录已经存在;默认情况下,'git merge'会使用提交消息,例如“合并分支'branch_A'到master”,这告诉你来自第二个父(commit ^ 2)的所有提交都来自'branch_A'并被合并到第一个parent(commit ^ 1),即'master'。

有了这些信息,您可以找到'branch_A'的首次合并(当'branch_A'确实存在时),并找到合并基,这将是分支点:)

我已经尝试过Mark Booth和Charles Bailey的存储库并且解决方案有效;怎么可能不行?唯一不行的方法是,如果您手动更改了合并的默认提交消息,则分支信息确实丢失。

有用:

[alias]
    branch-point = !sh -c 'merge=$(git rev-list --min-parents=2 --grep="Merge.*$1" --all | tail -1) && git merge-base $merge^1 $merge^2'

然后你可以做'git branch-point branch_A'。

享受;)

答案 22 :(得分:-2)

您可以检查分支A的reflog,以查找创建它的提交,以及该分支指向的提交的完整历史记录。 Reflog位于.git/logs