Git日志,只获取特定分支的提交

我想列出只是特定分支的一部分的所有提交。

使用下面的代码,它列出了来自分支的所有提交,也包括来自父(master)的所有提交

git log mybranch

我发现的另一种选择是排除主可达的提交,并给我我想要的,但我想避免知道其他分支名称的需要。

git log mybranch --not master

我试图使用git for-each-ref,但它也列出了mybranch,所以实际上它排除了所有:

git log mybranch --not $(git for-each-ref --format '^%(refname:short)' refs/heads/)

更新:

我正在测试一个我不久前发现的新选项,直到现在看来,这可能是我一直在寻找的:

git log --walk-reflogs mybranch

更新(2013 - 02 - 13 t15:08):

——walk-reflogs选项很好,但我检查了reflogs的过期时间(默认为90天,gc.reflogExpire)。

我想我找到了我一直在寻找的答案

git log mybranch --not $(git for-each-ref --format='%(refname)' refs/heads/ | grep -v "refs/heads/mybranch")

我只是从可用的分支列表中删除当前分支,并使用该列表从日志中排除。这样我只能得到只有mybranch能到达的提交。

276936 次浏览

但是我想避免知道其他分支机构名称的需要。

我认为这是不可能的:Git中的分支总是基于另一个分支,或者至少是另一个提交,正如“Git差异不够显示”中解释的那样:

enter image description here

您需要一个日志参考点来显示正确的提交。

如“GIT -我从哪里分支?”中所述:

分支只是指向DAG中特定提交的指针

因此,即使git log master..mybranch是一个答案,如果mybranch基于myotherbranch,本身基于master,它仍然会显示太多提交。

为了找到这个引用(分支的起源),你只能解析提交并查看它们在哪个分支中,如下所示:

你可以尝试这样做:

#!/bin/bash


all_but()
{
target="$(git rev-parse $1)"
echo "$target --not"
git for-each-ref --shell --format="ref=%(refname)" refs/heads | \
while read entry
do
eval "$entry"


test "$ref" != "$target" && echo "$ref"
done
}


git log $(all_but $1)

或者,借用Git用户手册中的食谱:

#!/bin/bash
git log $1 --not $( git show-ref --heads | cut -d' ' -f2 | grep -v "^$1" )

快速回答:

git log $(git merge-base master b2)..HEAD

比方说:

  1. 你有一个master分支

  2. 进行几次提交

  3. 您创建了一个名为b2的分支

  4. git log -n1;提交Id是b2和master之间的合并基

  5. 做一些提交在b2

  6. git log将显示b2和master的日志历史

  7. 使用提交范围,如果你不熟悉这个概念,我邀请你谷歌它或堆栈溢出它,

    对于您的实际上下文,您可以这样做,例如

    git log commitID_FOO..comitID_BAR
    

    “..”是log命令的范围操作符。

    这意味着,在一个简单的形式中,给我所有比commitID_FOO更近期的日志…

  8. 看第4点,merge base

    因此:git log COMMITID_mergeBASE..HEAD将显示差异

  9. Git可以像这样为您检索合并基

    git merge-base b2 master
    
  10. Finally you can do:

    git log $(git merge-base master b2)..HEAD
    

听起来你应该使用cherry:

git cherry -v develop mybranch

这将显示包含在mybranch中的所有提交,但不包含在开发中。如果省略最后一个选项(mybranch),它将比较当前分支。

正如VonC指出的那样,您总是在将您的分支与另一个分支进行比较,因此了解您的分支,然后选择与哪个分支进行比较。

在我的情况下,我们使用Git Flow和GitHub。你所需要做的就是:在GitHub上比较你的功能分支和开发分支。

它将只显示提交到您的特性分支。

例如:

https://github.com/your_repo/compare/develop...feature_branch_name

下面的shell命令可以做你想做的事情:

git log --all --not $(git rev-list --no-walk --exclude=refs/heads/mybranch --all)

警告

如果你签出了mybranch,上面的命令将不起作用。这是因为mybranch上的提交也可以通过HEAD到达,所以Git并不认为提交是mybranch唯一的。为了让它在mybranch被签出时工作,你还必须为HEAD添加一个exclude:

git log --all --not $(git rev-list --no-walk \
--exclude=refs/heads/mybranch \
--exclude=HEAD \
--all)

然而,你应该排除HEAD,除非mybranch被签出,否则你可能会显示不专属于mybranch的提交。

类似地,如果你有一个名为origin/mybranch的远程分支,它对应于本地mybranch分支,你必须排除它:

git log --all --not $(git rev-list --no-walk \
--exclude=refs/heads/mybranch \
--exclude=refs/remotes/origin/mybranch \
--all)

如果远程分支是远程存储库的默认分支(通常只适用于origin/master),你也必须排除origin/HEAD:

git log --all --not $(git rev-list --no-walk \
--exclude=refs/heads/mybranch \
--exclude=refs/remotes/origin/mybranch \
--exclude=refs/remotes/origin/HEAD \
--all)

如果你签出了分支,而且有一个远程分支,而且远程分支是远程存储库的默认分支,那么你最终会排除很多:

git log --all --not $(git rev-list --no-walk \
--exclude=refs/heads/mybranch \
--exclude=HEAD
--exclude=refs/remotes/origin/mybranch \
--exclude=refs/remotes/origin/HEAD \
--all)

解释

git rev-list命令是一个低级(管道)命令,它遍历给定的修订并转储遇到的SHA1标识符。你可以把它想象成git log,只不过它只显示sha1——没有日志消息,没有作者名,没有时间戳,没有任何“花哨”的东西。

--no-walk选项,顾名思义,防止git rev-list遍历祖先链。所以如果你输入git rev-list --no-walk mybranch,它只会打印一个SHA1标识符:mybranch分支的提示提交标识符。

--exclude=refs/heads/mybranch --all参数告诉git rev-list从除refs/heads/mybranch之外的每个引用开始。

因此,当你运行git rev-list --no-walk --exclude=refs/heads/mybranch --all时,Git会打印除refs/heads/mybranch外的每个ref的tip提交的SHA1标识符。这些提交和它们的祖先是你感兴趣的提交——这些是你想要看到的提交。

其他提交是你想要看到的,所以我们收集git rev-list --no-walk --exclude=refs/heads/mybranch --all的输出,并告诉Git显示除了这些提交和它们的祖先之外的所有内容。

--no-walk参数对于大型存储库是必要的(并且是对小型存储库的优化):如果没有它,Git将不得不打印,并且shell将不得不收集(并在内存中存储)比必需的更多的提交标识符。对于大型存储库,收集的提交数量很容易超过shell命令行参数的限制。

Git错误?

我本希望以下几点能起作用:

git log --all --not --exclude=refs/heads/mybranch --all

但事实并非如此。我猜这是Git中的一个bug,但也可能是故意的。

我终于找到了做上级想要的事的方法。其实很简单:

git log --graph [branchname]

该命令将以图形的格式显示从所提供的分支可到达的所有提交。但是,通过查看提交图中*是提交行的第一个字符,你可以很容易地过滤该分支上的所有提交。

例如,让我们看看下面cakephp GitHub repo上git log --graph master的摘录:

D:\Web Folder\cakephp>git log --graph master
*   commit 8314c2ff833280bbc7102cb6d4fcf62240cd3ac4
|\  Merge: c3f45e8 0459a35
| | Author: José Lorenzo Rodríguez <lorenzo@users.noreply.github.com>
| | Date:   Tue Aug 30 08:01:59 2016 +0200
| |
| |     Merge pull request #9367 from cakephp/fewer-allocations
| |
| |     Do fewer allocations for simple default values.
| |
| * commit 0459a35689fec80bd8dca41e31d244a126d9e15e
| | Author: Mark Story <mark@mark-story.com>
| | Date:   Mon Aug 29 22:21:16 2016 -0400
| |
| |     The action should only be defaulted when there are no patterns
| |
| |     Only default the action name when there is no default & no pattern
| |     defined.
| |
| * commit 80c123b9dbd1c1b3301ec1270adc6c07824aeb5c
| | Author: Mark Story <mark@mark-story.com>
| | Date:   Sun Aug 28 22:35:20 2016 -0400
| |
| |     Do fewer allocations for simple default values.
| |
| |     Don't allocate arrays when we are only assigning a single array key
| |     value.
| |
* |   commit c3f45e811e4b49fe27624b57c3eb8f4721a4323b
|\ \  Merge: 10e5734 43178fd
| |/  Author: Mark Story <mark@mark-story.com>
|/|   Date:   Mon Aug 29 22:15:30 2016 -0400
| |
| |       Merge pull request #9322 from cakephp/add-email-assertions
| |
| |       Add email assertions trait
| |
| * commit 43178fd55d7ef9a42706279fa275bb783063cf34
| | Author: Jad Bitar <jadbitar@mac.com>
| | Date:   Mon Aug 29 17:43:29 2016 -0400
| |
| |     Fix `@since` in new files docblocks
| |

正如你所看到的,只有提交8314c2ff833280bbc7102cb6d4fcf62240cd3ac4c3f45e811e4b49fe27624b57c3eb8f4721a4323b*作为提交行中的第一个字符。这些提交来自主分支,而其他四个提交来自其他分支。

这将输出当前分支上的提交。如果传递了任何参数,它只输出哈希值。

git_show_all_commits_only_on_this_branch

#!/bin/bash
function show_help()
{
ME=$(basename $0)
IT=$(cat <<EOF
  

usage: $ME {NEWER_BRANCH} {OLDER_BRANCH} {VERBOSE}
  

Compares 2 different branches, and lists the commits found only
in the first branch (newest branch).


e.g.
  

$ME         -> default. compares current branch to master
$ME B1      -> compares branch B1 to master
$ME B1 B2   -> compares branch B1 to B2
$ME B1 B2 V -> compares branch B1 to B2, and displays commit messages
  

)
echo "$IT"
exit
}


if [ "$1" == "help" ]
then
show_help
fi


# Show commit msgs if any arg passed for arg 3
if [ "$3" ]
then
OPT="-v"
fi


# get branch names
OLDER_BRANCH=${2:-"master"}
if [ -z "$1" ]
then
NEWER_BRANCH=$(git rev-parse --abbrev-ref HEAD)
else
NEWER_BRANCH=$1
fi


if [ "$NEWER_BRANCH" == "$OLDER_BRANCH" ]
then
echo "  Please supply 2 different branches to compare!"
show_help
fi


OUT=$(\git cherry $OPT $OLDER_BRANCH $NEWER_BRANCH)


if [ -z "$OUT" ]
then
echo "No differences found. The branches $NEWER_BRANCH and $OLDER_BRANCH are in sync."
exit;
fi


if [ "$OPT" == "-v" ]
then
echo "$OUT"
else
echo "$OUT" | awk '{print $2}'
fi

我发现这种方法相对简单。

结帐到分行和比

  1. < p >运行

    git rev-list --simplify-by-decoration -2 HEAD
    

This will provide just two SHAs:

1) last commit of the branch [C1]

2) and commit parent to the first commit of the branch [C2]

  1. Now run

    git log --decorate --pretty=oneline --reverse --name-status <C2>..<C1>
    

Here C1 and C2 are two strings you will get when you run first command. Put these values without <> in second command.

This will give list of history of file changing within the branch.

git rev-list --exclude=master --branches --no-walk

将列出不是master的每个分支的提示。

git rev-list master --not $(git rev-list --exclude=master --branches --no-walk)

将列出master历史中没有在任何其他分支历史中的每个提交。

排序对于为提交选择设置过滤器管道的选项很重要,因此--branches必须遵循它应该应用的任何排除模式,而--no-walk必须遵循提供提交的过滤器,rev-list不应该走过。

我正在使用以下命令:

git shortlog --no-merges --graph --abbrev-commit master..<mybranch>

git log --no-merges --oneline --decorate master..<mybranch>

我需要在一行中为特定的分支导出日志。

所以我可能想出了一个更简单的解决方案。
当执行git log --pretty=oneline --graph时,我们可以看到当前分支中所有未完成的提交都是以|

开头的行 所以一个简单的grep -v就可以完成这个工作:
git log --pretty=oneline --graph | grep -v "^|" < / p >

当然,如果你需要其他信息,你可以改变pretty参数,只要你把它放在一行中。

你可能也想删除合并提交。
当消息以"Merge branch"开头时,管道另一个grep -v,你就完成了

在我的特定ase中,最后的命令是:
git log --pretty="%ad : %an, %s" --graph | grep -v "^|" | grep -v "Merge branch" < / p >

在这里,我根据Richard Hansen的回答(和Ben C的建议)给出了一个别名,但我对其进行了调整,以排除标签。别名应该相当健壮。

# For Git 1.22+
git config --global alias.only '!b=${1:-$(git branch --show-current)}; git log --oneline --graph "heads/$b" --not --exclude="$b" --branches --remotes #'
# For older Git:
git config --global alias.only '!b=${1:-$(git symbolic-ref -q --short HEAD)}; b=${b##heads/}; git log --oneline --graph "heads/$b" --not --exclude="$b" --branches --remotes #'

使用示例:

git only mybranch  # Show commits that are in mybranch ONLY
git only           # Show commits that are ONLY in current branch

注意,只有意味着如果给定的分支被删除(不包括标签的影响),提交将是失去了(在垃圾收集之后)。即使有一个名为mybranch的标记(多亏了前缀heads/),别名也应该工作。还要注意,根据只有的定义,如果提交是任何远程分支(包括上游分支,如果有的话)的一部分,则不会显示提交。

别名将单行历史记录显示为所选提交的图形。

  a --- b --- c --- master
\           \
\           d
\           \
e --- f --- g --- mybranch (HEAD)
\
h --- origin/other

在上面的例子中,git only将显示:

  * (mybranch,HEAD)
* g
|\
| * d
* f

为了包含标签(但仍然不包括HEAD),别名变成(针对旧Git进行如上调整):

git config --global alias.only '!b=${1:-$(git branch --show-current)};  git log --oneline --graph --all --not --exclude="refs/heads/$b" --exclude=HEAD --all #'

或者包含所有标签的变体,包括HEAD(并默认删除当前分支,因为它不会输出任何东西):

git config --global alias.only '!git log --oneline --graph --all --not --exclude=\"refs/heads/$1\" --all #'

最后一个版本是唯一真正满足commit -that-are-lost-if-given-branch-is-deleted标准的版本,因为如果签出分支就不能删除分支,HEAD或任何其他标记指出的提交都不会丢失。然而,前两个变体更有用。

最后,别名不能用于远程分支(例如。git only origin/master)。别名必须修改,例如:

git config --global alias.remote-only '!git log --oneline --graph "$1" --not --exclude="$1" --remotes --branches #'

选项1(似乎更快,但你可能会在Git Bash中得到错误bash: /mingw64/bin/git: Argument list too long(它在Linux中工作),这取决于你的项目有多少分支)

git log <your_branch> --not $(git branch -a | grep -v <your_branch> | grep -ve '->' | sed "s/\s//g")

选项2

git rev-list <your_branch> | git name-rev --stdin | sed -E 's/~[0-9]+//g; s/\^[0-9]+//g' | grep " <your_branch>" | awk -F " " '{print $1}'

我通过使用git log <branch>来做到这一点 还有一个--not <branch-name>选项 原始文档:git-log

格式示例:

git log branch-name  --date-order --format='%ai %an <%ae> %h %f'--format='%ai , %an ,<%ae> ,%h ,%f' >> c:\log.csv

这个问题在2021年还在讨论,8年前就被问到了。我很困惑为什么没有人建议git -first parent。这难道不是现今的解决方案吗?我目前也有同样的问题,这看起来对我来说很好。我综合了这些选项:

git --first-parent --cherry  first-commit-on-branch..
我甚至合并了来自master的进一步提交到我的特性分支中,但这些提交被过滤掉了,因为cherry是-cherry-pick, -right-only和-no-merges的组合。 你需要手动找到分支上的第一个提交,或者从那以后你想要找到所有后续的提交
从上面的评论中我看到-你只希望那些提交属于那个分支,而不是它的父分支。但从技术上讲,当你从另一个分支创建一个分支时,这些提交也会复制到子分支。 但是对于你的问题,下面的命令是有效的
git log childBranch...parentBranch

如果父分支是远程分支,可以使用以下命令。

git log yourBranch --not --remotes=origin

我做了一个别名,可以简单地键入“;git logbranch"并且所有的提交都是针对当前分支的。 下面是别名:

[alias]
logbranch = "!cd -- \"${GIT_PREFIX:-.}\" && CURRENT_BRANCH=$(git branch --show-current) && git log $CURRENT_BRANCH --not $(git for-each-ref --format='%(refname)' refs/heads/ | grep -v refs/heads/$CURRENT_BRANCH) #"

注意:把它放在主目录下的.gitconfig文件的[alias]标签下。

所以,我基本上使用了问题的帖子中最初发布的内容,并使用了CURRENT_BRANCH变量以使其更快。 希望这将节省你一些时间!< / p >