我有一个存储库,其中包含分支主服务器和A以及两者之间的大量合并活动.当基于master创建分支A时,如何在我的存储库中找到提交?
我的存储库基本上是这样的:
-- X -- A -- B -- C -- D -- F (master) \ / \ / \ / \ / G -- H -- I -- J (branch A)
我正在寻找修订版A,这不是git merge-base (--all)
找到的.
我正在寻找同样的事情,我发现了这个问题.谢谢你的要求!
然而,我发现,我在这里看到的答案似乎并不十分给你要的答案(或者说我一直在寻找) -他们似乎放弃了G
提交,而不是A
提交.
所以,我创建了以下树(按时间顺序分配的字母),所以我可以测试一下:
A - B - D - F - G <- "master" branch (at G) \ \ / C - E --' <- "topic" branch (still at E)
这看起来与你的有点不同,因为我想确保我得到(指的是这个图,而不是你的)B,但不是A(而不是D或E).这是附加到SHA前缀和提交消息的字母(我的repo可以从这里克隆,如果这对任何人都很有趣):
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.经过一些修补之后,我发现了以下三种方法:
您应该在视觉上看到这样的树(从主视图中查看):
或者在这里(从主题上看):
在这两种情况下,我都选择了B
图表中的提交.单击它后,其完整的SHA将显示在图表下方的文本输入字段中.
git log --graph --oneline --all
(编辑/侧注:添加--decorate
也可能很有趣;它添加了分支名称,标签等的指示.不将其添加到上面的命令行,因为下面的输出不反映它的用法.)
显示(假设git config --global color.ui auto
):
或者,在直文中:
* 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
在您的图表中.
你没有在你的问题中指明你是否想要像上面这样的东西,或者只是一个命令,它只会让你获得一个修订版,而不是其他任何东西.嗯,这是后者:
diff -u <(git rev-list --first-parent topic) \ <(git rev-list --first-parent master) | \ sed -ne 's/^ //p' | head -1 6aafd7ff98017c816033df18395c5c1e7829960d
你也可以把你的〜/的.gitconfig为(注:尾随破折号是很重要的,感谢布赖恩的将注意力集中于):
[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
将不会工作- <()
语法不存在香草sh
.(再次感谢@conny,在本页的另一个答案的评论中让我意识到它!)
由于liori用于指出在比较相同分支当上述可以落下,并想出替代的diff形式其去除从所述混合物中的sed形式,并且使这种"安全"的(即,它返回一个结果(即,最近的提交)即使你比较主人和主人):
作为.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' -
从shell:
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,为替代形式.
所以,这就是我[和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`.."'
快乐的giting!
您可能正在寻找git merge-base
:
git merge-base在两次提交之间找到最佳共同祖先,以便在三向合并中使用.一个共同的祖先比另一个共同的祖先更好,如果后者是前者的祖先.没有任何更好的共同祖先的共同祖先是最好的共同祖先,即合并基础.请注意,一对提交可以有多个合并基础.
我已经习惯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
如果你喜欢简洁的命令,
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"的祖先,顾名思义,在 "branch_name",并在"主",因为它是在什么祖先"大师".所以你得到了两个分支中最早的提交.
命令
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.
鉴于此线程中的许多答案都没有给出问题所要求的答案,这里是每个解决方案的结果摘要,以及我用来复制问题中给出的存储库的脚本.
使用给定的结构创建存储库,我们得到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'解决方案只为您提供最新的第一divergance.
这意味着,对我的工作流程,我想我将不得不坚持标注长时间运行的分支的分支点,因为我不能保证他们能够可靠地以后可以找到.
这真的都归结为git
缺少hg
调用命名分支的东西.博主JHW称这些宗族与家庭在他的文章为什么我喜欢水银多的Git和他的后续文章推荐水银主场迎战Git的(使用图形!) .我会建议人们读他们明白了为什么有些善变的皈依怀念没有命名的分支机构在git
.
mipadi提供的解决方案返回两个答案,I
并且C
:
$ 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向我展示了一种更简洁的方法来编写示例存储库.
一般来说,这是不可能的.在分支历史中,在命名分支之前的分支和合并被分支,并且两个命名分支的中间分支看起来相同.
在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"
怎么样的
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`^
肯定我错过了什么,但IMO,上面的所有问题都是因为我们总是试图找到历史中的分支点,并且由于可用的合并组合而导致各种问题.
相反,我采用了不同的方法,基于两个分支共享大量历史的事实,分支前的所有历史记录都是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 :-)