查找Git提交来自哪个分支


649

在给定其SHA-1哈希值的情况下,是否有办法找出提交来自哪个分支?

如果您能告诉我如何使用Ruby Grit做到这一点,则可以加分。


4
以下不同的方法务实,实用有效的方法来推断一个可能的答案,但我们注意到,在git的问题本身就是一个误区,提交不来自分支。分支来去去去,它们移动,只有提交代表了真实的回购历史。再说一次,这不是说以下解决方案不好的方法。只是知道他们都不给出完全可信的答案,而这在git中是无法通过设计获得的。(一个简单的例子是删除分支:我分支,我两次提交,合并到另一个分支,我删除第一个分支。提交从哪里来?
RomainValeri

1
我有一种情况,我明确地从标签中提取了深度为1的浅表克隆。它便宜,方便,高效...直到现在我做的所有事情都很漂亮。现在,我想知道标签在哪个分支上,至少在那个细节上,我已经了解了。您不能总是回家,大声笑
Paul Hodges

Answers:


864

尽管Dav是正确的,即不会直接存储信息,但这并不意味着您永远找不到。这是您可以做的几件事。

查找提交所在的分支

git branch -a --contains <commit>

这将告诉您所有在历史记录中具有给定提交的分支。显然,如果提交已被合并,则此功能就没什么用了。

搜索引用

如果您正在其中进行提交的存储库中,则可以在引用日志中搜索该提交的行。超过90天的Reflog会被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可以使用其他内容,那就是您的起点!

如果您不在进行提交的存储库中工作,那么在这种情况下,您最好的方法是检查reflog,并查找何时首次将提交引入存储库中。运气好的话,您就获得了它所承诺的分支。这有点复杂,因为您不能同时遍历提交树和刷新日志。您需要解析reflog输出,检查每个哈希以查看其是否包含所需的提交。

查找后续合并提交

这是依赖于工作流程的,但是如果工作流程良好,则会在开发分支上进行提交,然后将其合并。您可以执行以下操作:

git log --merges <commit>..

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

如果您希望能够这样做,那么即使在快进情况下,也可能希望使用该--no-ff选项git merge来强制执行合并提交创建。(不过,不要急于求成。如果使用过度,这可能会令人困惑。)VonC对一个相关问题的回答有助于详细说明该主题。


5
+1。关于该特定问题的信息的绝对缺乏使您想知道这是否确实是一个问题?分支可以随时更改,重命名或删除。可能git describe就足够了,因为(带注释的)标签可以被视为比分支更重要。
VonC

9
我绝对同意-分支意味着轻巧灵活。如果您确实采用了其中信息变得很重要的工作流程,则可以使用该--no-ff选项来确保始终存在合并提交,因此您可以始终跟踪给定提交向主提交的合并的路径。
卡斯卡贝尔

32
仅供参考,如果您想查找仅在远程存在的提交,请将-a标志添加到第一个命令,例如git branch -a --contains <commit>
Jonathan Day

8
@JonathanDay:不,可以在任何分支上找到提交。如果希望遥控器上的遥控器,请使用-r
卡斯卡贝尔2011年

7
@SimonTewsi就像我说的,如果确实存在问题,请merge --no-ff在合并时可靠地记录分支名称。但是否则,请将分支名称视为临时的短标签,并将描述提交为永久的短标签。“在开发过程中,我们用什么简称?” 真的不应该像“此提交做什么”这样重要的问题吗?
卡斯卡贝尔2013年

154

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

git name-rev <SHA>

例如(其中test-branch是分支名称):

git name-rev 651ad3a
251ad3a remotes/origin/test-branch

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

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

这里git name-rev commit<SHA2>返回branchB


3
你救了我的日子。这是完美的解决方案。
塔可

7
这个完美的解决方案仅用了8年的时间。谢谢!
S1J0

6
我发现git name-rev --name-only <SHA>对于仅获取分支名称更有用。我的问题...在任何情况下都可以返回多个分支吗?
迪恩·凯顿

1
这应该是最好的答案。
JCollier

1
谢谢@JCollier的客气话。
khichar.anil

47

2013年12月更新:

sschuberth 评论

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

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

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

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

具体来说,在BRANCH包含其COMMIT祖先的第一对父代历史记录中查找最旧的提交。


2010年9月的原始答案:

塞巴斯蒂安·杜切杜什(刚刚被抽搐了(在此回答之前16分钟):

git-what-branch:发现提交在哪个分支上,或如何到达命名分支

这是来自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 1f9c381fa3e0b9b9042e310c69df87eaf9b46ea4
 1f9c381fa3e0b9b9042e310c69df87eaf9b46ea4 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 master
   v2.6.12-rc3-461-g84e48b6 is on v2.6.12-n
   [...]

该程序不考虑选择感兴趣的提交的影响,仅考虑合并操作。


4
git-what-branch似乎不再维护了。git-when-merged是用Python编写的替代方法,对我来说很好用。
sschuberth

@sschuberth感谢您的更新。我已将您的评论包含在答案中,以提高知名度。
VonC 2013年

37

例如,要查找该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 a435005445a6752dfe788b8d994e155b3cd9778f
Merge: 0953cac a06cc29
Author: Eugen Konkov
Date:   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

4
这是唯一给我想要的结果的解决方案。我尝试了其余的。
手工艺者2016年

请注意,这仅在合并提交摘要包含合并中使用的分支名称的情况下才有效,默认情况下,它们通常会这样做。但是git merge -m"Any String Here"会掩盖源分支目标和目标分支信息。
qneill

@qneill:不,合并始终具有有关合并分支的信息:Merge: f6b70fa d58bdcb。您可以像这样命名合并提交。我不会
有事

1
@EugenKonkov一旦分支遍历了合并,有关它们来自图中的哪个路径的历史记录将保留在reflog中,而不是git对象数据库中。我发布了一个答案,试图解释我的意思
qneill

UPD版本对我
有用

9

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

COMMIT=$(git rev-parse <ref>) # expands hash if needed
for BRANCH in $(git for-each-ref --format "%(refname)" refs/heads); do
  if $(git rev-list $BRANCH | fgrep -q $COMMIT); then
    echo $BRANCH
  fi
done

此SO答案的交叉报道)


6

khichar.anil覆盖在他的回答中大部分内容。

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

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

第二句话似乎遗漏了一些东西。
彼得·莫滕森

我更新了说明。Thx的反馈。
phyatt

4

一个穷人的选择是使用工具tig1HEAD,搜索提交,然后直观地追踪从提交回,直到合并提交可见的线。默认的合并消息应指定将哪个分支合并到何处:)

1 Tig是用于Git的基于ncurses的文本模式界面。它主要用作Git存储库浏览器,但它也可以协助分阶段进行更改以在块级别进行提交,并充当各种Git命令输出的传呼机。


3

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

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


1
但是,如果要添加元数据,为什么不使用git note而不是弄乱提交的标题呢?见stackoverflow.com/questions/5212957/...stackoverflow.com/questions/7298749/...stackoverflow.com/questions/7101158/...
VonC

老实说,当我写它的时候我不知道git笔记的存在。是的,使用git notes实现相同的目的可能是一个更好的主意。
pajp,2012年

3

我处理相同的问题(詹金斯多分支管道)-仅提交信息,并尝试查找此提交最初来自的分支名称。它必须适用于远程分支,本地副本不可用。

这就是我的工作方式:

git rev-parse HEAD | xargs git name-rev

(可选)您可以剥离输出:

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

0

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

您最好先拉开一切:

git pull --all

然后执行分支搜索:

git name-rev <SHA>

要么:

git branch --contains <SHA>

0

如果OP试图确定创建特定提交时分支所遍历的历史记录(“在给定其SHA-1散列值的情况下查找提交来自哪个分支”),那么没有引用日志,就不会有任何引用Git对象数据库中的记录,显示了什么命名分支绑定到什么提交历史记录。

(我将其发布为答复评论的答案。)

希望此脚本可以说明我的观点:

rm -rf /tmp/r1 /tmp/r2; mkdir /tmp/r1; cd /tmp/r1
git init; git config user.name n; git config user.email e@x.io
git commit -m"empty" --allow-empty; git branch -m b1; git branch b2
git 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 b2
git clone /tmp/r1 /tmp/r2; cd /tmp/r2; git fetch origin b2:b2
set -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'的提交是来自远程克隆/ tmp / r2的分支b1还是b2。

(此处输出的最后几行)

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

两种情况的输出git log 80c10e5..HEAD --ancestry-path --merges --oneline --color | tail -n 1git log 086c9ce..HEAD --ancestry-path --merges --oneline --color | tail -n 1命令是什么?
Eugen Konkov

当然,SHA会在每次运行命令时更改,以重新整理您的命令,我在全新运行时使用了HEAD ^ 1HEAD ^ 2。命令和输出是:$ git log HEAD^1..HEAD --ancestry-path --merges --oneline --color | tail -n 1哪个产生376142d merge branches$ git log HEAD^2..HEAD --ancestry-path --merges --oneline --color | tail -n 1哪个产生376142d merge branches -显示合并提交摘要,在创建合并时可以覆盖(如我所断言的)摘要,这可能会使合并的分支历史模糊。
qneill

0

TL; DR:

如果您关心Shell退出状态,请使用以下内容:

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

双方branch-namebranch-names接受提交作为参数,并默认 HEAD如果没有给出。


可用于脚本编写的别名

branch-current = "symbolic-ref --short HEAD"  # https://stackoverflow.com/a/19585361/5353461
branch-names = !"[ -z \"$1\" ] && git branch-current 2>/dev/null || git branch --format='%(refname:short)' --contains \"${1:-HEAD}\" #"  # https://stackoverflow.com/a/19585361/5353461
branch-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 eae13ea
master
% echo $?
0
  • 输出到STDOUT
  • 退出值为 0

提交多个分支的可达性

% git branch-name 4bc6188
Multiple branches:
attempt-extract
master%
% echo $?
1
  • 输出到STDERR
  • 退出值为1

由于存在退出状态,因此可以安全地建立这些状态。例如,要获取用于获取的遥控器:

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

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

1
您的示例的一些解释将很棒
Eugen Konkov '18

-4

除了遍历所有树,直到找到匹配的哈希,否。


7
我真的不想拒绝投票,因为严格来说,git不会永久存储该信息是正确的,但是尽管如此,还是经常有可能找出来。看我的答案。
卡斯卡贝尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.