列出已合并的分支的 Git 命令

4

有没有一种方法可以列出已合并到当前工作树中的分支?以下内容接近:

git branch -r --merged

然而,这也包括空的分支。例如,在当前分支最新提交之前的某个时间点,我运行了以下命令:
git checkout -b empty_branch
git push -u origin empty_branch

现在,第一个命令在结果列表中包括empty_branch。我发现有一个相关的问题如何在git中找到空分支,但是被接受的答案对没有提交的分支无效。有没有办法在git中检测没有提交的分支,或者从git branch --merged的结果中过滤出这样的分支?

在 Git 中,不存在“空分支”这样的东西。您创建的新分支具有与当前分支相同的所有提交。对于您链接到的问题,目标是查找其尖端提交不包含任何文件(空)的分支。 - torek
@torek,您有提议重新表述的想法吗?以更清晰地识别符合上述条件的分支?即使“空分支”不是正确的git术语,我仍然需要回答如何获取这些分支列表的问题。 - Jeff G
做一个 git log 对你很有帮助:git log --color --graph --pretty=format:'\''%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\'' --abbrev-commit - bagsmode
1个回答

2
你在 Git 方面有一个错误的假设。虽然 Git 很奇怪,但这个假设并不是不合理的,但它会让你的问题有些偏差。
在 Git 中,分支名称一开始并不像大多数人认为的那样有很大的意义。分支名称只是保存了某些现有提交的哈希 ID。还有一种被人们称为“分支”的底层实体,它由一些或所有的提交组成,这些提交都可以从由分支名称定义的最新提交中到达。关于这个问题,可以参考这个链接:What exactly do we mean by "branch"?Think Like (a) Git,但让我们来尝试进行快速总结:
  • What Git is really all about is commits. Each commit is identified by its own unique hash ID. No other commit can ever have this hash ID. The contents of a commit include a snapshot—made from the index, rather than from the work-tree, but I will try not to get into all those hairy details here—and include the name and email address of the author of the commit, the same for the committer (usually the same person), and two date-and-time-stamps: one for author, one for committer. They also include the log message the committer supplied at the time he/she/they/pronoun-of-choice made the commit. Perhaps most importantly, the contents of a commit include the raw hash IDs of any commits that should be considered immediate predecessors of that commit.

  • In other words, each commit has some set of parent hash IDs, most commonly just one hash ID. These parent IDs make commits form backwards-looking chains: from a last commit, we can go back to a previous commit. From there, we can go back one more step, and so on. So if a branch name like master holds the hash ID of the last commit we should consider to be part of the branch, the remaining commits are that commit's parent(s), the parent(s) of the parent(s), and so on. If the hash ID of the master tip commit is H and its parent is G and G's parent is F and so on, we have:

    ... <-F <-G <-H   <-- master
    

    and that's what a branch is: it's either the name, or the series of commits ending at H, or both: we tend to have to guess what someone means when they say "the master branch".

git branch --merged 命令的作用如下:

  • 查找当前分支(HEAD)的哈希 ID。
  • 对于所有分支名称 B,判断以下条件:1
    • 标识为 B 的提交是否是标识为 HEAD 的提交的祖先?如果是,则打印名称 B。无论如何,继续下一个名称。

因此,如果图形的一部分如下所示:

             I--J   <-- feature2
            /
...--F--G--H   <-- master (HEAD)
      \
       K--L   <-- feature1
git branch --merged命令将测试的两个提交是JL,相对于当前提交H。如果JH的祖先(但它不是),则会打印feature2。如果LH的祖先(但它不是),则会打印feature1。当然,HH的祖先,所以这会打印master(带有前缀*表示它是当前分支)。
如果有一个指向FGH或任何“在”F之前的提交的第四个名称,则git branch --merged也会打印出该名称。
我认为你想要打印所有指向任何真正的祖先提交的名称,而不是指向提交H的任何名称。最简单的方法可能是让git branch --merged打印所有内容,然后从列表中删除哈希ID与HEAD相匹配的任何名称。
要删除此类名称,请对每个名称使用git rev-parse。使用git rev-parse HEAD查找当前分支的哈希ID,即上图中H的实际哈希ID。然后,再次对来自git branch --merged的每个名称使用git rev-parse,如果结果与第一个git rev-parse相同,则丢弃该名称。否则,保留该名称。
(您将需要编写一些代码。如果git for-each-ref可以执行--not并组合一些布尔表达式,则可能可以使用该命令完成,但它不执行--not和组合。) 1Git使用的is-ancestor测试允许相等性,即它是≤而不是<(或更精确地说,是≼而不是≺)。git merge --is-ancestor也是如此。

这已经足够接近标记为答案了。我实际上想排除任何指向H、G、F的分支...一旦L合并到H中,feature1仍然指向L,因此我们知道实际工作已经完成。如果feature1指向H、G、F等,则可以假定尚未对已合并到主干的该分支进行任何工作。这有意义吗,还是仍然令人困惑?我想出了一种直接在循环中使用rev-parse获取符合此条件的分支列表的方法,但目前没有我的代码。当我能访问时,我会在这里发布一个答案。 - Jeff G
是的,所有这些看起来都很有道理。 - torek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接