Imagine the following history:
c---e---g--- feature
/ \
-a---b---d---f---h--- master
How can I find when commit "c" has been merged into master (ie, find merge commit "h") ?
Imagine the following history:
c---e---g--- feature
/ \
-a---b---d---f---h--- master
How can I find when commit "c" has been merged into master (ie, find merge commit "h") ?
Your example shows that the branch feature
is still available.
In that case h
is the last result of:
git log master ^feature --ancestry-path
If the branch feature
is not available anymore, you can show the merge commits in the history line between c
and master
:
git log <SHA-1_for_c>..master --ancestry-path --merges
This will however also show all the merges that happened after h
, and between e
and g
on feature
.
Comparing the result of the following commands:
git rev-list <SHA-1_for_c>..master --ancestry-path
git rev-list <SHA-1_for_c>..master --first-parent
will give you the SHA-1 of h
as the last row in common.
If you have it available, you can use comm -1 -2
on these results. If you are on msysgit, you can use the following perl code to compare:
perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' file1 file2
(perl code from http://www.cyberciti.biz/faq/command-to-display-lines-common-in-files/ , which took it from "someone at comp.unix.shell news group").
See process substitution if you want to make it a one-liner.
master
was merged into feature
, then immediately feature
is merged into master
as a fast-forward (tip of feature
replaces master
). Would that cause --first-parent
to return the wrong parent? –
Danner comm -1 -2
but it didn't work. comm
only works on sorted lines. (The perl one-liner works, although I couldn't read it.) –
Disherison git find-merge h master
(returns nothing but should return h), git find-merge d master
(returns f but should return d), git find-merge c feature
(returns e but should return g). –
Beecham git log <SHA-1_for_c>..master --ancestry-path --merges --oneline | tail -1
so far, I always get the answer I'm looking for. Under what condition might that not work? –
Encephalitis This will however also show all the merges that happened after h, and between e and g on feature.
I don't understand how that can be when according to the graph h
comes after e
and g
. –
Chancey Add this to your ~/.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'"
Then you can use the aliases like this:
# current branch
git find-merge <SHA-1>
# specify master
git find-merge <SHA-1> master
To see the merge commit's message and other details, use git show-merge
with the same arguments.
(Based on Gauthier's answer. Thanks to Rosen Matev and javabrett for correcting a problem with sort
.)
sort -k2 | uniq -f1 -d | sort -n | tail -1 | cut -f2
is not finding correctly the last row in common. Here's an example where it fails. –
Tinishatinker 16db9fef5c581ab0c56137d04ef08ef1bf82b0b7
here when I run it on your paste, is that not expected? What OS are you on? –
Gish 29c40c3a3b33196d4e79793bd8e503a03753bad1
–
Tinishatinker sort -n
with sort -k 1,1 -n
? –
Gish fedora
. –
Gish -k2
) applying a byte-based tiebreaker, not preserving the input-order when the second field (commit-hash) is equal. This behaviour can be halted in a number of ways: the -s|--stable
switch does this - if you add it to the sort for @RosenMatev's paste, it will sort as-expected i.e. result is 16db9fef5c581ab0c56137d04ef08ef1bf82b0b7
not 29c40c3a3b33196d4e79793bd8e503a03753bad1
. This result could also be achieved by changing that sort to sort -k2,2 -k1,1n
. Which is correct - original cat+cat order, or sorted numerically? –
Friend -s
is more appropriate in this case, as otherwise we're mixing two different orderings. –
Gish git show-merge
give you that git show merge [themergecommit]
won't? –
Fordham git log merge-commit-hash^-
–
Gish Your example shows that the branch feature
is still available.
In that case h
is the last result of:
git log master ^feature --ancestry-path
If the branch feature
is not available anymore, you can show the merge commits in the history line between c
and master
:
git log <SHA-1_for_c>..master --ancestry-path --merges
This will however also show all the merges that happened after h
, and between e
and g
on feature
.
Comparing the result of the following commands:
git rev-list <SHA-1_for_c>..master --ancestry-path
git rev-list <SHA-1_for_c>..master --first-parent
will give you the SHA-1 of h
as the last row in common.
If you have it available, you can use comm -1 -2
on these results. If you are on msysgit, you can use the following perl code to compare:
perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' file1 file2
(perl code from http://www.cyberciti.biz/faq/command-to-display-lines-common-in-files/ , which took it from "someone at comp.unix.shell news group").
See process substitution if you want to make it a one-liner.
master
was merged into feature
, then immediately feature
is merged into master
as a fast-forward (tip of feature
replaces master
). Would that cause --first-parent
to return the wrong parent? –
Danner comm -1 -2
but it didn't work. comm
only works on sorted lines. (The perl one-liner works, although I couldn't read it.) –
Disherison git find-merge h master
(returns nothing but should return h), git find-merge d master
(returns f but should return d), git find-merge c feature
(returns e but should return g). –
Beecham git log <SHA-1_for_c>..master --ancestry-path --merges --oneline | tail -1
so far, I always get the answer I'm looking for. Under what condition might that not work? –
Encephalitis This will however also show all the merges that happened after h, and between e and g on feature.
I don't understand how that can be when according to the graph h
comes after e
and g
. –
Chancey git-get-merge will locate and show the merge commit you're looking for:
pip install git-get-merge
git get-merge <SHA-1>
The command follows the children of the given commit until a merge into another branch (presumably master) is found.
git get-merge <SHA-1> origin/master
–
Wilkens That is, to summarize Gauthier's post:
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
EDIT: because this uses process substitution "<()
", it is not POSIX compatible, and it may not work with your shell. It works with bash
or zsh
though.
<()
is not POSIX compatible. You need to use bash
, zsh
or a shell supporting process substitution. I edited my answer accordingly. –
Inamorata I needed to do this, and somehow found git-when-merged
(which actually references this SO question, but Michael Haggerty never added a reference to his very nice Python script here). So now I have.
Building on Gauthier's great answer, we don't need to use comm
to compare the lists. Since we're looking for the last result in --ancestry-path
which is also in --first-parent
, we can simply grep for the latter in the output of the former:
git rev-list <SHA>..master --ancestry-path | grep -f <(git rev-list <SHA>..master --first-parent) | tail -1
Or for something snappy and reusable, here's a function to pop into .bashrc
:
function git-find-merge() {
git rev-list $1..master --ancestry-path | grep -f <(git rev-list $1..master --first-parent) | tail -1
}
comm
did not work when the inputs were not sorted. –
Beecham git log --topo-order
Then look for the first merge before the commit.
For the Ruby crowd, there's git-whence. Very easy.
$ gem install git-whence
$ git whence 1234567
234557 Merge pull request #203 from branch/pathway
I use below bash script which I place at path ~/bin/git-find-merge
. It's based on Gauthier's answer and evilstreak's answer with few tweaks to handle corner cases. comm
throws when the inputs are not sorted. grep -f
works perfectly.
Corner cases:
~/bin/git-find-merge
script:
#!/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
Which lets me do this:
(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
This script is also available on my github.
My ruby version of @robinst's idea, works twice faster (which is important when searching for very old commit).
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
You can just use it like this:
ruby find-commit.rb SHA master
This is the best way to find the commit in master:
[alias]
find-merge = !"f() { git rev-list $1..master --ancestry-path --merges --reverse | head -1; }; f"
find-merge = !"f() { git rev-list $1..master --ancestry-path --merges | tail -1; }; f"
. –
Humility You can try something like this. The idea is to iterate through all merge commit and see if the commit "c" is reachable from one of them:
$ 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
–
Incondensable I've had to do this several times (thanks to everyone that answered this question!), and ended up writing a script (using Gauthier's method) that I could add to my little collection of git utilities. You can find it here: https://github.com/mwoehlke/git-utils/blob/master/bin/git-merge-point.
A graphical solution is to find it in gitk (using the “SHA1 ID” box) and follow the line of commits up to the merge commit.
© 2022 - 2024 — McMap. All rights reserved.