【问题标题】:Git log to get commits only for a specific branchGit 日志仅获取特定分支的提交
【发布时间】:2013-02-13 07:11:46
【问题描述】:

我想列出仅属于特定分支的所有提交。

通过以下内容,它列出了来自分支的所有提交,也来自父级(主)

git log mybranch

我发现的另一个选项是排除 master 可访问的提交并给我我想要的,但我想避免知道其他分支名称的需要。

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-13T15: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 达到的提交

【问题讨论】:

  • 也看到了这个问题,但不一样
  • 你确定这是你想要的吗?我发现最好记住一个目标:“我的分支上有什么不在上游”,或者“我的分支上有什么不在 master 中”。另外,虽然 git 的修剪速度很快,但随着分支的增多,它会变得更加昂贵。我有一个我使用的脚本,在 bzr 的“缺失”命令之后我称之为“git missing”。你可以在这里找到它:github.com/jszakmeister/etc/blob/master/git-addons/git-missing
  • 我实际上需要这个作为 post-receive 钩子,所以“master”不会总是要排除的分支
  • 是的,StarPinkER 提到的副本对我来说效果很好:git log $(git merge-base HEAD branch)..branch

标签: git


【解决方案1】:

听起来你应该使用cherry

git cherry -v develop mybranch

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

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

【讨论】:

  • 这正是我所需要的,但似乎应该有一个更直观的命令(即使在 Git 世界中也是如此)。
  • 您也可以使用git cherry -v master 将您当前的分支与主分支进行比较。
  • 使用 git cherry 的危险在于,只有当分支之间的文件差异相同时,提交才会匹配。如果进行了任何类型的合并,这会使一个分支和另一个分支之间的差异不同,那么 git cherry 会将它们视为不同的提交。
  • @MattArnold 您必须将文本“develop”和“mybranch”更改为您的仓库中存在的分支
  • 当 OP 明确表示他想避免明确命名另一个分支时,我不明白为什么这会得到支持。 git cherry -v develop mybranch 与 OP 提到的 git log mybranch --not develop 非常相似。
【解决方案2】:

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

我认为这是不可能的:Git 中的分支总是基于另一个或至少基于另一个提交,如“git diff doesn't show enough”中所述:

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

如“GIT - Where did I branch from?”中所述:

分支只是指向 DAG 中某些提交的指针

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

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

【讨论】:

    【解决方案3】:

    我终于找到了做 OP 想要做的事情的方法。很简单:

    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* 是提交行中的第一个字符。这些提交来自主分支,而其他四个来自其他分支。

    【讨论】:

    • 这不是一回事,但只需少量提交即可完成工作。
    【解决方案4】:

    下面的shell命令应该做你想做的事:

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

    注意事项

    如果您已签出mybranch,则上述命令将不起作用。这是因为mybranch 上的提交也可以通过HEAD 访问,所以Git 不认为这些提交是mybranch 独有的。要在签出mybranch 时使其工作,您还必须为HEAD 添加排除项:

    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 的提示提交的 SHA1 标识符。这些提交及其祖先是您感兴趣的提交——这些是您想看到的提交。

    other 提交是您想查看的提交,因此我们收集 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 中的一个错误,但也许是故意的。

    【讨论】:

    • 我来到这里想知道如何做 Mercurial 的 hg log -b &lt;branch&gt;。我不明白为什么人们说 git 不友好。 /s
    • 我不知道为什么 git log --all --not --exclude=refs/heads/mybranch --all 不起作用,但 git log refs/heads/mybranch --not --exclude=refs/heads/mybranch --all 起作用,同样需要注意的是排除 HEAD 和 origin。
    • 这是对 SO 上所有类似的“仅限此分支”问题的最佳且唯一真实的答案。如果可以忽略标签,则可以使用git log --branches --remotes --not $(git rev-list --no-walk --exclude=mybranch --branches --remotes) 解决有关排除HEAD 的问题。请注意此处 exclude 的不同 ref 语法。
    • ... 或者使用@BenC 解决方案:git log mybranch --not --exclude=mybranch --branches --remotes,相当紧凑。
    【解决方案5】:

    快速回答:

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

    假设:

    1. 你有一个ma​​ster分支

    2. 做一些提交

    3. 您创建了一个名为 b2

    4. 的分支
    5. git log -n1;提交 ID 是 b2 和 master 之间的合并基础

    6. b2

    7. 中进行一些提交
    8. git log 将显示您的 b2 和 master 的日志历史记录

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

      对于您的实际情况,您可以这样做

      git log commitID_FOO..comitID_BAR
      

      “..”是日志命令的范围运算符。

      这意味着,以简单的形式,给我所有比 commitID_FOO 更新的日志...

    10. 查看第 4 点,合并基础

      所以:git log COMMITID_mergeBASE..HEAD 会告诉你区别

    11. Git 可以像这样为你检索合并基础

      git merge-base b2 master
      
    12. 终于可以做到了:

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

    【讨论】:

      【解决方案6】:

      我正在使用以下命令:

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

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

      【讨论】:

      • +1。 --graph 只添加* 作为第一个字符,因此可以避免此选项。 --abbrev-comit--decorate 似乎没有区别(对于我的分支)。
      【解决方案7】:

      你可以试试这样的:

      #!/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)
      

      或者,借用recipe in the Git User's Manual

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

      【讨论】:

      • +1。我在回答中写道,您需要解析提交才能找到分支的来源,而您似乎已经做到了。
      • 你用过log --walk-reflogs吗?我正在阅读有关此选项的信息,并且正在为我提供到目前为止所需的结果,(仍在测试中)
      • @dimirc Reflogs 是完全不同的野兽。它随着时间的推移记录分支点,通常是为了恢复。我不确定你在 post-receive 钩子中做了什么,但也许如果你解释了它,人们可以为你的问题提供更明智的答案。
      • 我只需要在推送时解析/检查所有提交消息。我想介绍的情况是,如果有人在 master 推送多个提交并创建一个包含多个提交的新分支。 post-receive 钩子需要检查对两个 refs/branch 所做的更改,例如 master 和 newbranch,我需要知道新分支的边界以避免两次解析提交消息,因为可能是从 master 提交的。
      • 好的。然后我认为我概述的上述方法确实是您想要的。使用 reflog,条目会在一段时间后下降,我认为这会让您有些头疼。您可能也想看看使用git show-ref --tags
      【解决方案8】:

      我发现这种方法相对容易。

      结帐到分行,然后

      1. 运行

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

      这将只提供两个 SHA:

      1) 分支的最后一次提交 [C1]

      2) 并将父级提交到分支的第一个提交 [C2]

      1. 现在运行

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

      这里 C1 和 C2 是您在运行第一个命令时会得到的两个字符串。将这些不带 的值放在第二个命令中。

      这将给出分支内文件更改历史的列表。

      【讨论】:

      • 如果你阅读了问题的前三行,你会看到作者列出了这个确切的命令并解释了为什么它不是他们想要的
      • 啊@black_fm 我对答案做了一些更改。如果你现在觉得它有用,你可以投票。 :)
      【解决方案9】:
      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 的过滤器'不应该走路。

      【讨论】:

        【解决方案10】:

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

        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
        

        【讨论】:

        • 这为我生成了大约 100 条不相关的日志消息。
        • 嘿@ArlieStephens - 我试过了,它似乎仍然对我有用?我只是在上面添加了帮助和更多错误消息。告诉我进展如何!
        • 有趣。看看你现在的代码,我认为它可能和原来一样简单,假设我关心的分支来自 master。使用当前代码,我可以明确指定父分支。 (IIRC,我的开发分支来自发布分支,而不是主分支。)
        • @ArlieStephens - 是的,我不认为我改变了任何功能......我又更新了一次,并在一些本地测试后使文档更加准确......并添加了一个“详细”选项之前有,但我没有记录它
        【解决方案11】:

        在这里,我根据 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
        

        请注意,ONLY 表示如果给定分支被删除(不包括标签的影响),则提交将是 LOST(垃圾回收之后)。即使不幸的是有一个名为mybranch 的标签(感谢前缀heads/),别名也应该起作用。另请注意,如果提交是任何远程分支(包括上游,如果有)的一部分,则不会显示提交,符合 ONLY 的定义。

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

          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 #'
        

        最后一个版本是唯一真正满足条件 commits-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 #'
        

        【讨论】:

        • 很有趣,是我 2013 年答案的一个不错的替代品。赞成。
        【解决方案12】:

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

        它将仅显示对您的功能分支所做的提交。

        例如:

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

        【讨论】:

        • 我同意当你遇到“我想看看拉取请求会是什么样子”的情况时,最好的解决方案通常是忽略git,而是使用 GitHub 来进行拉取请求或比较分支。
        【解决方案13】:

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

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

        所以一个简单的grep -v 就可以完成这项工作:
        git log --pretty=oneline --graph | grep -v "^|"

        当然,如果您需要其他信息,您可以更改 pretty 参数,只要将其保持在一行即可。

        您可能也想删除合并提交。
        当消息以“合并分支”开头时,通过管道传递另一个 grep -v 就完成了。

        在我的具体情况下,最终命令是:
        git log --pretty="%ad : %an, %s" --graph | grep -v "^|" | grep -v "Merge branch"

        【讨论】:

          【解决方案14】:

          这仍然是在 2021 年讨论过的,这个问题是 8 年前提出的。我很困惑为什么没有人建议使用 git --first-parent。这不是现在的解决方案吗?我目前有同样的问题,这对我来说看起来很好。我结合了这些选项:

          git --first-parent --cherry  first-commit-on-branch..
          

          我什至将来自 master 的进一步提交合并到我的功能分支中,但是由于cherry 是--cherry-pick、--right-only 和--no-merges 的组合,这些提交被过滤掉了。 您需要手动找出分支上的第一个提交或从那时起您想要查找所有后续提交的提交。

          【讨论】:

            【解决方案15】:

            选项 1(似乎更快,但您可能会在 Git Bash 中收到错误 bash: /mingw64/bin/git: Argument list too long(虽然它在 Linux 中有效),具体取决于您的项目有多少分支)

            git log &lt;your_branch&gt; --not $(git branch -a | grep -v &lt;your_branch&gt; | grep -ve '-&gt;' | sed "s/\s//g")

            选项 2

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

            【讨论】:

              【解决方案16】:

              我设法通过使用git log &lt;branch&gt; 来做到这一点 还有一个选项--not &lt;branch-name&gt; 原始文档:git-log

              格式示例:

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

              【讨论】:

                猜你喜欢
                • 2012-11-04
                • 1970-01-01
                • 2015-10-03
                • 2021-12-16
                • 1970-01-01
                • 2021-06-09
                • 1970-01-01
                • 1970-01-01
                • 2021-05-31
                相关资源
                最近更新 更多