从中间合并获取合并分支分支的提交 [英] Get commit where merged branch forked from (with intermediate merge)
问题描述
cd / tmp&& amp;& mkdir fit&& cd fit
git init
touch m1&& git add m1&& git commit -mmaster 1
touch m2&& git add m2&& git commit -mmaster 2
git checkout -b develop
touch d1&& git add d1&& git commit -mdevelop 1
git checkout master
touch m3&& git add m3&& git commit -mmaster 3
git checkout develop
git merge master --no-edit
touch d2&& git add d2&& git commit -mdevelop 2
touch d3&& git add d3&& git commit -mdevelop 3
git checkout master
git merge develop --no-edit
touch m4&& git add m4&& git commit -mmaster 4
git reflog expire --expire = now --all&& git gc --prune = now --aggressive
git最老的祖先master开发
$ p
git最老的祖先开发master
$ b结果也是无用的。
但是
tig
和git log --graph
仍然能够看到开发1
是开发
分支,并且此分支从master 2
>在master
中提交。 p>
是否可以检索
使用当前的git控制台工具控制2
?解决方案
git --no-pager show -s --format =%B $(git rev-parse develop)
更简单:
git --no-pager显示-s --format =%B develop
或者:
git --no-pager log --no-walk --format =%B develop
(
show -s
和log --no-walk
是几乎相同的东西;这里的关键项目是删除不必要的git rev-parse
)。
但是我无法在
开发
分支中检索第一次提交
该图像中的第一个提交是
master 1
或27ee6b8
将随着提交的时间而变化)。这也是第一次提交分支master
。
这里的问题是分支没有起点。在某种意义上,分支是结构 - 图片片段 - 通过从结束点开始并回溯到开始到达。这意味着一些提交在许多分支上;通常, root 提交,您在存储库中进行的第一次提交,位于每个分支上(尽管在具有多个根的存储库中,某些根可能不在某些分支上)。
$ b一个分支 name 一般来说有一些例外,就是该分支上的提示提交的同义词,这就是为什么你不需要明确的
git rev-parse
。然而,分支名称的关键特征是它随着时间的推移移动,因此它总是命名分支的提示提交。
另请参阅我们的意思是分支?
如果您希望标记某个特定的提交,为了稍后记住它,通常的工具是Git标记。标签非常像分支名称,因为它标识了一个特定的提交。然而,与分支名称不同,标签永远不会移动,而Git不会自动移动它。
git reflog expire --expire = now --all
Reflogs的存在是为了能够观察参考文献的移动(随着时间的推移)。像开发
这样的分支名称的reflog默认保留30或90天, 1 <开发的哈希ID 用于识别。通过使他们失效,你已经消除了及时回顾并查看 develop @ 1
,develop @ 2
等等。如果您保留了它们,您可以查找存在的最旧的develop
。 可能是它出生的时候,你可以经常说:
05d0c47 master @ {37 }:clone:from ...
(表示
master
出生在这一点)。
不幸的是,reflogs do 过期了,所以这不是完全可靠的。该标签是可靠的,但可能很烦人,因为
git log
会修饰其标签提交。如果有一个过程用于查找有趣的提交,那么可以使用它。在这种情况下,就是这样一个过程:你想要提交已经或者已经是合并的基础。
要找到合并基础,找到合并本身,然后找到它的父母:
m = 11c63bc#这是合并
p1 = $(git rev-parse $ {m} ^ 1)
p2 = $(git rev-parse $ {m} ^ 2)
现在
$ p1
和$ p2
是此合并的两个父母。 (一个合并可以有两个以上的父母,但大多数合并只有两个)。这两个分支最后合并的共同点是两个父母的合并基础:
git merge-base --all $ p1 $ p2
由于只有一个合并基础,因此只打印一个提交哈希。如果有几个,它会打印所有的,因为我们使用
- 所有
。如果省略- all
,我们会得到一个在(显然)随机选择的实体(实际选择取决于用于查找合并基的算法)。
与以前一样,不需要很多临时变量 - 我们可以这样做:
mbases = $(git merge-base --all $ {m} ^ 1 $ {m} ^ 2)
自
> git merge-base
采用与git rev-parse
相同的提交说明符语法:^ 1
和^ 2
后缀的工作方式与此相同(并且在大多数Git命令中的确如此) p>
1 到期时间是可配置的。缺省情况下,30天的更短时间用于无法从引用的当前值到达的散列ID;更长的90天默认值是针对可从当前参考值获得的哈希ID。
Lets use the latest available git 2.16.2 and tig 2.3.3.
cd /tmp && mkdir fit && cd fit git init touch m1 && git add m1 && git commit -m "master 1" touch m2 && git add m2 && git commit -m "master 2" git checkout -b develop touch d1 && git add d1 && git commit -m "develop 1" git checkout master touch m3 && git add m3 && git commit -m "master 3" git checkout develop git merge master --no-edit touch d2 && git add d2 && git commit -m "develop 2" touch d3 && git add d3 && git commit -m "develop 3" git checkout master git merge develop --no-edit touch m4 && git add m4 && git commit -m "master 4" git reflog expire --expire=now --all && git gc --prune=now --aggressive
It is so easy to retrieve the last commit in
develop
branch:git --no-pager show -s --format=%B $(git rev-parse develop)
develop 3
But I couldn't retrieve the first commit in
develop
branch. So I couldn't find the commit where branch forked from.git merge-base --fork-point develop git rev-list develop..master git rev-list develop master git rev-list master develop git rev-list ^develop master
Results are useless.
I've found a solution for question How to get commit where merged branch forked from
git oldest-ancestor master develop git oldest-ancestor develop master
Results are useless too.
But
tig
andgit log --graph
are still able to see thatdevelop 1
was the first commit of thedevelop
branch and this branch were forked frommaster 2
commit inmaster
.Is it possible to retrieve
master 2
with current git console tools?解决方案git --no-pager show -s --format=%B $(git rev-parse develop)
Even simpler:
git --no-pager show -s --format=%B develop
or:
git --no-pager log --no-walk --format=%B develop
(
show -s
andlog --no-walk
are nearly the same thing; the key item here is to drop the unnecessarygit rev-parse
).But I couldn't retrieve the first commit in
develop
branchThe first commit in that branch is
master 1
, or27ee6b8
in your image (the hash ID will vary with the time that the commit is made). This is also the first commit in branchmaster
.The problem here is that branches do not have "starting points". Branches are, in one sense, the structure—the graph fragment—that one reaches by starting at the ending point and working back to the beginning. This means that some commits are on many branches; typically, the root commit, the first commit you make in a repository, is on every branch (though in a repository with multiple roots, some roots may not be on some branches).
A branch name is, in general—there are some exceptions—synonymous with the tip commit on that branch, which is why you don't need an explicit
git rev-parse
. The key feature of a branch name, however, is that it moves over time, so that it always names the tip commit of the branch.See also What exactly do we mean by "branch"?
If you wish to mark some particular commit, in order to remember it later, the usual tool for this is a Git tag. A tag is very much like a branch name, in that it identifies one specific commit. Unlike a branch name, however, a tag is never supposed to move, and Git won't move it automatically.
git reflog expire --expire=now --all
Reflogs exist specifically to be able to observe the movement (over time) of references. The reflog for a branch name like
develop
retains, for 30 or 90 days by default,1 the hash IDs thatdevelop
used to identify. By expiring them, you've removed your ability to go back in time and look atdevelop@1
,develop@2
, and so on. If you had retained them, you could look for the oldestdevelop
that exists. That might be when it was born, and you can often tell:05d0c47 master@{37}: clone: from ...
(indicating that
master
was born at this point).Unfortunately, reflogs do expire, so this is not completely reliable. The tag is reliable, but may be annoying since
git log
will decorate commits with their tags. If there's a procedure for finding the interesting commit, you can use that. In this case, there is such a procedure: you want the commit(s) that was or were the merge base(s) of the merge.To find the merge base, find the merge itself, then find its parents:
m=11c63bc # this is the merge p1=$(git rev-parse ${m}^1) p2=$(git rev-parse ${m}^2)
Now
$p1
and$p2
are the two parents of this merge. (A merge can have more than two parents, but most merges have only two.) The common point where these two branches were last merged is the merge base of the two parents:git merge-base --all $p1 $p2
Since there is only one merge base, this prints just the one commit hash. If there were several, it would print all of them because we used
--all
. Leaving out--all
, we would get one chosen at (apparently) random (the actual one chosen depends on the algorithm used to find the merge bases).As before, one does not need a lot of temporary variables—we could do:
mbases=$(git merge-base --all ${m}^1 ${m}^2)
since
git merge-base
takes the same commit-specifier syntax asgit rev-parse
: the^1
and^2
suffixes work the same there (and indeed work the same in most Git commands).
1The expiration times are configurable. The shorter time, 30 days by default, is for hash IDs that are not reachable from the current value of the reference; the longer 90-day default is for hash IDs that are reachable from the current value of the reference.
这篇关于从中间合并获取合并分支分支的提交的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!