簡體   English   中英

Git 日志以僅獲取特定分支的提交

[英]Git log to get commits only for a specific branch

我想列出僅屬於特定分支的所有提交。

通過以下內容,它列出了來自分支的所有提交,也列出了來自父級(master)的所有提交

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 才能達到的提交

從聽起來你應該使用cherry來看:

git cherry -v develop mybranch

這將顯示包含在mybranch 中的所有提交,但不包含在develop 中 如果您不使用最后一個選項 ( mybranch ),它將改為比較當前分支。

正如 VonC 指出的那樣,您總是將您的分支與另一個分支進行比較,因此請了解您的分支,然后選擇要比較的分支。

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

我認為這是不可能的:Git 中的一個分支總是基於另一個或至少基於另一個提交,如“ git diff 顯示不夠”中所述:

在此處輸入圖片說明

您需要一個日志參考點來顯示正確的提交。

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

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

因此,即使git log master..mybranch是一個答案,如果mybranch基於myotherbranch而本身基於master ,它仍然會顯示太多提交。

為了找到該引用(您的分支的來源),您只能解析提交並查看它們在哪個分支中,如下所示:

我終於找到了做 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的第一個字符為* 這些提交來自 master 分支,而其他四個來自其他一些分支。

以下 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分支的提示提交的mybranch

--exclude=refs/heads/mybranch --all參數告訴git rev-list從每個引用開始,除了refs/heads/mybranch

因此,當您運行git rev-list --no-walk --exclude=refs/heads/mybranch --all ,Git 會打印每個 ref 的提示提交的 SHA1 標識符,除了refs/heads/mybranch 這些提交和他們的祖先是您沒有,這些都是你希望看到的提交感興趣的提交。

其他提交是你想看到的,所以我們收集git rev-list --no-walk --exclude=refs/heads/mybranch --all並告訴 Git 顯示除那些提交及其祖先之外的所有內容.

--no-walk參數對於大型存儲庫是必需的(並且是對小型存儲庫的優化):沒有它,Git 將不得不打印,並且 shell 將不得不收集(並存儲在內存中)比必要更多的提交標識符. 對於大型存儲庫,收集的提交數量很容易超過 shell 的命令行參數限制。

吉特錯誤?

我本來希望以下內容起作用:

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

但事實並非如此。 我猜這是 Git 中的一個錯誤,但也許是故意的。

快速回答:

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

讓我們說:

  1. 你有一個分支

  2. 做一些提交

  3. 您創建了一個名為b2的分支

  4. git log -n1 ; 提交 ID 是 b2 和 master 之間的合並基礎

  5. b2 中做一些提交

  6. git log會顯示你的 b2 和 master 的日志歷史

  7. 使用commit range,如果你不熟悉這個概念,我請你google一下或者stackoverflow-it,

    對於您的實際上下文,您可以執行例如

    git log commitID_FOO..comitID_BAR

    “..”是日志命令的范圍運算符。

    這意味着,以簡單的形式,給我所有比 commitID_FOO 更新的日志......

  8. 看點#4,合並基礎

    所以: git log COMMITID_mergeBASE..HEAD會告訴你區別

  9. Git可以像這樣為你檢索合並基礎

    git merge-base b2 master
  10. 最后你可以這樣做:

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

我正在使用以下命令:

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

要么

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

你可以嘗試這樣的事情:

#!/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_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
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 不是應該走路。

我發現這種方法相對容易。

結帳到分行,然后

  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 是你運行第一個命令時會得到的兩個字符串。 將這些值不帶 <> 放在第二個命令中。

這將給出分支內文件更改的歷史列表。

在這里,我根據 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意味着提交將丟失(垃圾回收后)。 即使不幸有一個名為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 #'

在我的情況下,我們使用 Git Flow 和 GitHub。 您需要做的就是:將您的功能分支與 GitHub 上的開發分支進行比較。

它將顯示僅對您的功能分支進行的提交。

例如:

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

我需要為特定分支導出一行日志。

所以我可能想出了一個更簡單的解決方案。
在執行git log --pretty=oneline --graph我們可以看到當前分支中未完成的所有提交都是以|開頭的行|

所以一個簡單的grep -v就可以完成這項工作:
git log --pretty=oneline --graph | grep -v "^|"

當然,如果您需要其他信息,您可以更改漂亮參數,只要將其保留在一行中即可。

您可能也想刪除合並提交。
當消息以“合並分支”開頭時,再輸入一個grep -v就完成了。

在我的特定情況下,最終命令是:
git log --pretty="%ad : %an, %s" --graph | grep -v "^|" | grep -v "Merge branch"

從上面的評論中我看到 - 你只希望那些提交屬於那個分支而不是它的父分支。 但從技術上講,如果您認為 - 當您從另一個分支創建分支時,這些提交也會復制到子分支。 但是對於您的問題,以下命令將起作用。

git log childBranch...parentBranch

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

git log yourBranch --not --remotes=origin

選項 1 (看起來更快,但您可能會收到錯誤bash: /mingw64/bin/git: Argument list too long in Git Bash(盡管它在 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 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 變量來使其更快。 希望這會為您節省一些時間

這有效:

for a in $(git rev-list anybranch..mybranch); do echo -n "$a "; git branch --contains $a | xargs; done

您可以使用嚴苛的git rev-list --all但如果您只關心提交而不關心其他地方,選擇您認為您來自的任何分支至少會限制范圍,您可以一目了然地看到哪些提交屬於那個分支。

anybranch..mybranch與 mybranch 相同mybranch --not anybranchset mybranch - set anybranch from set theory while ...是不相交的集合,A 不在 B 中,B 不在 A 中

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM