特定のコミットを含むマージコミットを見つける


183

次の履歴を想像してみてください。

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

コミット "c"がマスターにマージされたときにどのようにして見つけることができますか(つまり、マージコミット "h"を見つけます)?

回答:


157

あなたの例は、ブランチfeatureがまだ利用可能であることを示しています。

その場合h、最後の結果は次のとおりです。

git log master ^feature --ancestry-path

ブランチfeatureがもう利用できない場合は、cとの間の履歴行にマージコミットを表示できますmaster

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

ただし、これによりh、の後、およびの間egに発生したすべてのマージも表示されfeatureます。


次のコマンドの結果を比較します。

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

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

h共通の最後の行としてSHA-1を提供します。

利用できる場合は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
commがある場合でも、「git rev-list」コマンドの出力は辞書式にソートされないため、使用できません。もちろん、一般的な行を探す前に各コマンドの出力を並べ替えることもできますが、その場合、目的のコミットが必ずしも最後のコミットであるとは限りません。ですから、(あいまいではありますが)perlコマンドのようなものが必要だと思います。
mhagger 2013年

12
この提案を実装するスクリプトgit-when-mergedを(ちょうど他のいくつかの機能と共に)書いたところです。github.com/mhagger/git-when-merged
mhagger

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を返す必要があります)。
hIpPy 2017年

133

これをあなたに追加してください~/.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は、同じ引数を指定して使用します。

(に基づく ゴーティエの答え。おかげローゼンMatevjavabrettの問題を修正するためsort。)


6
綺麗な!これは最高のドロップインソリューションです。
Nジョーンズ

1
sort -k2 | uniq -f1 -d | sort -n | tail -1 | cut -f2共通の最後の行が正しく検出されないことに注意してください。ここで「それが失敗した例です。
Rosen Matev 2017年

1
@RosenMatev 16db9fef5c581ab0c56137d04ef08ef1bf82b0b7ペーストで実行するとここで見つかりますが、それは予期されていませんか?どのOSを使用していますか?
robinst 2017年

1
@robinst、あなたは正しいです。私は「(GNU coreutils)8.4」を入手しましたが、私にとってそれは見つかりました29c40c3a3b33196d4e79793bd8e503a03753bad1
Rosen Matev

2
@powlo:特にありません。便宜上、コマンドは2つではなく1つにすぎません。
robinst

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互換ではなく、シェルで動作しない可能性があります。それはで動作しますbashzshけれども。


4
私はインターネットからコードをコピー/ペーストすることはあまりありませんが、そうすることで完全に機能します!私が考えないようにしてくれたTotorに感謝します。
Ben

2
@ TheodoreR.Smith残念ながら、構文<()はPOSIX互換ではありません。あなたは使用する必要があるbashzshまたはシェルは、支持プロセス置換を。私はそれに応じて私の答えを編集しました。
Totor

私のために働いていませんでした。私の機能ブランチは、マスターにマージされる前に、最初に別のブランチにマージされたと思います。これが、コマンドが「Merge branch 'feature_branch'」ではなく「Merge branch 'release_branch'」になる理由を説明できます。
Tim Kuipers

14

私はこれを行う必要があり、どういうわけか見つかりましたgit-when-merged(実際にはこのSOの質問を参照していますが、Michael Haggertyが非常に優れたPythonスクリプトへの参照をここに追加したことはありません)。だから今私は持っています。


私は実際に彼のページからこの質問に行きました。
Flavius

12

ゴーティエの素晴らしい答えに基づいてcomm、リストを比較するためにを使用する必要はありません。--ancestry-pathもにある最後の結果を探しているので--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入力がソートされていない場合は機能しませんでした。
hIpPy 2017

4

Rubyの群衆のために、git-whenceがあります。非常に簡単。

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

4

私はパスに配置した以下のbashスクリプトを使用します~/bin/git-find-merge。これは、Gauthierの回答evilstreakの回答に基づいており、コーナーケースを処理するための微調整はほとんどありません。comm入力がソートされていない場合にスローされます。grep -f完璧に動作します。

コーナーケース:

  • コミットがブランチの最初の親パスでのマージコミットの場合は、コミットを返します。
  • commitがブランチの最初の親パスでの非マージコミットの場合、ブランチを返します。これはffマージまたはコミットのいずれかであり、ブランチのみにあり、正しいコミットを理解するための良い方法はありません。
  • コミットとブランチが同じ場合は、コミットを返します。

~/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のアイデアのルビーバージョンは、2倍速く動作します(これは非常に古いコミットを検索するときに重要です)。

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

このようなものを試すことができます。アイデアは、すべてのマージコミットを反復処理し、そのうちの1つからコミット "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
オイゲン・Konkov

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.