我有一个带有master和a分支的存储库,在这两个分支之间有很多合并活动。当分支A基于master创建时,我如何在我的存储库中找到提交?

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

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

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


当前回答

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

在git中,分支只是历史记录部分提示的当前名称。他们并没有很强的认同感。

这通常不是一个大问题,因为两个提交的merge-base(参见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"

其他回答

要从分支点查找提交,可以使用这个。

git log --ancestry-path master..topicbranch

你可以使用下面的命令返回branch_a中最老的提交,master无法访问它:

git rev-list branch_a ^master | tail -1

也许有一个额外的健全性检查,该提交的父节点实际上可以从master访问…

经过大量的研究和讨论,很明显没有什么灵丹妙药能在所有情况下都起作用,至少在当前版本的Git中不是这样。

这就是为什么我写了几个补丁,增加了尾巴分支的概念。每次创建分支时,也会创建一个指向原始点的指针,即tail ref。每当分支重基时,这个ref都会更新。

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

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

下面实现了等价于svn log——stop-on-copy的git,也可以用来查找分支的起源。

方法

前往所有分支 收集目标分支和其他分支的mergeBase log和迭代 在mergeBase列表中出现的第一次提交时停止

就像所有的河流都流向大海,所有的分支都流向主人,因此我们在看似不相关的分支之间找到了合并基地。当我们从分支头通过祖先返回时,我们可以在第一个潜在的合并基点上停下来,因为理论上它应该是这个分支的原点。

笔记

我还没有尝试过这种兄弟分支和兄弟分支相互合并的方法。 我知道肯定有更好的解决办法。

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

我也在寻找同样的东西,我发现了这个问题。谢谢你的提问!

然而,我发现我在这里看到的答案似乎并没有完全给出你所要求的答案(或者我正在寻找的答案)——它们似乎给出了G提交,而不是A提交。

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

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

这看起来和你的有点不同,因为我想确保我得到了(指的是这张图,不是你的)B,但不是a(也不是D或E)。下面是SHA前缀和提交消息附加的字母(我的回购可以从这里克隆,如果有人感兴趣的话):

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

所以,我们的目标是:找到b。以下是我在修改后找到的三种方法:


1. 在视觉上,用gitk:

你应该能看到这样的树(从master上看):

或者在这里(从主题来看):

在这两种情况下,我都选择了提交图中的B。一旦单击它,它的完整SHA就会显示在图形下方的文本输入字段中。


2. 从视觉上看,但从终端来看:

Git日志—图形—一行—全部

(编辑/旁注:添加—装饰也可以很有趣;它添加了分支名称、标记等的指示。没有将它添加到上面的命令行,因为下面的输出没有反映它的使用。)

它显示(假设git配置-global颜色。ui汽车):

或者,直接说:

*   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提交视为最低公共点,即在我的图中是B,或者在你的图中是A。


3.用贝壳魔法:

您没有在问题中指定您想要的是类似上述的东西,还是只提供一个修订版本的单一命令,而不是其他任何命令。下面是后者:

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

你也可以把它放到~/中。Gitconfig as(注意:后面的破折号很重要;谢谢Brian的关注):

[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不能工作——<()语法在vanilla sh中不存在。(再次感谢@conny,让我在本页另一个答案的评论中意识到它!)

注:上述备选版本:

感谢liori指出,在比较相同的分支时,上面的内容可能会失败,并提出了一个替代的diff表单,从混合中删除sed表单,并使其“更安全”(即,即使在比较master和master时,它也会返回一个结果(即最近的提交):

作为.git-config行:

[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' -

从壳上:

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'\'' -'

所以,在我的测试树中(不好意思,它暂时不可用;它回来了),现在对master和topic都有效(分别给出提交G和B)。再次谢谢你,利奥里,给我另一种形式。


所以,这就是我(和liori)想到的。这似乎对我有用。它还允许额外的两个别名,这可能会证明很方便:

git config --global alias.branchdiff '!sh -c "git diff `git oldest-ancestor`.."'
git config --global alias.branchlog '!sh -c "git log `git oldest-ancestor`.."'

git-ing快乐!