|
|
Subscribe / Log in / New account

Merging one commit at a time

Merging one commit at a time

Posted Jun 18, 2019 16:14 UTC (Tue) by epa (subscriber, #39769)
Parent article: Rebasing and merging in kernel repositories

I sometimes rebase instead of merging because it is less error-prone (for me and my workflow). I can review each commit as it is applied, and resolve conflicts at the smallest level of granularity. But then, as the article notes, you have an incompatible history with anything that has been previously published.

Is there a way to get the best of both worlds? Can I ask to merge a branch into mine a single commit at a time? That is, for each commit X on the other branch that's not already an ancestor of my branch, individually 'git merge X', resolving conflicts if necessary. Then go on to the next commit in the other branch, and so on to the end. The revision graph will become spaghetti, but you have individually reviewed each change rather than having to resolve conflicts as a big lump, and at the same time preserved the history of your branch. (If there were long sequences of commits that applied without conflicts, they could be replaced with a single merge commit merging in the last one of the sequence.)

Is there a git tool that will do this?


to post comments

Merging one commit at a time

Posted Jun 18, 2019 17:21 UTC (Tue) by mathstuf (subscriber, #69389) [Link] (2 responses)

Maybe this tool is what you're looking for? https://github.com/mhagger/git-imerge

Merging one commit at a time

Posted Jun 18, 2019 18:27 UTC (Tue) by epa (subscriber, #39769) [Link] (1 responses)

Yes, the 'rebase-with-history' goal sounds like what I am talking about. Thanks.

Merging one commit at a time

Posted Jun 21, 2019 15:43 UTC (Fri) by andrewsh (subscriber, #71043) [Link]

Mercurial implements rebase-with-history in a very interesting way I would love Git to adopt at some point.

https://www.mercurial-scm.org/doc/evolution/user-guide.html

Merging one commit at a time

Posted Jun 19, 2019 9:45 UTC (Wed) by nilsmeyer (guest, #122604) [Link]

Interesting question, off the top of my head I would think you could start another branch and then cherry-pick in the order you need.

Merging one commit at a time

Posted Jun 19, 2019 16:51 UTC (Wed) by iabervon (subscriber, #722) [Link] (1 responses)

Remember that git is able to use local branches however you'd like to come up with the information you need. You can reparent a local copy of the public branch in order to figure out the correct merge resolution, and then, when you do the merge that's appropriate for the project history, you can compare against the right answer that you worked out separately.

There's nothing saying you can't do the error-prone merge, and, before pushing it, amend it to the error-free result you figured out some other way.

Merging one commit at a time

Posted Jun 19, 2019 17:14 UTC (Wed) by epa (subscriber, #39769) [Link]

That's right. (What I sometimes do is to merge first, then make a rebased version of the same changes, and check they are the same.) But this does still lose some information in that you have a monster merge commit whose exact origins aren't clear. Sometimes it may be better to "show your working" by resolving the conflicts with the first part of your patch series (leaving a revision which is meaningful in itself, can be built and tested), then apply the second part of your patch series, etc. Rebase achieves this, but at the expense of losing history and annoying anyone who is referring to the branch (possibly including myself). The tool suggested above looks like the best of both worlds.

Merging one commit at a time

Posted Jun 19, 2019 19:19 UTC (Wed) by rweikusat2 (subscriber, #117920) [Link] (8 responses)

It's pretty simple, actually. Assuming you have two somehow identified commits, eg, tags, one representing and old version of something which is already incorporated in your code and one denoting a new version, the command
git rev-list --reverse "$old".."$new"
can be used to get a list of all commits between old and new in chronological order. These can then be applied one-by-one via
git cherry-pick "$commit"
I have a fairly simple (77 lines) script for this which aborts in case of a merge conflict after putting a tag with a magic name on the last 'clean' single commit merge. After resolving the conflict, the script can just be rerun. It'll find the last successful cherry-pick with the help of the tag, delete the tag and continue merging the remaining commits in the sequence.

Merging one commit at a time

Posted Jun 19, 2019 20:40 UTC (Wed) by mbunkus (subscriber, #87248) [Link] (3 responses)

Isn't that exactly what "git rebase … --onto …" with "git rebase --continue" after fixing conflicts does? With slightly different branch setups, of course.

Merging one commit at a time

Posted Jun 19, 2019 21:13 UTC (Wed) by rweikusat2 (subscriber, #117920) [Link] (2 responses)

It's more or less what cvs -j... -j... update does in order to incorporate changes from a vendor branch. git rebase saves a set of local modifications and reapplies these on top of a new upstream commit. The outcome (after fixing conflicts) as a branch whose history has been changed as if the local modifications had been applied on top of the new commit. In the other case, it a modified branch with upstream updates merged into it, ie, no history change.

Merging one commit at a time

Posted Jun 20, 2019 12:19 UTC (Thu) by mbunkus (subscriber, #87248) [Link] (1 responses)

I was thinking along these lines: using your nomenclature of $old..$new as the range of commits you want to cherry-pick on top of your current branch (let's call it "work"), you could do:

git checkout -b $new work-with-old-to-new
git rebase --onto work ${old}~

The second command will

1. reset the current branch ("work-with-old-to-new") to the revision given with "--onto", meaning "work" and "work-with-old-to-new" will point to the same commit,
2. take the commits in range $old..$new and cherry-pick each of them on top of the current branch (still "work-with-old-to-new")
3. If any of the cherry-pick attempts fails, git will pause rebasing, let you fix the situation (resolve conflichts, git add, git commit…) and then you run "git rebase --continue" and git will continue cherry-picking where it was interrupted.

The result is that your new branch "work-with-old-to-new" will now contain all commits from the range $old..$new on top of what's already present in "work". And that sounds pretty much like what you described.

This procedure has nothing to do with "upstream" or with which branch "work" tracks.

Merging one commit at a time

Posted Jun 20, 2019 14:16 UTC (Thu) by rweikusat2 (subscriber, #117920) [Link]

You can obvioulsly invert the inverted operation to end up with a more-or-less similar outcome. But the result will then be a trashed upstream branch as that has been rebased onto a work branch. Now, please don't inform me that one could branch the upstream branch solely for the reason to have a branch to trash. I understand that as well. But it's not the intended outcome which is integrate changes from the upstream branch into the work branch in a manageable fashion.

Merging one commit at a time

Posted Jun 19, 2019 21:18 UTC (Wed) by epa (subscriber, #39769) [Link] (1 responses)

You suggest cherry-picking commits. But that loses the history, at least in a form that git can later use automatically. A new commit is created with the same changes but the original cherry-picked commit does not become an ancestor. Indeed, ‘git rebase’ can be considered a way of cherry-picking a series of commits.

That’s why I asked about a tool which, instead of cherry-picking, does a merge at each step, leaving a revision that shows what happened and can in turn be merged automatically with others that might have done a similar thing.

Merging one commit at a time

Posted Jun 20, 2019 14:21 UTC (Thu) by rweikusat2 (subscriber, #117920) [Link]

I was assuming git could keep track of this as subversion can. If it can't, that's something which will need to be investigated and eventually fixed.

Merging one commit at a time

Posted Jun 20, 2019 9:48 UTC (Thu) by geert (subscriber, #98403) [Link]

Instead of rev-list, you can also use cherry:
git cherry -v $old $new
This will show all new commits, prefixed with a - or +, indicating if they are or are not yet present in the old branch. Then you can cherry-pick the ones prefixes with +. (yes, this is the same as what git rebase --onto does).

Merging one commit at a time

Posted Jun 23, 2019 17:21 UTC (Sun) by nix (subscriber, #2304) [Link]

This seems like a more verbose and error-prone way of doing what git cherry-pick $a..$b already does. git cherry-pick has supported ranged operations for many years now: you should probably think about discarding tools that reimplement that feature.

(You can tell if the merge failed via the exitcode of the git cherry-pick: git cherry-pick --abort then gets you back to a consistent state.)


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds