[英]How to list branches that contain a given commit at or after branch creation point
I realize that this question looks a lot like 1419623 , but it's different. 我意识到这个问题看起来很像1419623 ,但它有所不同。
Imagine the following tree: 想象一下以下的树:
master
|
N
M
H-J-K-L-O -- branch2
E
C-D-F-G-I -- branch1
B
A
I need a method to tell that commits C, D, F, G, and I all belong to branch1. 我需要一个方法来告诉提交C,D,F,G和我都属于branch1。 For commits D,F,G,I
git branch --contains
works just great. 对于提交D,F,G,我
git branch --contains
工作得很好。 However, for commit C it will list master
, branch1
, and branch2
because they all really do include it. 但是,对于提交C,它将列出
master
, branch1
和branch2
因为它们都包含它。 Is there any method to only list the branches that have the given commit at or after the branch creation? 是否有任何方法只在分支创建时或之后列出具有给定提交的分支?
That is, I need some command that would behave like this: 也就是说,我需要一些行为如下的命令:
$ git some-command B
master
$ git some-command C
branch1
master
$ git some-command F
branch1
$ git some-command H
branch2
master
$ git some-command L
branch2
$ git some-command M
master
Is there any? 有没有?
There is no way to do this. 没有办法做到这一点。
git
does not keep track of branch creation times. git
不跟踪分支创建时间。 At a physical level of what's stored, there is no difference in how C
relates to the three branches. 在存储的物理层面上,
C
与三个分支的关系没有区别。
In Git, branches do not store any metadata about when or how they were created, or about who created them. 在Git中,分支机构不存储关于何时或如何创建它们的任何元数据,或者关于谁创建它们的元数据。 They are just pointers to a commit.
它们只是提交的指针。
And it actually wouldn't make sense to store that information. 实际上存储这些信息是没有意义的。 As an example, here's a typical workflow:
例如,这是一个典型的工作流程:
git checkout master
// Make changes and commit A (on master)
git checkout -b my-branch
// Make changes and commit B (on my-branch)
git checkout master
// Make changes and commit C (on master)
...which generates a commit graph like: ...生成一个提交图,如:
* C master
|
| * B my-branch
|/
* A
...but I can't tell you how many times I've accidentally committed on master
instead of a branch. ......但是我不能告诉你我多少次意外地犯了
master
而不是分支。 A good way of fixing that is to checkout a new branch where you are, and then reset master
back to where it was before this commit. 解决这个问题的一个好方法是检查你所在的新分支,然后将
master
重置回到提交之前的位置。 Something like: 就像是:
git checkout master
// Make changes and commit A (on master)
// Make changes and commit B (on master)
// Oops... I meant to commit B on a branch
git checkout -b my-branch
git checkout master
git reset master^
// Make changes and commit C (on master)
Both of these generate the same commit graph, but would have different interpretations of which commits "belong" to each branch (specifically, my-branch
would not contain either A
or B
, since the branch was created after each of those commits). 这两个都生成相同的提交图,但是对于哪个提交“属于”每个分支会有不同的解释(具体来说,
my-branch
不包含A
或B
,因为分支是在每个提交之后创建的)。
You have a couple of good answers so far, but I think it's worth pointing out that given your question formulation, the command would have to print multiple branch names for commits B
and C
for instance. 到目前为止,您有几个很好的答案,但我认为值得指出的是,给定您的问题表达式,该命令必须为提交
B
和C
打印多个分支名称。
If you are willing to live within the limitations of Git reflogs (essentially, 90 days by default), there is an alternative forumlation that I think will do what you want. 如果你愿意生活在Git reflogs的限制范围内(基本上是默认的90天),我认为可以做你想做的另一种论坛。
Let me re-draw your graph the way I prefer, which is left (earlier commits) to right (later commits) with names at the right hand edges: 让我按照我喜欢的方式重新绘制你的图形,这是左边(早期提交)到右边(后面的提交),右边是名字:
D--F--G--I <-- branch1
/
A--B--C--E--H--M--N <-- master
\
J--K--L--O <-- branch2
When you created branch1
, you probably did so like this: 当你创建
branch1
,你可能 branch1
:
$ git checkout master # which resolves to commit `C`, because ...
... because at this point the graph looks like this: ...因为此时图形看起来像这样:
A--B--C <-- master (HEAD)
Now you run: 现在你运行:
$ git checkout -b branch1
which makes the graph look like this: 这使得图形看起来像这样:
A--B--C <-- master, branch1 (HEAD)
That is, now two branch-names exist, both of which identify commit C
. 也就是说,现在存在两个分支名称, 两者都标识提交
C
The name HEAD
is currently attached to branch1
, due to the use of git checkout -b
. 由于使用了
git checkout -b
,因此名称HEAD
当前附加到branch1
。
At this point you: 此时你:
... do some work ...
$ git commit -m message # which creates commit `D`
giving: 赠送:
D <-- branch1 (HEAD)
/
A--B--C <-- master
As you make more commits, they are added wherever HEAD
is attached, so this causes branch1
to accumulate commits. 当您进行更多提交时,它们会被添加到
HEAD
附加的任何位置,因此这会导致branch1
累积提交。
Your question includes this phrasing: 你的问题包括这句话:
Is there any method to only list the branches that have the given commit at or after the branch creation?
是否有任何方法只在分支创建时或之后列出具有给定提交的分支?
which falls afoul of two problems: branches don't really have a "creation", and commits have—or contain , really— every commit that is reachable from their tip backwards. 这与两个问题相悖:分支实际上没有“创造”,并且提交已经或者包含 ,实际上 - 每个提交都可以从他们的提示向后移动。
What we could instead ask is: For each commit, which branch reflogs have entries that refer directly to that commit? 我们可以提出的问题是: 对于每次提交,哪些分支reflog具有直接引用该提交的条目? Assuming no reflog value has expired and been removed, we will find that, at this point—where
branch1
exists and points to commit D
—commit D
is in only branch1
's reflog, commit C
is in both branch1
and master
's reflog, commit B
is only in master
's reflog, and commit A
is only in master
's reflog. 假设没有reflog值已经过期并被删除,我们会发现,此时
branch1
存在并且指向commit D
-commit D
只在branch1
的reflog中,commit C
在branch1
和master
的reflog中, commit B
只在master
的reflog中,而commit A
只在master
的reflog中。
Later, when we have commits A
through O
inclusive, we will find that H
is like C
: it's in two different reflogs, for master
and branch2
. 后来,当我们提交
A
到O
,我们会发现H
就像C
:它在两个不同的reflog中,对于master
和branch2
。 The remaining commits are in only one reflog. 剩下的提交只在一个reflog中。
Hence we would start by asking the reflog-based question. 因此,我们首先要问基于reflog的问题。 Then, for commits that appear in multiple reflogs, we would declare that such a commit is "more in tune with" whichever reflog was created later .
然后,对于出现在多个reflog中的提交,我们将声明这样的提交“更符合” 以后创建的任何reflog。 That's because the reflog entry that has
C
as a commit on master
was created at the time commit C
itself was created, so that reflog entry has an earlier date; 这是因为在创建提交
C
本身时创建了C
作为master
提交的reflog条目,因此reflog条目具有更早的日期; but when branch1
was created, pointing to commit C
, that, in effect, "takes" commit C
into branch1
. 但是当创建
branch1
时,指向commit C
,实际上,“将”提交C
带入” branch1
。
Note that this formulation also has a flaw—besides, of course, the one that occurs when the reflog entry expires. 请注意,此配方还有一个缺陷 - 当然,还有在reflog条目到期时发生的缺陷。 Suppose we now run:
假设我们现在运行:
git checkout -b branch3 <hash-of-C>
and make a new commit on branch3
? 并在
branch3
上进行新的提交? Our graph is now: 我们的图表现在是:
,--P <-- branch3 (HEAD)
/
| D--F--G--I <-- branch1
|/
A--B--C--E--H--M--N <-- master
\
J--K--L--O <-- branch2
Commit C
is now in three reflogs: one for master
, one for branch1
, and one for branch3
. Commit
C
现在有三个 reflog:一个用于master
,一个用于branch1
,一个用于branch3
。 (The full reflog for branch3
consists of "commit P
, then commit C
".) The latest is branch3
: by attaching C
as the base of branch3
we've effectively "stolen" commit C
for branch3
. (
branch3
的完整reflog包含“commit P
,then commit C
”。) 最新的是branch3
:通过附加C
作为branch3
的基础,我们有效地“窃取”了branch3
提交C
If you dislike this, the obvious alternative is to assign commit C
to the earliest reflog that contains it, ie, to master
. 如果您不喜欢这个,那么显而易见的替代方法是将提交
C
分配给包含它的最早的 reflog,即master
。 But now: 但现在:
git who-owns <hash-of-C>
says master
, not branch1
, which is not what you asked for. master
,而不是branch1
,这不是你要求的。 It's easy enough to do either one though; 尽管如此,做任何一个都很容易; here's the untested pseudocode framework to get you started:
这是未经测试的伪代码框架,可以帮助您入门:
#! /bin/sh
# git-who-owns: pick a branch to "own" a commit
. git-sh-setup
case $# in
0) die "usage: git who-owns <commit> ...";;
esac
who-owns() {
local b hash
hash=$(git rev-parse $1^{commit}) || return 1
for b in $(git for-each-ref --format='%(refname:short) refs/heads); do
git reflog --no-abbrev $b | awk '$1 == "$hash"'
done
}
status=0
for arg do who-owns "$arg" || status=1; done
exit $status
If this script is in your $PATH
as git-who-owns
, running git who-owns <commit-specifier>
will print out all the matching reflogs. 如果这个脚本在你的
$PATH
作为git-who-owns
,运行git who-owns <commit-specifier>
将打印出所有匹配的reflog。
There's another bigger flaw: if a commit could belong to one or more branches, but was brought in via an en-masse operations like git fetch
followed by git merge
, its hash ID will not be in any reflog. 还有一个更大的缺陷:如果一个提交可能属于一个或多个分支,但是通过
git fetch
和git merge
类的集合操作引入,它的哈希ID将不会出现在任何reflog中。 You can hybridize the above along with --contains
operations to assign such "in-between" commits to branches. 您可以将上述内容与
--contains
操作混合,以将这种“中间”提交分配给分支。
(The idea in general is not terribly workable, though. I would not spend a lot of time on this. Instead, drop a marker, such as a tag, on a boundary commit when you want to generate bounded lists of commits. Then git rev-list branch-name ^marker
will get you the commit IDs you want. This is more conventionally spelled git rev-list marker..branch-name
, eg, git log orgin/master..master
.) (一般来说,这个想法并不是非常可行。我不会花很多时间在这上面。相反,当你想要生成有界的提交列表时,在边界提交上删除标记,例如标记。然后
git rev-list branch-name ^marker
将为你提供你想要的提交ID。这是更常规拼写的git rev-list marker..branch-name
,例如git log orgin/master..master
。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.