查找包含特定提交的合并提交


182

想象一下以下历史:

       c---e---g--- feature
      /         \
-a---b---d---f---h--- master

如何找到提交“ c”已合并到主服务器中的时间(即找到合并提交“ h”)?

Answers:


156

您的示例显示该分支feature仍然可用。

在这种情况下h,最后的结果是:

git log master ^feature --ancestry-path

如果该分支feature不再可用,则可以在c和之间的历史记录行中显示合并提交master

git log <SHA-1_for_c>..master --ancestry-path --merges

然而,这将同时显示所有之后发生的合并h,以及之间e以及gfeature


比较以下命令的结果:

git rev-list <SHA-1_for_c>..master --ancestry-path

git rev-list <SHA-1_for_c>..master --first-parent

将为您提供SHA-1 h作为共同的最后一行。

如果可用,则可以comm -1 -2在这些结果上使用。如果您使用的是msysgit,则可以使用以下perl代码进行比较:

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2

(来自http://www.cyberciti.biz/faq/command-to-display-lines-common-in-files/的 perl代码,来自“ comp.unix.shell新闻组的某人”)。

如果要使其成为单线,请参阅流程替代


4
即使您有通讯,也不能使用它,因为“ git rev-list”命令的输出未按字典顺序排序。您当然可以在查找公共行之前对每个命令的输出进行排序,但是所需的提交不一定是最后一个。因此,我认为有必要像perl命令(尽管晦涩)。
mhagger

12
我刚刚写了一个脚本git-when-merged来实现这个建议(还有很多其他功能)。见github.com/mhagger/git-when-merged
mhagger 2013年

2
假设在某个点master被合并到feature,然后立即feature被合并成master一个快进(尖feature内容替换master)。那会导致--first-parent返回错误的父母吗?
开尔文

4
我试过了,comm -1 -2但是没有用。comm仅适用于排序的行。(虽然我看不懂,但是perl的单行代码有效。)
Domon

2
这有点晚了,但是添加后使人们可以发现它有用,因为git本身不提供此功能。我认为此答案无法解决某些极端情况(请参见下面的我的答案stackoverflow.com/a/43716029/58678,该案例可以处理大多数极端情况)。以下是一些极端的情况:(不git find-merge h master返回任何值,但应返回h),git find-merge d master(返回f,但应返回d),git find-merge c feature(返回e但应返回g)。
嬉皮

130

将此添加到您的~/.gitconfig

[alias]
    find-merge = "!sh -c 'commit=$0 && branch=${1:-HEAD} && (git rev-list $commit..$branch --ancestry-path | cat -n; git rev-list $commit..$branch --first-parent | cat -n) | sort -k2 -s | uniq -f1 -d | sort -n | tail -1 | cut -f2'"
    show-merge = "!sh -c 'merge=$(git find-merge $0 $1) && [ -n \"$merge\" ] && git show $merge'"

然后,您可以使用如下别名:

# current branch
git find-merge <SHA-1>
# specify master
git find-merge <SHA-1> master

要查看合并提交的消息和其他详细信息,请使用git show-merge相同的参数。

(基于Gauthier的回答。感谢Rosen Matevjavabrett纠正了sort。的问题。)


6
美丽!这是最好的嵌入式解决方案。
N琼斯

1
注意sort -k2 | uniq -f1 -d | sort -n | tail -1 | cut -f2不要正确地找到最后一行。是一个失败的示例。
罗森·马特夫

1
@RosenMatev 16db9fef5c581ab0c56137d04ef08ef1bf82b0b7当我在您的粘贴上运行它时,会在这里找到它,这不是预期的吗?您在什么操作系统上?
robinst

1
@robinst,您的正确。我有“((GNU coreutils)8.4””,对我来说,它发现29c40c3a3b33196d4e79793bd8e503a03753bad1
Rosen Matev

2
@powlo:没什么,为方便起见,这只是一个命令,而不是两个。
罗宾斯特

28

git-get-merge将找到并显示您要查找的合并提交:

pip install git-get-merge
git get-merge <SHA-1>

该命令遵循给定提交的子级,直到找到合并另一个分支(可能是主分支)为止。


22

也就是说,总结Gauthier的帖子:

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' <(git rev-list --ancestry-path <SHA-1_for_c>..master) <(git rev-list --first-parent <SHA-1_for_c>..master) | tail -n 1

编辑:因为它使用进程替换<()”,所以它与POSIX不兼容,并且可能不适用于您的shell。可以使用bashzsh尽管可以。


4
我不经常从Internet复制/粘贴代码,但是当我这样做时,它就可以完美地工作!感谢Totor让我不去思考。
2013年

2
不幸的<()是,@ TheodoreR.Smith的语法与POSIX不兼容。您需要使用bashzsh或外壳支持进程替换。我相应地编辑了答案。
Totor 2014年

没为我工作。我认为我的功能分支首先合并到另一个分支,然后再合并到主分支。这可以解释为什么您的命令导致“合并分支'release_branch'”而不是“合并分支'feature_branch'”的结果。
蒂姆·库珀斯

14

我需要这样做,并且以某种方式找到了它git-when-merged(实际上引用了这个SO问题,但是Michael Haggerty从未在这里添加对他非常漂亮的Python脚本的引用)。所以现在我有。


我实际上是从他的页面上提出这个问题的。
Flavius

12

基于Gauthier的出色答案,我们不需要使用comm这些列表进行比较。由于我们正在寻找的最后一个结果--ancestry-path也是in --first-parent,因此我们可以在前者的输出中为后者简单地grep:

git rev-list <SHA>..master --ancestry-path | grep -f <(git rev-list <SHA>..master --first-parent) | tail -1

或为方便又可重复使用的功能,提供以下功能.bashrc

function git-find-merge() {
  git rev-list $1..master --ancestry-path | grep -f <(git rev-list $1..master --first-parent) | tail -1
}

这对我有用。comm输入未排序时不起作用。
嬉皮

4

对于Ruby人群,这里有git-whence。好简单。

$ gem install git-whence
$ git whence 1234567
234557 Merge pull request #203 from branch/pathway

4

我使用下面放置在path的bash脚本~/bin/git-find-merge。它基于Gauthier的答案evilstreak的答案,并进行了一些调整以处理极端情况。comm输入未排序时引发。grep -f完美地工作。

角落案例:

  • 如果commit是分支的第一父级路径上的合并提交,则返回commit。
  • 如果commit是分支的第一父级路径上的非合并提交,则返回branch。它要么是ff合并,要么只是提交在分支上,并且没有一种很好的方法来找出正确的提交。
  • 如果commit和branch相同,则返回commit。

~/bin/git-find-merge 脚本:

#!/bin/bash

commit=$1
if [ -z $commit ]; then
    echo 1>&2 "fatal: commit is required"
    exit 1
fi
commit=$(git rev-parse $commit)
branch=${2-@}

# if branch points to commit (both are same), then return commit
if [ $commit == $(git rev-parse $branch) ]; then
    git log -1 $commit
    exit
fi

# if commit is a merge commit on first-parent path of branch,
# then return commit
# if commit is a NON-merge commit on first-parent path of branch,
# then return branch as it's either a ff merge or commit is only on branch
# and there is not a good way to figure out the right commit
if [[ $(git log --first-parent --pretty='%P' $commit..$branch | \
    cut -d' ' -f1 | \
    grep $commit | wc -l) -eq 1 ]]; then
    if [ $(git show -s --format="%P" $commit | wc -w) -gt 1 ]; then
        # if commit is a merge commit
        git log -1 $commit
    else
        # if commit is a NON-merge commit
        echo 1>&2 ""
        echo 1>&2 "error: returning the branch commit (ff merge or commit only on branch)"
        echo 1>&2 ""
        git log -1 $branch
    fi
    exit
fi

# 1st common commit from bottom of first-parent and ancestry-path
merge=$(grep -f \
    <(git rev-list --first-parent  $commit..$branch) \
    <(git rev-list --ancestry-path $commit..$branch) \
        | tail -1)
if [ ! -z $merge ]; then
    git log -1 $merge
    exit
fi

# merge commit not found
echo 1>&2 "fatal: no merge commit found"
exit 1

这让我做到了:

(master)
$ git find-merge <commit>    # to find when commit merged to current branch
$ git find-merge <branch>    # to find when branch merged to current branch
$ git find-merge <commit> pu # to find when commit merged to pu branch

这个脚本也可以在我的github上找到


2
git log --topo-order

然后在提交之前查找第一个合并。


1

我的@robinst想法的红宝石版本的运行速度提高了两倍(这对于搜索非常旧的提交非常重要)。

find-commit.rb

commit = ARGV[0]
master = ARGV[1] || 'origin/master'

unless commit
  puts "Usage: find-commit.rb commit [master-branch]"
  puts "Will show commit that merged <commit> into <master-branch>"
  exit 1
end

parents = `git rev-list #{commit}..#{master} --reverse --first-parent --merges`.split("\n")
ancestry = `git rev-list #{commit}..#{master} --reverse --ancestry-path --merges`.split("\n")
merge = (parents & ancestry)[0]

if merge
  system "git show #{merge}"
else
  puts "#{master} doesn't include #{commit}"
  exit 2
end

您可以像这样使用它:

ruby find-commit.rb SHA master

0

您可以尝试这样的事情。这个想法是遍历所有合并提交,并查看其中的一个是否可以到达提交“ c”:

$ git log --merges --format='%h' master | while read mergecommit; do
  if git log --format='%h' $mergecommit|grep -q $c; then
    echo $mergecommit;
    break
  fi
done

这与以下内容相同:git rev-list <SHA-1_for_c>..master --ancestry-path --merges
Eugen Konkov 2016年

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.