获取最终分支列表(也就是最终节点)。

7

如何获取仅为最终分支的分支列表(即不是任何其他提交的父级) - 其他单词是提交树的末尾节点的分支。

我期望git branch命令中有像--final或--tips这样的选项,但只有--merged、--no-merged和--contains选项。


由于Git保存提交的父级而不是其子级,因此在历史记录图中查找叶节点(您所描述的“提示”)并不那么简单。编写一个脚本来查找您想要了解的内容相对容易,但需要遍历存储库中的所有提交。 - Nevik Rehnel
我想找到可以由我删除的分支。例如,如果我删除非叶节点提示中的分支,则会清理对我无用的分支。任何非最终分支都可以恢复,因为它们的提交将在树内部。 - Perlover
4个回答

6

更新以处理任意数量的分支,任何类型的提示:

git for-each-ref --format='%(objectname)^{commit}' \
| git cat-file --batch-check='%(objectname)^!' \
| grep -v missing \
| git log --oneline --stdin

这个还是比较容易调整的,你可以通过列出那些需要的引用来限制你所寻找的提示种类,在 for-each-ref 中可以添加进一步的排除标签提示等。


在 Git 2.36 中,当我运行 git show -s --oneline --decorate $( git show-branch --independent -a ) 命令时,我会得到很多警告消息,显示为 "warning: ignoring origin/my/branch/name; cannot handle more than 26 refs"。有任何想法吗?(更新:git show-branch 的手册上说:"一次不能显示超过29个分支和提交")- 这就解释了这个问题,但是为什么会出现 26 和 29 的差异呢? - Dai
@Dai yup。已修复,现在可以处理任何情况了。好问题。方便使用的命令之所以存在,是因为它们对于构建它们的具体情况来说是方便的。你能够将它们推广到多远总是有点武断的。核心命令,也就是所谓的“管道”,你可以用任何方式来操作,但有时你会感到油腻和恼怒 :-)。 - jthill

5

已经有一些有用的答案,但没有给出所要求的内容:分支列表(对我来说这意味着分支名称,而不是提交ID)。

获取最新分支名称列表

{
        echo "** Below is list of tip branches a.k.a. final nodes."
        echo "** These aren't reachable from any other branches."
        echo "** Removing them is potentially DANGEROUS: it would make some commit unreachable."
        TIPS=$( git rev-list --branches --children | grep -v ' ' | sort )
        { while read branchname commitid message
                do grep $commitid < <(echo $TIPS) -q && echo $branchname
                done
        } < <(git branch -v --no-abbrev | sed 's/\*//' )
        echo "** end of list"
}

结果看起来像这样:

** Below is list of tip branches a.k.a. final nodes.
** These aren't reachable from any other branches.
** Removing them is potentially DANGEROUS: it would make some commit unreachable.
feature-workingonit
dev
** end of list

获取非尖端分支名称列表

在整理事务时,您可能想查看非尖端分支。以下是获取它们列表的方法。唯一的变化是将&&替换为||

{
        echo "** Below is list of non-tip branches a.k.a. inner nodes."
        echo "** These are reachable from other branches."
        echo "** If you remove them, all commits would stay reachable, but be careful anyway."
        TIPS=$( git rev-list --branches --children | grep -v ' ' | sort )
        { while read branchname commitid message
                do grep $commitid < <(echo $TIPS) -q || echo $branchname
                done
        } < <(git branch -v --no-abbrev | sed 's/\*//' )
        echo "** end of list"
}

结果如下所示:
** Below is list of non-tip branches a.k.a. inner nodes.
** These are reachable from other branches.
** If you remove them, all commits would stay reachable, but be careful anyway.
feature-alreadymerged
master
** end of list

获取没有提交信息的分支名称列表,包括提交ID和消息

您可以根据需要进行装饰,例如通过更改:

echo $branchname

到达

echo -e "$branchname\t$commitid\t$message"

结果看起来像这样:
** Below is list of non-tip branches a.k.a. inner nodes.
** These are reachable from other branches.
** If you remove them, all commits would stay reachable, but be careful anyway.
feature-alreadymerged   426ac5186841876163970afdcc5774d46641abc4    Cool feature foo, ref task #1234
master  39148238924687239872eea425c0e837fafdcfd9    Some commit
** end of list

您可以在喜欢的工具中查看这些分支,以确定是否有必要将它们删除。


3

由于分支只是在工作目录中添加新提交时移动的标签 - 所有分支都“根据定义”是树的叶节点。

但我想你正在寻找那些与另一个分支的叶提交没有共同祖先的分支。因此,您需要 CE,但不需要下面的 ABD

-A-o-B-o-C
      \
       D-o-E

如果情况确实如此,那么找到一份没有共同祖先的所有分支的列表就有可能了。例如,D和E共享D当前的叶提交的共同祖先。
这可能可以通过脚本完成,但我不知道是否有一个git命令可以为您做到这一点,适用于所有分支。但是,Git在过去以其(有时晦涩)的命令参数组合方面给我带来了惊喜。
我在听取了您的评论后进行了更多思考,我认为我更好地理解为什么您所要求的东西不能成为一个可能的(或有用的)命令。这是因为像“git merge --squash <branch>”这样的命令不会创建合并提交。创建的提交只是另一个常规提交--不含任何说明它来自另一个分支的内容。
例如,从以下开始:
-A-o-B-o-C
      \
       D-o-E

当以CHEAD时,命令git merge --squash E将把E中包含的所有更改放入索引中,准备提交。提交后树的结构将如下所示:

-A-o-B-o-o-C    <== notice branch `C` has moved forward one commit
      \
       D-o-E
C 提交现在包含了所有来自 D-o-E 的更改,没有说明它来自哪里。现在很可能不需要 D-o-E 了,但只有项目知识可以告诉你。

因为你要做的是一个手动过程,所以你需要理解。我想到了一些命令,可以帮助你确定是否可以删除分支。

这个 git log 命令产生的是一个图形,只显示修饰过的提交(分支、标签和 HEAD)。

git log --all --graph --oneline --simplify-by-decoration --decorate

运行此命令将为您提供一个视觉图形,使您可以快速查看“终点”分支的位置。提示:添加>> <filename>.txt以将图形转储到可用铅笔标记的文本文件中。
另外,一旦您确定了要保留的“终点”分支,请运行以下命令以查看从<branch>开始的祖先分支列表。这将列出已完全合并(具有合并提交)到<branch>中的分支。
git branch --merged <branch>

这可能不完全是您想要的,但正如我上面所解释的,我认为这是不可能的。希望这些命令能够帮助到您。


是的,我想在你的示例中找到 C 和 E 分支,而不是 A、B、D。例如,我有一个带有许多分支的存储库,我想要清理分支引用。 - Perlover

3

如果您获取所有引用的列表,您可以列出所有不是其他引用父级的引用:

refs=`git show-ref -s`
git log --oneline --decorate $refs --not `echo "$refs" | sed 's/$/^/'`

这是通过过滤掉所有的父提交来实现的。


我认为这不会起作用:并非所有的引用都在“refs”目录中有一个文件,其中一些引用在“packed-refs”文件中。 - svick
此外,由于某种原因,您的代码对我来说根本不起作用:它没有返回任何内容。 - svick
没有 git show-refs 但你的代码没有输出任何行 而你想用 s/$/^/ 实现什么功能呢? 例如: $ echo $refs 722aaacadeb64136d19dab91c7516682c2a8c796 3b242412889ca1101914c2834858ebdc9efa4839 refs中间有空格。我尝试了 s/ /\n/g,但这并不能帮助定义叶节点 :( - Perlover
这正是我在寻找的。还有一个问题留下了。如何验证所有这些引用是否在另一个仓库中,并且我可以安全地删除该仓库。有选项吗? - user1325696

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