查找Git提交来自哪个分支

有没有办法找出提交来自哪个分支,给定其SHA-1哈希值?

如果您能告诉我如何使用Ruby Grit完成此操作,则会获得加分。

323332 次浏览

除了搜索所有的树,直到找到匹配的哈希,没有。

虽然Dav说信息不是直接存储的是正确的,但这并不意味着你永远无法发现。以下是一些你可以做的事情。

查找提交所在的分支

git branch -a --contains <commit>

这将告诉您在其历史中具有给定提交的所有分支。显然,如果提交已经合并,这就不那么有用了。

搜索重新发布

如果你在进行提交的存储库中工作,你可以在重新发布的代码中搜索该提交的行。超过90天的重新发布会被git-gc修剪,所以如果提交太旧,你将找不到它。也就是说,你可以这样做:

git reflog show --all | grep a871742

查找提交a871742。请注意,您必须使用提交的缩写7首数字。输出应该是这样的:

a871742 refs/heads/completion@{0}: commit (amend): mpc-completion: total rewrite

表示提交是在分支“完成”上进行的。默认输出显示缩写的提交哈希,因此请务必不要搜索完整哈希,否则您将找不到任何内容。

git reflog show实际上只是git log -g --abbrev-commit --pretty=oneline的别名,所以如果你想摆弄输出格式以使不同的东西可用于grep for,那就是你的起点!

如果你不在提交的存储库中工作,在这种情况下你能做的最好的事情就是检查reflgs并找到提交首次引入到你的存储库的时间;幸运的话,你获取了它提交到的分支。这有点复杂,因为你不能同时遍历提交树和reflgs。你需要解析reflg输出,检查每个哈希以查看它是否包含所需的提交。

查找后续的合并提交

这是依赖于工作流的,但是对于良好的工作流,提交是在开发分支上进行的,然后合并到开发分支中。你可以这样做:

git log --merges <commit>..

查看具有给定提交作为祖先的合并提交。(如果提交仅合并一次,则第一个应该是您要进行的合并;否则您必须检查几个,我想。)合并提交消息应包含已合并的分支名称。

如果你希望能够做到这一点,你可能希望使用--no-ff选项git merge来强制创建合并提交,即使在快进情况下也是如此。(不过,不要太急于求成。如果过度使用,这可能会变得模糊。)VonC对相关问题的回答对这个主题进行了有益的阐述。

2013年12月更新:

sschuberth评论数

git-what-branch(Perl脚本,见下文)似乎不再维护。git-when-merged是一个用Python编写的替代方案,对我来说效果很好。

它基于“查找包含特定提交的合并提交”。

git when-merged [OPTIONS] COMMIT [BRANCH...]

查找提交何时合并到一个或多个分支中。
找到将COMMIT带入指定BRANCH(es)的合并提交。

具体来说,在包含COMMIT作为祖先的BRANCH的第一父级历史记录中查找最古老的提交。


2010年9月的原始答复:

塞巴斯蒂安灌肠器只是推特(在这个SO答案之前16分钟):

git分支:发现提交在哪个分支上,或者它是如何到达命名分支的

这是Seth Robertson中的perl脚本,看起来很有趣:

概要

git-what-branch [--allref] [--all] [--topo-order | --date-order ][--quiet] [--reference-branch=branchname] [--reference=reference]<commit-hash/tag>...

概述

告诉我们(默认情况下)提交和合并的最早因果路径,以导致请求的提交到达命名分支。如果直接在命名分支上进行提交,那显然是最早的路径。

通过最早的因果路径,我们指的是最早合并到命名分支的路径,通过提交时间(除非指定--topo-order)。

性能

如果许多分支(例如数百个)包含提交,系统可能需要很长时间(对于Linux树中的特定提交,探索一个分支需要8秒,但有超过200个候选分支)来跟踪每个提交的路径。选择一个特定的--reference-branch --reference tag来检查将快数百倍(如果你有数百个候选分支)。

示例

 # git-what-branch --all 1f9c381fa3e0b9b9042e310c69df87eaf9b46ea41f9c381fa3e0b9b9042e310c69df87eaf9b46ea4 first merged onto master using the following minimal temporal path:v2.6.12-rc3-450-g1f9c381 merged up at v2.6.12-rc3-590-gbfd4bda (Thu May  5 08:59:37 2005)v2.6.12-rc3-590-gbfd4bda merged up at v2.6.12-rc3-461-g84e48b6 (Tue May  3 18:27:24 2005)v2.6.12-rc3-461-g84e48b6 is on masterv2.6.12-rc3-461-g84e48b6 is on v2.6.12-n[...]

这个程序没有考虑到对感兴趣的提交进行樱桃挑选的影响,只有合并操作。

作为一个实验,我做了一个post-提交挂钩,将当前签出分支的信息存储在提交元数据中。我还稍微修改了gitk以显示该信息。

您可以在这里查看:https://github.com/pajp/branch-info-commits

一个穷人的选择是在HEAD上使用工具#01,搜索提交,然后直观地按照该提交的行进行备份,直到看到合并提交。默认的合并消息应该指定哪个分支被合并到哪里:)

1 Tig是一个基于ncurses的Git文本模式界面。它的功能主要作为Git存储库浏览器,但它也可以帮助暂存在块级别更改提交并充当输出的寻呼机各种Git命令。

git branch --contains <ref>是执行此操作的最明显的“瓷器”命令。如果您只想使用“管道”命令执行类似的操作:

COMMIT=$(git rev-parse <ref>) # expands hash if neededfor BRANCH in $(git for-each-ref --format "%(refname)" refs/heads); doif $(git rev-list $BRANCH | fgrep -q $COMMIT); thenecho $BRANCHfidone

(从这个SO答案交叉)

例如,要发现c0118fa提交来自redesign_interactions

* ccfd449 (HEAD -> develop) Require to return undef if no digits found*   93dd5ff Merge pull request #4 from KES777/clean_api|\| * 39d82d1 Fix tc0118faests for debugging debugger internals| * ed67179 Move &push_frame out of core| * 2fd84b5 Do not lose info about call point| * 3ab09a2 Improve debugger output: Show info about emitted events| *   a435005 Merge branch 'redesign_interactions' into clean_api| |\| | * a06cc29 Code comments| | * d5d6266 Remove copy/paste code| | * c0118fa Allow command to choose how continue interaction| | * 19cb534 Emit &interact event

你应该运行:

git log c0118fa..HEAD --ancestry-path --merges

然后向下滚动以找到最后合并提交。这是:

commit a435005445a6752dfe788b8d994e155b3cd9778fMerge: 0953cac a06cc29Author: Eugen KonkovDate:   Sat Oct 1 00:54:18 2016 +0300
Merge branch 'redesign_interactions' into clean_api

更新

或者只有一个命令:

git log c0118fa..HEAD --ancestry-path --merges --oneline --color | tail -n 1

要查找本地分支:

grep -lR YOUR_COMMIT .git/refs/heads | sed 's/.git\/refs\/heads\///g'

要查找远程分支:

grep -lR $commit .git/refs/remotes | sed 's/.git\/refs\/remotes\///g'

如果OP试图确定在创建特定提交时被分支遍历的历史(“根据其SHA-1哈希值找出提交来自哪个分支”),那么如果没有reflg,则没有Git对象数据库中的任何记录显示命名分支绑定到什么提交历史。

(我发布了这个作为回复评论的答案。)

希望这个脚本能说明我的观点:

rm -rf /tmp/r1 /tmp/r2; mkdir /tmp/r1; cd /tmp/r1git init; git config user.name n; git config user.email e@x.iogit commit -m"empty" --allow-empty; git branch -m b1; git branch b2git checkout b1; touch f1; git add f1; git commit -m"Add f1"git checkout b2; touch f2; git add f2; git commit -m"Add f2"git merge -m"merge branches" b1; git checkout b1; git merge b2git clone /tmp/r1 /tmp/r2; cd /tmp/r2; git fetch origin b2:b2set -x;cd /tmp/r1; git log --oneline --graph --decorate; git reflog b1; git reflog b2;cd /tmp/r2; git log --oneline --graph --decorate; git reflog b1; git reflog b2;

输出显示没有任何方法可以知道“Add f1”的提交是来自远程克隆的分支b1还是b2 /tmp/r2.

(输出的最后一行在这里)

+ cd /tmp/r1+ git log --oneline --graph --decorate*   f0c707d (HEAD, b2, b1) merge branches|\| * 086c9ce Add f1* | 80c10e5 Add f2|/* 18feb84 empty+ git reflog b1f0c707d b1@{0}: merge b2: Fast-forward086c9ce b1@{1}: commit: Add f118feb84 b1@{2}: Branch: renamed refs/heads/master to refs/heads/b118feb84 b1@{3}: commit (initial): empty+ git reflog b2f0c707d b2@{0}: merge b1: Merge made by the 'recursive' strategy.80c10e5 b2@{1}: commit: Add f218feb84 b2@{2}: branch: Created from b1+ cd /tmp/r2+ git log --oneline --graph --decorate*   f0c707d (HEAD, origin/b2, origin/b1, origin/HEAD, b2, b1) merge branches|\| * 086c9ce Add f1* | 80c10e5 Add f2|/* 18feb84 empty+ git reflog b1f0c707d b1@{0}: clone: from /tmp/r1+ git reflog b2f0c707d b2@{0}: fetch origin b2:b2: storing head

这个简单的命令就像一个魅力:

git name-rev <SHA>

例如(其中测试分支是分支名称):

git name-rev 651ad3a251ad3a remotes/origin/test-branch

即使这样也适用于复杂的场景,例如:

origin/branchA//branchB/commit<SHA1>/commit<SHA2>

这里git name-rev commit<SHA2>返回分支B

太长别读:

如果您关心shell退出状态,请使用以下命令:

  • branch-current-当前分支的名称
  • branch-names-干净的分支名称(每行一个)
  • branch-name-确保只从branch-names返回一个分支

branch-namebranch-names都接受提交作为参数,如果没有给出,则默认为HEAD


脚本中有用的别名

branch-current = "symbolic-ref --short HEAD"  # https://stackoverflow.com/a/19585361/5353461branch-names = !"[ -z \"$1\" ] && git branch-current 2>/dev/null || git branch --format='%(refname:short)' --contains \"${1:-HEAD}\" #"  # https://stackoverflow.com/a/19585361/5353461branch-name = !"br=$(git branch-names \"$1\") && case \"$br\" in *$'\\n'*) printf \"Multiple branches:\\n%s\" \"$br\">&2; exit 1;; esac; echo \"$br\" #"

提交只能从一个分支到达

% git branch-name eae13eamaster% echo $?0
  • 输出到STDOUT
  • 退出值为0

提交可从多个分支到达

% git branch-name 4bc6188Multiple branches:attempt-extractmaster%% echo $?1
  • 输出到STDERR
  • 退出值为1

由于退出状态,可以安全地构建这些。例如,要获取用于获取的远程:

remote-fetch = !"branch=$(git branch-name \"$1\") && git config branch.\"$branch\".remote || echo origin #"

我处理同样的问题(Jenkins多分支管道)-只有提交信息并试图找到此提交最初来自的分支名称。它必须适用于远程分支,本地副本不可用。

这就是我的工作:

git rev-parse HEAD | xargs git name-rev

您可以选择删除输出:

git rev-parse HEAD | xargs git name-rev | cut -d' ' -f2 | sed 's/remotes\/origin\///g'

我认为有人应该面对同样的问题,即找不到分支,尽管它实际上存在于一个分支中。

你最好先把所有的都拉出来:

git pull --all

然后执行分支搜索:

git name-rev <SHA>

或:

git branch --contains <SHA>

#30340;的答案中的大部分内容。

我只是添加了将从修订名称列表中删除标签的标志。这给了我们:

git name-rev --name-only --exclude=tags/* $SHA

我尝试了上述所有解决方案,但没有一个对我有效。

这是迄今为止唯一对我有效的方法(假设HEAD在一个合理的地方):

git log --branches --source | grep <sha>
#or if you also care about remotesgit log --branches --remotes --source | grep <sha>

分支的名称应该在该行的末尾。

留档

--来源

打印出命令行上给出的ref名称,通过该名称到达每个提交。

因此,这可能会根据HEAD的位置而改变,但对我来说,将HEAD放在我的master分支的最新提交中会产生我预期的结果。

使用gitk --all进行可视化检查也可能有所帮助。它为每个提交都有一个“分支”字段,但它显示了“可以到达”该提交的所有分支,而不一定是该提交“打开”的分支。看这里


旁白:

值得一提的是,这个问题有点用词不当。与其他SVC不同,git分支只是指向提交历史/图中位置的临时指针。它们不“拥有”提交,也不“由提交组成”。它们本质上只是标签,在你签出标签时更新到你推送的任何提交。所以一个分支不是一系列提交,而是指向一系列提交结束/尖端的指针。因此,提交不“属于”或“来自”分支,它们只是你图中的节点。

那么我们在这里到底在做什么?我们使用可达性的概念。如果我们从每个分支指向的提交开始(“分支”的“末端”)并追溯历史,我们能找到/到达什么提交?如果找到它们的唯一方法是开始在该特定分支上搜索,我们说提交是“在”分支上。

请注意,这不是一件稳定的事情。如果我们突然合并,重命名或重新命名我们的分支,哪些提交可以被(因此'on')我们的分支到达,我们的分支将会改变。这就是为什么提交在特定分支上/归某个特定分支所有在git中不是一个官方的或总是有意义的概念。(以及为什么这个问题没有一个干净/明确的答案)

简短的回答是,试图重建这些信息的技巧似乎并不是在所有情况下都有效。

git checkout <SHA>->将使您进入分离的HEAD状态。

git rev-parse --abbrev-ref HEAD->将打印HEAD,因此此命令在此状态下不起作用。

现在我们处于分离的HEAD状态,我们可以使用以下命令来获取分支名称。
PS:如果你不在分离的HEAD状态,这将不起作用!

git branch -a --contains HEAD | sed -n 2p | awk '{ printf $1 }'

瞧!现在你有了分支名称。