我有一个存储库,其中包含分支主服务器和A以及两者之间的大量合并活动.当基于master创建分支A时,如何在我的存储库中找到提交?
我的存储库基本上是这样的:
-- X -- A -- B -- C -- D -- F (master)
\ / \ /
\ / \ /
G -- H -- I -- J (branch A)
Run Code Online (Sandbox Code Playgroud)
我正在寻找修订版A,这不是git merge-base (--all)
找到的.
lin*_*des 468
我正在寻找同样的事情,我发现了这个问题.谢谢你的要求!
然而,我发现,我在这里看到的答案似乎并不十分给你要的答案(或者说我一直在寻找) -他们似乎放弃了G
提交,而不是A
提交.
所以,我创建了以下树(按时间顺序分配的字母),所以我可以测试一下:
A - B - D - F - G <- "master" branch (at G)
\ \ /
C - E --' <- "topic" branch (still at E)
Run Code Online (Sandbox Code Playgroud)
这看起来与你的有点不同,因为我想确保我得到(指的是这个图,而不是你的)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
Run Code Online (Sandbox Code Playgroud)
所以,目标:二分找到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
Run Code Online (Sandbox Code Playgroud)
你也可以把你的〜/的.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' -
Run Code Online (Sandbox Code Playgroud)
这可以通过以下(使用引用进行复杂化)命令行来完成:
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'\'' -'
Run Code Online (Sandbox Code Playgroud)
注: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' -
Run Code Online (Sandbox Code Playgroud)
从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'\'' -'
Run Code Online (Sandbox Code Playgroud)
因此,在我的测试树中(暂时不可用,抱歉;它回来了),现在可以在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`.."'
Run Code Online (Sandbox Code Playgroud)
快乐的giting!
Gre*_*ill 122
您可能正在寻找git merge-base
:
git merge-base在两次提交之间找到最佳共同祖先,以便在三向合并中使用.一个共同的祖先比另一个共同的祖先更好,如果后者是前者的祖先.没有任何更好的共同祖先的共同祖先是最好的共同祖先,即合并基础.请注意,一对提交可以有多个合并基础.
mip*_*adi 39
我已经习惯git rev-list
了这种事情.例如,(注意3个点)
$ git rev-list --boundary branch-a...master | grep "^-" | cut -c2-
Run Code Online (Sandbox Code Playgroud)
会吐出分支点.现在,它并不完美; 因为你已经将master合并到分支A中几次,所以会分出几个可能的分支点(基本上,原始分支点,然后是将master合并到分支A的每个点).但是,至少应该缩小可能性.
我已将该命令添加到我的别名中~/.gitconfig
:
[alias]
diverges = !sh -c 'git rev-list --boundary $1...$2 | grep "^-" | cut -c2-'
Run Code Online (Sandbox Code Playgroud)
所以我可以称之为:
$ git diverges branch-a master
Run Code Online (Sandbox Code Playgroud)
小智 29
如果你喜欢简洁的命令,
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.
Mar*_*oth 18
鉴于此线程中的许多答案都没有给出问题所要求的答案,这里是每个解决方案的结果摘要,以及我用来复制问题中给出的存储库的脚本.
使用给定的结构创建存储库,我们得到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
Run Code Online (Sandbox Code Playgroud)
我唯一补充的是标记,它使我们明确了我们创建分支的点,从而明确了我们希望找到的提交.
有效的唯一解决方案是由lindes提供的正确返回A
:
$ diff -u <(git rev-list --first-parent branch_A) \
<(git rev-list --first-parent master) | \
sed -ne 's/^ //p' | head -1
74c6405d17e319bd0c07c690ed876d65d89618d5
Run Code Online (Sandbox Code Playgroud)
正如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
Run Code Online (Sandbox Code Playgroud)
Greg Hewgill提供的解决方案回归I
$ git merge-base master branch_A
a06711b55cf7275e8c3c843748daaa0aa75aef54
$ git merge-base --all master branch_A
a06711b55cf7275e8c3c843748daaa0aa75aef54
Run Code Online (Sandbox Code Playgroud)
Karl提供的解决方案返回X
:
$ diff -u <(git log --pretty=oneline branch_A) \
<(git log --pretty=oneline master) | \
tail -1 | cut -c 2-42
7a1c939ec325515acfccb79040b2e4e1c3e7bbe5
Run Code Online (Sandbox Code Playgroud)
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"
Run Code Online (Sandbox Code Playgroud)
我怀疑git版本对此有很大的不同,但是:
$ git --version
git version 1.7.1
Run Code Online (Sandbox Code Playgroud)
感谢Charles Bailey向我展示了一种更简洁的方法来编写示例存储库.
CB *_*ley 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"
Run Code Online (Sandbox Code Playgroud)
如果已经与父对象进行了集成合并,则此技术也会失败(例如,临时分支用于执行测试合并到主服务器,然后快速转发到功能分支以进一步构建).
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"
Run Code Online (Sandbox Code Playgroud)
Von*_*onC 10
Git 2.36 提出了一个更简单的命令:
(branch_A_tag)
|
--X--A--B--C--D--F (master)
\ / \ /
\ / \ /
G--H--I--J (branch A)
Run Code Online (Sandbox Code Playgroud)
vonc@vclp MINGW64 ~/git/tests/branchOrigin (branch_A)
git log -1 --decorate --oneline \
$(git rev-parse \
$(git rev-list --exclude-first-parent-only ^main branch_A| tail -1)^ \
)
80e8436 (tag: branch_A_tag) A - Work in branch main
Run Code Online (Sandbox Code Playgroud)
git rev-list --exclude-first-parent-only ^main branch_A
给你J -- I -- H -- G
tail -1
给你Ggit rev-parse G^
给你它的第一个父级:A
或branch_A_tag使用测试脚本:
(branch_A_tag)
|
--X--A--B--C--D--F (master)
\ / \ /
\ / \ /
G--H--I--J (branch A)
Run Code Online (Sandbox Code Playgroud)
这给你:
vonc@vclp MINGW64 ~/git/tests/branchOrigin (branch_A)
git log -1 --decorate --oneline \
$(git rev-parse \
$(git rev-list --exclude-first-parent-only ^main branch_A| tail -1)^ \
)
80e8436 (tag: branch_A_tag) A - Work in branch main
Run Code Online (Sandbox Code Playgroud)
这是:
(branch_A_tag)
|
--X--A--B--C--D--F (master)
\ / \ /
\ / \ /
G--H--I--J (branch A)
Run Code Online (Sandbox Code Playgroud)
在 Git 2.36(2022 年第 2 季度)中,“ git log
” (man)和朋友们学会了一个--exclude-first-parent-only
仅沿着第一个父链向下传播 UNINTERESTING 位的选项,就像--first-parent
选项显示仅沿着第一个父链缺少 UNINTERESTING 位的提交一样。
请参阅Jerry 张 ( )的提交 9d505b7(2022 年 1 月 11 日)。(由Junio C Hamano 合并 -- --在提交 708cbef中,2022 年 2 月 17 日)jerry-skydio
gitster
git-rev-list
: 添加 --exclude-first-parent-only 标志签字人:张杰
了解分支在历史上首次与某些集成分支分离的时间非常有用,以便能够枚举用户的本地更改。
然而,这些局部变化可以包括任意合并,因此在寻找分歧点时有必要忽略这种合并结构。为了做到这一点,教导“
rev-list
家庭接受”--exclude-first-parent-only
,这限制了排除提交的遍历仅遵循第一个父链接。Run Code Online (Sandbox Code Playgroud)-A-----E-F-G--main \ / / B-C-D--topic
在此示例中,目标是返回集合
{B, C, D
},它表示已合并到main
分支中的主题分支。( man )最终将不会返回任何提交,因为排除最终也会遍历提交。(男人) 但是会如愿返回。
git rev-list topic ^main
main
topic
git rev-list --exclude-first-parent-only topic ^main
{B, C, D}
添加新标志的文档,并澄清文档以
--first-parent
指示它仅适用于遍历包含的提交集。
rev-list-options
现在包含在其手册页中:
--first-parent
当查找要包含的提交时,在看到合并提交时仅遵循第一个父提交。
在查看特定主题分支的演变时,此选项可以提供更好的概述,因为合并到主题分支往往只是为了不时调整到更新的上游,并且此选项允许您忽略引入到主题分支的单个提交。你的历史通过这样的合并。
rev-list-options
现在包含在其手册页中:
--exclude-first-parent-only
当查找要排除的提交(带有“{caret}”)时,在看到合并提交时仅遵循第一个父提交。
考虑到任意合并都可以是有效的主题分支更改,这可用于从主题分支与远程分支的分歧点查找主题分支中的更改集。
正如anarcat在评论中指出的,如果您的分支不是派生自master
,而是派生自main
, or prod
, or... 任何其他分支,您可以使用:
Run Code Online (Sandbox Code Playgroud)git for-each-ref --merged="$local_ref" --no-contains="$local_ref" \ --format="%(refname:strip=-1)" --sort='-*authordate' refs/heads
philb还在评论中提到了该--boundary
选项(输出排除边界提交。边界提交以 为前缀-
):
Run Code Online (Sandbox Code Playgroud)git rev-list --exclude-first-parent-only --boundary ^main branch_A | tail -1
直接获取
A
,无需额外git rev-parse G^
怎么样的
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`^
Run Code Online (Sandbox Code Playgroud)
小智 5
肯定我错过了什么,但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
Run Code Online (Sandbox Code Playgroud)
它正在解决我所有常见的情况.当然有边境的没有被覆盖但是...... ciao :-)
经过大量研究和讨论后,很明显没有一种灵丹妙药可以在所有情况下发挥作用,至少在当前版本的 Git 中是这样。
这就是为什么我编写了几个添加tail
分支概念的补丁。每次创建分支时,也会创建一个指向原始点的指针,即 ref tail
。每次分支变基时,该引用都会更新。
要找出 devel 分支的分支点,你所要做的就是使用devel@{tail}
,就是这样。
https://github.com/felipec/git/commits/fc/tail
一个简单的方法git log --graph
就是使用选项来更容易地看到分支点--first-parent
。
$ 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
Run Code Online (Sandbox Code Playgroud)
现在添加--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
Run Code Online (Sandbox Code Playgroud)
这样更容易!
请注意,如果 repo 有很多分支,您将要指定要比较的 2 个分支,而不是使用--all
:
$ git log --decorate --oneline --graph --first-parent master origin/topic
Run Code Online (Sandbox Code Playgroud)