15

Assume the following graph:

A -- B -- C -- D -- E -- F
      \
       G -- H -- I

I would like to find G in order to be able to an interactive rebase to squash the commits (but still keep one commit on the topic branch) which will be reviewed and merged later. I don't want to just rebase the whole branch, because I want to keep the information that there was a branch and it was merged.

(I know that I can look at the history and just use the SHA checksum of the commit, but I'm looking for a way to do it without manually digging up the information or counting how many commits were made and use ~ from HEAD with that number).

Edit: clarification of what I want to achieve:

I want to avoid this:

A -- B -- C -- D -- E -- F -- J
      \                      /
       G -- H -- I -- -- -- -

And have something like this instead:

A -- B -- C -- D -- E -- F -- J
      \                      /
       G' -- -- -- -- -- -- -

In other words, I want to squash the commits with an interactive rebase on the topic branch into one, but still keep the branch and use a regular merge to integrate the changes from the topic branch into the master.

Tamás Szelei
  • 23,169
  • 18
  • 105
  • 180

3 Answers3

22

There are no commits “on a branch” in git. There are only commits reachable from a branch. And that information is not a very good indicator of what branch that commit originally belonged to. So I see two ways to interpret your question:

  • I want to find the first commit after the merge-base of branch and master that is on the side of branch.
  • I want to find the first commit that is reachable from branch, but not from master.

I am gonna assume you meant the second one. The answer to that is returned by this command:

 git rev-list ^master mybranch | tail -n 1

Note: This is all gonna fail horribly if you branch off topic branches.


I am a little bit confused as to what you want to achieve. If you want a whole branch to be represented by one commit, but still get a merge, You should do this:

git checkout mybranch
git reset --soft `git merge-base mybranch master`
git commit -m 'All of Mybranch'
git checkout master
git merge --no-ff mybranch

Note that this will start the squash at the first merge-base. This might not be what you want if you merged master into mybranch before. You can modify this to start at the first commit on mybranch, but not on master (might have wierd and unexpected results in complex histories) by replacing the second command with this:

 git reset --soft `git rev-list ^master mybranch | tail -n 1`^
cjs
  • 25,752
  • 9
  • 89
  • 101
Chronial
  • 66,706
  • 14
  • 93
  • 99
7

You will get B with git merge-base

git merge-base F I

Or more simply with

git merge-base my_branch my_other_branch

You can then rebase with the result

git rebase -i B

And just keep the first commit G, while squashing all the others.

cexbrayat
  • 17,772
  • 4
  • 27
  • 22
1

git merge-base will give you B. You want all direct children of B that are also ancestors of I. That's related to How do I find the next commit in git? and Referencing the child of a commit in Git . Try something like:

root=$(git merge-base master topic-branch)

git rev-list --parents ^$root topic-branch | grep " $root" | cut -f1 -d' '

This will list all the commits from the root to the tip of the topic-branch along with their parents and show the ones where the parent is the merge-base root.

Because a topic branch may contain merges there may be more than one "first" commit in a branch, topologically.

Community
  • 1
  • 1
Joe
  • 29,416
  • 12
  • 68
  • 88