我正在寻找特定分支的提交。我的树看起来如下:
feature/X C--E--F
/ \
master -A--B--D---G--H--I--J->
如何获得提交C,E和F? 我试过的是:
git rev-list feature/X ^master
但这没有提交。我假设在特殊情况下问题是feature / X与master的后合并。这就是为什么提交C,E和F也可以从主人那里访问的原因,不是吗?那么 - 如何处理这种情况,任何想法?
此致 托马斯
答案 0 :(得分:1)
一般来说,一旦合并,提交来自哪个分支的详细信息就会丢失。您所拥有的只是提交由当前分支覆盖。
但是,我可以找到一种这样的方式。
首先,您找到合并发生的位置。这可以使用git log --merges -1
来查找最近合并到主人(在您的情况下为H
)。我假设的featureX
分支就在F
后面。这个提交有2个父母。由于featureX
已合并为母版,因此您可以使用H^
获取目标分支父级。
然后,您可以找到H^
和F
之间的差异,git log H^..featureX
,它应该为您提供从featureX
可以访问的所有提交,并省略可从H^
到达的提交{1}}即。 C
,E
和F
。
如果完成,那么,我应该得到所有"更新X"提交。
首先,我们得到合并提交。
% git log --merges -1 --oneline
e8928b9 Merge branch 'X'
然后我们得到了有问题的日志。功能分支在我的仓库中称为X
。
% git log e8928\^..X --oneline
92a1f58 Updates x 10
f56306d Updates x 9
54d2253 Updates x 8
a8ba58b Updates x 7
10d08c5 Updates x 6
625d267 Updates x 5
96671d4 Updates x 4
5031498 Updates x 3
41770ea Updates x 2
442033b Updates x 1
这充其量只是hackish。我对寻找真正的解决方案非常感兴趣。
答案 1 :(得分:1)
有一点点hackish,但是工作单行:
git rev-list "$(git rev-list feature/X..master | tail -n 1)^"..feature/X
答案 2 :(得分:1)
重新绘制它可能会有所帮助:
feature/X C--E--F
/ \
master -A--B--D---G--H--I--J->
这样:
C--E--F <-- feature/X
/ \
<--A--B--D---G--H--I--J <-- master
原因是箭头确实指向后方,feature/X
指向分支feature/X
的提示,即提交F
和master
指向master
的提示提交(我在此假设为J
,但可能会有更多给出您的原始图纸)
正如您所指出的那样,feature/X ^master
(也可以拼写为master..feature/X
)失败,因为通过从提交开始,F
可以从master
访问提交master
其中J
点(H
)并向后工作。当我们点击提交master
时,我们同时向后通过父母一起工作,因此删除C--E--F
中可到达的所有提交的请求也会消除H
序列。
要阻止这种情况发生,我们必须从feature/X
之前的某个点开始删除提交,即第一次合并之前的一个点,将master
的提示带入G
。任何提交D
,B
或git rev-list feature/X ^$hash
都可以。也就是说,如果我们有这些提交的任何一个的哈希值,那么:
D
会做到这一点。
qzb's method找到提交^
,然后使用后缀J
来标识其第一个也是唯一的父级。它的工作原理是列出master
(F
的提示)可以从feature/X
(git rev-list
的提示)无法访问的每个提交。需要注意的是:D
可能会对提交进行排序,因此| tail -1
实际上可能不会列在最后,但D
会假定列表以提交git rev-list
&#39结尾; s hash。
因此,这取决于提交中存储的日期戳。如果它们是按顺序制作的(因此随着提交的及时向前移动,日期都会增加),这不是问题。通常他们这样做。但有时您可以在&#34;错误&#34;中添加提交。日期顺序,由于时钟设置不正确,或提交在不同的计算机上进行,不同意它是什么时间,或其他什么。
我们可以通过告诉--topo-order
使用--topo-order
来强制日期假设,这会强制它以 graph 顺序列出提交(使用图拓扑中的部分顺序) 。因此,使用此方法时,请添加H
。
Noufal Ibrahim的方法通过使用git log
来查找提交git rev-list
。使用git log
会更好一些,H=$(git rev-list --merges -1 master)
# H stands for Hash, and also for "commit H" :-)
采用与git log
相同的选项,但只打印哈希(这就是我们想要的):
HEAD
(请注意,我们必须指定图表行走的起点,而H
默认为从H
开始。获取提交H
的哈希值还不够,因为我们必须向后爬一个父级。由于G
有两个父母,我们必须小心地从F
爬到git merge
(而不是H
)。
幸运的是,每当我们与git merge feature/X
合并时,Git都会确保新合并提交的 first 父级是当前分支上的提交。也就是说,当我们通过运行master
进行提交master
时,我们在分支G
上,名称H
意味着提交G
。因此$H^1
的第一个父级是$H^
,因此G
或H=$(git rev-list --merges -1 master)
git rev-list feature/X ^${H}^
标识提交H
:
$H
^
周围的花括号在技术上并不是必需的,只是为了清晰起见:我们展开G
,然后在扩展后放置^
(以识别提交git rev-list
) ,以及扩展前的另一个$yes ^$no
(告诉$no..$yes
我们将其用作排除说明符。)
由于H=$(git rev-list --merges -1 master)
git rev-list ^${H}^..feature/X
可以写成H
,我们也可以将其写为:
tail -1
这个方法效率更高一些(我们只列举一个提交--topo-order
,而不是使用--topo-order
来获取某个潜在长链的最后一次提交)并且不受日期顺序的影响问题(但我们在上面看到了如何修复H
)。
顺便说一句,在查找提交A
时,这也应该使用H
,原因相同:我们不希望Git排序并进行其他合并({{1例如,在feature/X
前面。
qzb注意到其中一条:F
指向提交 o--o---o--o--o <-- feature
/ \ / \
...--o--o--o---o--o--o---o <-- master
,如果过去有更多合并,我们不一定&#34;知道在哪里停止&#34;。那就是:
feature
通过以这种特殊的方式绘制这个特定的图表,我们明确了所有提交沿着&#34; top line&#34;是那些在feature
上完成的,master
被合并到master
两次,而feature
被合并回master
一次。 (顺便提一下,这种&#34;交叉合并&#34;会让你陷入困境。它不是错误的,但一般来说你应该小心将A合并到B和B中A.在某些情况下,这会为合并产生多个合并基础,这可能很棘手。)但Git并不清楚,还有其他方法来绘制图形,这也会使我们自己的眼睛模糊不清。 (此外,如果你允许&#34;快进&#34;合并(而不是非快进,实际合并提交,合并),一般来说,解开分支历史是不可能的。再次,它不是错误,你只需要准备好处理它。)
如果H
过去提交 C--E--F <-- feature/X
/ \
<--A--B--D---G--H--I--J <-- master
/
<-o--o--o <-- feature/Y
上的合并,则两种方法都会出现更重要的问题。也就是说,假设到目前为止我们绘制的字母图仍然有点误导,事实上它应该是这样的:
H=$(git rev-list --topo-order --merges -1 master)
现在,如果我们这样做:
$H
我们最终会设置I
以指向提交H
,而不是提交master
。原因很简单:我们要求从I
开始并向后工作的最新(拓扑)提交,这也是合并提交。那个提交I^
。但H
提交H
,排除git rev-list
会使后续C--E--F
排除提交D
。
这种做法似乎已经毁灭了;我们可以回到定位提交$(git rev-list feature/X..master | tail -n 1)
吗?不,因为qzb的诀窍:
I
当Git按下feature/Y
的第二个父级,即通过--topo-order
时,停止工作,并开始列出所有那些提交。如果没有--topo-order
,我们将获得最早的提交。对于I^1
,我们仍然没有被告知首先处理哪个链(I^2
vs A
)。如果该链在提交A
或更早的时候连接回来,我们可能会获得提交D
的哈希 - 或更早,而不是提交I
的哈希。
我们可以通过注明引入feature/Y
的其他合并feature/Y
并排除F
来解决这个问题,以便Git 不在该链上竞争。但这开始变得复杂。那么,我们真正需要的不是最近的合并,而是引入提交H
的合并(即&#34;找到我提交--ancestry-path
&#34;)。有办法实现吗?事实证明,有。我们想要的是--ancestry-path
。
feature/X
选项删除了不是被排除提交的后代的提交。由于master
已合并到H
,我们肯定知道在F
F
的后代之后存在一些提交(实际上F
) } -ie,master
是其父母之一 - 也是git rev-list --ancestry-path --topo-order ^feature/X master
的祖先。所以:
H
告诉Git列出提交I
,J
和I
,以及其他提交。也就是说,我们不会在合并tail -1
引入的其他提交中竞争:这些提交将被修剪。
如果我们丢弃除最后一次提交之外的所有提交(再次使用--merges
),并选择使用tail
加快某些操作,以便在使用H
之前放弃任何非合并,即使I
或J
是合并,我们也会找到提交H=$(git rev-list --ancestry-path --topo-order \
--merges ^feature/X master | tail -1)
git rev-list ^$H^..feature/X
:
--ancestry-path
这是两种方法的混合:我们使用H
查找从tail -1
开始的提交,使用H
删除所有但 commit {{ 1}},然后使用^$H^
排除提交 - G
- 及更早。