Colin
Colin

Reputation: 1162

Finding merge commits from tag across branches in git

My product is a plugin for IntelliJ. I support several versions of the underlying IntelliJ platform, and release builds of my plugin for each one since their APIs often change between versions. It's just me working on it, so I develop in master and then maintain a branch for each of the other versions. So my repo looks like this:

   1.6.0  1.6.1-eap1
.... a---b---c--- master
      \       \
       d-------e--- idea-2017.1
        \       \
         f-------g--- idea-2016.3
          \       \
          ...     ...  etc etc

a is a stable release, and has been tagged with 1.6.0. c is an EAP (beta) release, and has been tagged with 1.6.1-eap1. This scheme works fine for these two cases.

Occasionally I'd like to create a dev build which doesn't go into a release channel, but that users can download manually and test out if they like. I'd like to produce a dev build for each platform, since dev users could be using any IntelliJ version. The best way I can think of would be to create a branch for the dev build from, say, tag 1.6.0 (commit a), and then corresponding branches from commits d, f and so on which I can merge the dev branch into and create dev builds from.

Assuming I want to write a script to create and maintain these branches, how can I find commits d, f and so on from the tag 1.6.0 to create the dev build branches from?

Upvotes: 11

Views: 381

Answers (3)

max630
max630

Reputation: 9258

For the problem "find in between a ang g earliest commits from idea-2016.3 which was not in idea-2017, and earliest in idea-2017 which was not in master" solution would be to use command:

git log --oneline --source --ancestry-path  master idea-2017 idea-2016.3 --not 1.6.0

output is, in history same to yours:

cf32d9f idea-2017 e
88f264c idea-2016.3 g
5bc9fa1 idea-2017 d
3f460fe idea-2016.3 f
224cac8 master c
67620cd master b

Then find latest line marked with idea-2016.3 and latest one marked with idea-2017, that would be your commits.

Note that the output might be not desirable if there was some blocker issue due to version differences in for example f which was fixed in subsequent f'. So I would still consider explicit tagging.

Upvotes: 1

Colin
Colin

Reputation: 1162

Here's what I ended up doing:

#!/bin/sh

set -e  # Automatically abort if any simple command fails

die () {
    echo >&2 "$@"
    exit 1
}

[ "$#" -eq 2 ] || die "Usage: $0 <branch name> <tag>"

tag_commit=$(git rev-list --abbrev-commit -n 1 $2)
[ "${tag_commit}" = "" ] && die "No commit found for $2"

child_commit() {
  git log --graph --pretty=format:"%h %p" --decorate -20  --first-parent $1 | grep $2 | cut -c 3-10
}

branch_2017_1=$(child_commit idea-2017.1 $tag_commit)
[ "${branch_2017_1}" = "" ] && die "No commit found for idea-2017.1"

branch_2016_3=$(child_commit idea-2016.3 $branch_2017_1)
[ "${branch_2016_3}" = "" ] && die "No commit found for idea-2016.3"

branch_2016_2=$(child_commit idea-2016.2 $branch_2016_3)
[ "${branch_2016_2}" = "" ] && die "No commit found for idea-2016.2"

branch_2016_1=$(child_commit idea-2016.1 $branch_2016_2)
[ "${branch_2016_1}" = "" ] && die "No commit found for idea-2016.1"

git branch "$1" $tag_commit
git branch "$1-2017.1" $branch_2017_1
git branch "$1-2016.3" $branch_2016_3
git branch "$1-2016.2" $branch_2016_2
git branch "$1-2016.1" $branch_2016_1

Now I'll just update my merging and release preparation scripts to optionally accept a branch series name, and I think I'll be good.

The secret sauce is in the child_commit function, which is cribbed from here.

Upvotes: 3

lfn3
lfn3

Reputation: 1

Probably the easiest way is just to maintain a list of the supported versions/branch names in the shell script?

The reason I'm thinking this is if you add another change:

1.6.0      eap1 eap2
.... a---b---c---h--- master
      \       \   \
       d-------e---i--- idea-2017.1
        \       \   \
         f-------g---j--- idea-2016.3
          \       \   \
          ...     ...  etc etc

You want that change (h) merged into the head of the branches shown in your diagram, i.e h is merged to e and g. The branch 2017.1 would have been pointing at e which is the right commit to merge onto.

You could do something with walking back to find a common ancestor or something but I think that's needlessly complex and doesn't really buy you anything.

Plus if you just keep a list of branches you can stop supporting old versions relatively easily.

Upvotes: 0

Related Questions