简体   繁体   English

如何在分支创建点或之后列出包含给定提交的分支

[英]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,它将列出masterbranch1branch2因为它们都包含它。 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不包含AB ,因为分支是在每个提交之后创建的)。

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. 到目前为止,您有几个很好的答案,但我认为值得指出的是,给定您的问题表达式,该命令必须为提交BC打印多个分支名称。

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 Cbranch1master的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 . 后来,当我们提交AO ,我们会发现H就像C :它在两个不同的reflog中,对于masterbranch2 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 fetchgit 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM