Skip to content

Instantly share code, notes, and snippets.

@pjf
Last active January 2, 2016 01:39
Show Gist options
  • Save pjf/8231962 to your computer and use it in GitHub Desktop.
Save pjf/8231962 to your computer and use it in GitHub Desktop.
The curious matter of finding a given merge

My problem:

I'm working on a project where upstream doesn't use source control of any sort. Yes, that's exactly the same reaction I had as well.

I keep track of their releases on a branch named upstream. When new releases are made, I add them to this branch as a single commit, so at least I know what changed (in bulk) from the last release.

My changes (usually!) end up in the official releases, but because they don't use source control, my commits never do. Hence I want to find all the commits I've made since the last merge, as an "approximation" of what's changed between my release and theirs. Simple git log A..B isn't what I want here.

So, here's the network graph. I want to find the last point on master where upstream was merged in. In this case, that's 9039b22b .

To make matters more fun, sometimes there's a few fix-ups done (on a side-branch of upstream) after upstream is imported, but before the merge into master. They're great for keeping the master branch clean and tidy, but make this particular problem harder. However we can rely upon 'upstream' to always point to the commit we want to search from.

Also, we can't rely upon finding just the last merge in general, because all sorts of things get merged into master (ie: contributions from every other developer who does use source control). I could try to do some sort of magic looking at text messages, but humans edit those. Likewise I don't want to use a tag which keeps moving, because moving public tags in git is evil, and having code which relies upon private tags is misguided.

Yes, this is all horribly painful. It's inspired posts on how wonderful FOSS is when done properly, and it's not been contributing to my happiness. Yes, I'm trying very hard to change processes. You can shed tears for me if you like.

@shiftkey
Copy link

shiftkey commented Jan 3, 2014

So I had a trick to get this going but the branch names are getting in my way.

When I run this command:

git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit

to get a pretty log, I see that you've got a cheeky commit between origin/upstream and when it was merged into master:

screen shot 2014-01-03 at 2 29 53 pm

So I can't use origin/upstream as the branch name to determine where it was merged in (why? more on that in a second).

So I put my local branch to this point:

git checkout -b upstream -t origin/upstream
git reset 093a7cc --hard

Which gives me this view now:

screen shot 2014-01-03 at 2 32 30 pm

I can then call this little shell script:

lastCommitInBranch=$(git rev-parse $1)

# add in whitespace here to ensure we choose the ancestor commits
search=".\s*$lastCommitInBranch"
found=$(git rev-list --merges --parents origin/master | grep $search)
if test -z "$found"
then
 echo "could not find a point where this commit was merged into master"
else 
 echo "found a child of $lastCommitInBranch"
 IFS=$'\n'; array=($(echo $found | egrep -o '"[^"]*"|\S+'))
 echo "which is ${array[0]}"
 break
fi

from the repository like this: sh ~/my-cool-script.sh upstream

And I get this output:

found a child of 093a7cc7369b7a803e87666085cb74f889ae97d2
which is 9039b22b84c3465bd179f1fb07911f784e6a5fed

How does that feel?

@ilmari
Copy link

ilmari commented Jan 3, 2014

Ah, the commit between upstream and the merge screws up A..B. What you want is the first merge commit on the ancestry path from upstream (or rather, the merge base with upstream, if there are unmerged commits on it) to master, i.e.

git log --reverse --ancestry-path --merges --format=%h upstream..master |  head -n1

(we have to use head because git log's -<n> happens before --reverse)

Edited: added missing reverse

@pjf
Copy link
Author

pjf commented Jan 3, 2014

@ilmari: Wait, did you mean tail -n1, because that certainly gives the commit that I'm after! :)

@pjf
Copy link
Author

pjf commented Jan 3, 2014

Or alternatively, was there supposed to be a --reverse in that git command? ;)

@ilmari
Copy link

ilmari commented Jan 3, 2014

@pjf: The latter, it got lost in editing. But tail works too.

@pjf
Copy link
Author

pjf commented Jan 3, 2014

@shiftkey: It feels pretty cool! I also think I'm going to need more than the three hours of sleep I've had to fully comprehend what you've done. Damn my late night coding sessions... ;)

It also teaches me the git log --graph command, which I've never really used before! That's awesome! :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment