在Git中列出每个分支及其最后一次修订的日期

193

我需要从我们的远程代码库中删除旧的且无人维护的分支。我正在尝试找到一种按其最后修改日期列出远程分支的方法,但我找不到。

有没有简便的方法以这种方式列出远程分支?


2
可能是重复的问题,参考如何按最近提交的顺序获取git分支列表? - Kristján
5
这里的答案质量远不如这个问题的Stack Overflow页面(https://dev59.com/wG435IYBdhLWcg3w1juV)上的回答。请参考那里的解答。 - Software Engineer
13个回答

207

commandlinefu提出了两个有趣的建议:

for k in $(git branch | perl -pe s/^..//); do echo -e $(git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k -- | head -n 1)\\t$k; done | sort -r

或者:

for k in $(git branch | sed s/^..//); do echo -e $(git log --color=always -1 --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k --)\\t"$k";done | sort

这是针对本地分支,使用Unix语法。使用git branch -r,您可以类似地显示远程分支:

for k in $(git branch -r | perl -pe 's/^..(.*?)( ->.*)?$/\1/'); do echo -e $(git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k -- | head -n 1)\\t$k; done | sort -r

迈克尔·福雷斯特(Michael Forrest)在评论中提到,zsh需要对sed表达式进行转义:

for k in git branch | perl -pe s\/\^\.\.\/\/; do echo -e git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k -- | head -n 1\\t$k; done | sort -r 

kontinuity添加了在评论中:

如果您想将其添加到您的zshrc中,则需要以下转义。

alias gbage='for k in $(git branch -r | perl -pe '\''s/^..(.*?)( ->.*)?$/\1/'\''); do echo -e $(git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k -- | head -n 1)\\t$k; done | sort -r'

在多行中:

alias gbage='for k in $(git branch -r | \
  perl -pe '\''s/^..(.*?)( ->.*)?$/\1/'\''); \
  do echo -e $(git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k -- | \
     head -n 1)\\t$k; done | sort -r'

注意:基于 git for-each-ref refs/headsn8transwer 更加简洁。而且速度更快
另请参见 "Name only option for git branch --list?"。
更一般地,tripleee提醒我们在评论中
  • 使用现代的 $(command substitution) 语法优先过时的反引号语法。
(我在2014年通过 "What is the difference between $(command) and `command` in shell programming?" 来说明这一点)

1
@hansen j:有趣,不是吗?它是在 Stack Overflow 公开发布几个月后推出的(http://codeinthehole.com/archives/16-Current-pet-project-Command-Line-Fu.html),并且在某种程度上受到了 SO 的启发。还可以参考 http://www.commandlinefu.com/commands/tagged/67/git 了解更多关于 git 命令行的技巧 ;) - VonC
运行这些代码时,为什么会出现“zsh: no matches found: s/^..//”? - Michael Forrest
哦,显然zsh解析了正则表达式。我通过转义一切使其工作... 对于git branch | perl -pe s\/\^\.\.\/\/中的每一个k,执行以下操作:echo -e git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k -- | head -n 1\t$k; 然后按照倒序排序。 - Michael Forrest
@MichaelForrest 很有趣。我已经将您的评论包含在答案中以增加可见性。 - VonC
1
@tripleee 谢谢。我已经编辑了答案,并包含了您的评论以增加可见性。 - VonC
显示剩余15条评论

183

这是我使用的:

git for-each-ref --sort='-committerdate:iso8601' --format=' %(committerdate:iso8601)%09%(refname)' refs/heads

这是输出:

2014-01-22 11:43:18 +0100       refs/heads/master
2014-01-22 11:43:18 +0100       refs/heads/a
2014-01-17 12:34:01 +0100       refs/heads/b
2014-01-14 15:58:33 +0100       refs/heads/maint
2013-12-11 14:20:06 +0100       refs/heads/d/e
2013-12-09 12:48:04 +0100       refs/heads/f

对于远程分支,只需使用"refs/remotes"而不是"refs/heads":

git for-each-ref --sort='-committerdate:iso8601' --format=' %(committerdate:iso8601)%09%(refname)' refs/remotes

n8tr的答案的基础上,如果您还对分支上的最后一位作者感兴趣,并且如果您有“列”工具可用,您可以使用以下方法:

git for-each-ref --sort='-committerdate:iso8601' --format='%(committerdate:relative)|%(refname:short)|%(committername)' refs/remotes/ | column -s '|' -t

这将给你:

21 minutes ago  refs/remotes/a        John Doe
6 hours ago     refs/remotes/b        Jane Doe
6 days ago      refs/remotes/master   John Doe

您可能希望在执行命令之前调用 git fetch --prune 以获取最新信息,或者添加带有特定颜色的格式化输出语句 %(color:<color>) 来显示某些字段。


6
运用了循环引用和格式选项,非常妙。加一!听起来比我自己回答中提到的命令要简单。 - VonC
4
稍作调整:------- 使用git for-each-ref --sort='-authordate:iso8601' --format=' %(authordate:relative)%09%(refname:short)' refs/heads,可以得到相对日期并消除refs/heads。 - n8tr
1
对于那些不太明显的人,我认为这只显示本地分支的信息。 - hBrent
@hBrent,你说得对,它并没有完全回答问题。我已经相应地编辑了我的答案。 - ocroquette
1
这将按authordate对分支进行排序和列出(似乎是分支最初创建的时间?)。如果您将authordate更改为committerdate,则将在每个分支中看到最近提交的日期。就像这样:git for-each-ref --sort='-committerdate:iso8601' --format=' %(committerdate:iso8601)%09%(refname)' refs/heads - Logan Besecker
显示剩余5条评论

40

Olivier Croquette 的基础上,我喜欢使用相对日期并缩短分支名称,就像这样:

git for-each-ref --sort='-authordate:iso8601' --format=' %(authordate:relative)%09%(refname:short)' refs/heads

这将为您提供输出:

21 minutes ago  nathan/a_recent_branch
6 hours ago        master
27 hours ago    nathan/some_other_branch
29 hours ago    branch_c
6 days ago      branch_d

我建议创建一个Bash文件来添加所有你最喜欢的别名,并将脚本分享给你的团队。这里是一个例子,只添加这一个别名:

#!/bin/sh

git config --global alias.branches "!echo ' ------------------------------------------------------------' && git for-each-ref --sort='-authordate:iso8601' --format=' %(authordate:relative)%09%(refname:short)' refs/heads && echo ' ------------------------------------------------------------'"

然后,您可以执行以下操作,以获得格式良好且已排序的本地分支列表:

git branches

21

补充一下 @VonC 的评论,为了方便起见,将您喜欢的解决方案添加到您的 ~/.gitconfig 别名列表中:

[alias]  
    branchdate = !git for-each-ref --sort='-authordate' --format='%(refname)%09%(authordate)' refs/heads | sed -e 's-refs/heads/--'

然后简单的命令 "git branchdate" 就会为您打印列表...

3
赞一个,展示了如何在 .gitconfig 文件中使用它!另外,我把格式字符串改成了:--format='%(authordate)%09%(objectname:short)%09%(refname)',它还会获取每个分支的短哈希。 - Noah Sussman
不错。我会在末尾添加“| tac”以便按相反顺序排序,这样最近接触的分支就能很快地显示出来。 - Ben
1
你不需要使用 | tac,只需使用 --sort='authordate' 替代 -authordate 即可。 - Kristján

5

在查看此问题后,我总结出以下内容:

for REF in $(git for-each-ref --sort=-committerdate --format="%(objectname)" \
    refs/remotes refs/heads)
do
    if [ "$PREV_REF" != "$REF" ]; then
        PREV_REF=$REF
        git log -n1 $REF --date=short \
            --pretty=format:"%C(auto)%ad %h%d %s %C(yellow)[%an]%C(reset)"
    fi
done
PREV_REF是用来去除重复的检查,如果有多个分支指向同一个提交,则使用该检查。(例如本地分支在远程分支中也存在。)
请注意,根据问题提出者的要求,git branch --mergedgit branch --no-merged对于识别可以轻松删除的分支非常有用。
[https://git-scm.com/docs/git-branch]

5

已排序的远程分支和每个分支的最后提交日期。

for branch in `git branch -r | grep -v HEAD`;do echo -e `git show --format="%ci %cr" $branch | head -n 1` \\t$branch; done | sort -r

2
感谢您回答有关远程的OP问题。 - arcseldon
1
有意义的答案! - daparic

2
我做了一个简单的别名,不确定这是否是确切要求,但它很简单。我之所以这样做是因为我想列出所有分支而不仅仅是我的本地分支,而上面的命令只能做到这一点。您可以将上述内容管道传递给“grep origin”以仅获取原始分支。这会列出所有分支以及最后修改日期,帮助我决定应该拉取哪个版本的最新版本。这会导致以下类型的显示:
Wed Feb 4 23:21:56 2019 +0230   8 days ago      refs/heads/foo
Tue Feb 3 12:18:04 2019 +0230   10 days ago     refs/heads/master
Mon Feb 9 12:19:33 2019 +0230   4 days ago      refs/heads/bar
Wed Feb 11 16:34:00 2019 +0230  2 days ago      refs/heads/xyz
Tue Feb 3 12:18:04 2019 +0230   10 days ago     refs/remotes/origin/HEAD
Mon Feb 9 12:19:33 2019 +0230   4 days ago      refs/remotes/origin/foo
Tue Feb 3 12:18:04 2019 +0230   10 days ago     refs/remotes/origin/master
Tue Feb 3 12:18:04 2019 +0230   10 days ago     refs/remotes/origin/bar
Tue Feb 3 12:18:04 2019 +0230   10 days ago     refs/remotes/origin/xyz

试一试,如果有帮助请告诉我,愉快地使用 Git 吧


简单明了,没有特殊的技巧。 - MrMas

1

我根据VonC的回答制作了两个版本。

我的第一个版本:

for k in `git branch -a | sed -e s/^..// -e 's/(detached from .*)/HEAD/'`; do echo -e `git log -1 --pretty=format:"%Cgreen%ci |%Cblue%cr |%Creset$k |%s" $k --`;done | sort | column -t -s "|"

这个命令处理本地和远程分支(-a),处理分离头状态(使用更长的sed命令,虽然解决方案有点粗糙——它只是用关键字HEAD替换了分离的分支信息),添加提交主题(%s),并通过格式字符串中的文字管道字符将结果放入列中,并将最终结果传递给column -t -s "|"。(您可以使用任何分隔符,只要它不在其余输出中出现即可。)
我的第二个变体非常hacky,但我真的想要像branch命令一样仍然具有“这是您当前所在的分支”的指示器。
CURRENT_BRANCH=0
for k in `git branch -a | sed -e 's/\*/CURRENT_BRANCH_MARKER/' -e 's/(detached from .*)/HEAD/'`
do
    if [ "$k" == 'CURRENT_BRANCH_MARKER' ]; then
        # Set flag, skip output
        CURRENT_BRANCH=1
    elif [ $CURRENT_BRANCH == 0 ]; then
        echo -e `git log -1 --pretty=format:"%Cgreen%ci |%Cblue%cr |%Creset$k |%s" $k --`
    else
        echo -e `git log -1 --pretty=format:"%Cgreen%ci |%Cblue%cr |%Creset* %Cgreen$k%Creset |%s" $k --`
        CURRENT_BRANCH=0
    fi
done | sort | column -t -s "|"

这将把标记当前分支的*转换为关键字,当循环体看到该关键字时,它会设置一个标志并不输出任何内容。该标志用于指示下一行应使用替代格式。就像我说的那样,这完全是hacky的,但它可以工作!(大多数情况下。由于某种原因,我的最后一列在当前分支线上被向外缩进了。)

不幸的是,VonCs答案中的信息不是编写脚本的好基础。请参见这里http://git-blame.blogspot.com/2013/06/checking-current-branch-programatically.html - Andrew C
嗯,这展示了一种获取当前分支名称的方法,如果它有一个名称的话。有没有一种获取机器友好的分支列表的[首选]方式?(并且有一些方法可以直接从输出中区分当前分支,或者通过询问git“这是否与HEAD相同的引用?”) - benkc
git for-each-ref 是处理分支的脚本友好方式。您需要运行一次符号引用以获取当前分支。 - Andrew C
+1 鼓励一下,但那确实是我以前的答案了。http://stackoverflow.com/a/16971547/6309 或者(更完整的)https://dev59.com/Hm025IYBdhLWcg3wCxZN#19585361 可以使用更少的 'sed'。 - VonC

1
在PowerShell中,以下内容显示了已合并且至少两周之前的远程分支(author:relative 格式在两周后开始显示周数而非天数):
$safeBranchRegex = "origin/(HEAD|master|develop)$";
$remoteMergedBranches = git branch --remote --merged | %{$_.trim()};
git for-each-ref --sort='authordate:iso8601' --format=' %(authordate:relative)%09%(refname:short)' refs/remotes | ?{$_ -match "(weeks|months|years) ago" -and $_ -notmatch "origin/(HEAD|master|qa/)"} | %{$_.substring($_.indexof("origin/"))} | ?{$_ -in $remoteMergedBranches}

1
VonC的答案中获得灵感并进行改进(例如包括远程分支,避免使用Perl,用git log替换git show):
for k in $(git branch -a | sed -e 's/^..//' | grep -v -- '->'); \
    do echo -e $(git log -1 --pretty=format:"%ci %cr" $k) \
    \\t$k; done | sort -r

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