While there are plenty of wrong answers, there is no single right answer. Note, however, that git rebase
works by copying some commits, essentially,1 doing a series of git cherry-pick
operations to make each copy. It is in general difficult to cherry-pick a merge, so git rebase
simply doesn't bother: it just skips merges.2 This may be exactly what you want.
Suppose, for instance, that this is a literal transcription / accurate diagram of the commit graph, or at least the interesting part of it:
a--b--c--d--e--f--g--h--i--j <-- dev
\ \ \
x--y--z--q--r--s <-- feature
(except of course that each commit node has a big ugly hash ID instead of a simple one-letter name). If we copy the effect of x
, y
, q
, and s
to four new commits that add on to commit j
, putting them on a new branch, and rename the old feature
to old-feature
, we get:
x'-y'-q'-s' <-- new-feature
/
a--b--c--d--e--f--g--h--i--j <-- dev
\ \ \
x--y--z--q--r--s <-- old-feature
These four copies can be made by running git cherry-pick
on the four IDs for commits x
, y
, q
, and s
, after checking out commit j
using the new branch name new-feature
.
Note that git rebase
chooses which commits to copy using, in essence, git rev-list <limit-hash>..HEAD
, where <limit-hash>
is whatever you give git rebase
as its <upstream>
argument. Choosing commit j
suffices as this excludes the entire a-b-c-d-e-f-g-h-i-j
chain from the selected commits. We simply need HEAD
to identify commit s
, as it will if we check out commit s
using our new name.
Once git rebase
finishes the copying, it moves the current branch's name so that it points to the last-copied commit, i.e., s'
. So we don't even need a new name at all: we can just git checkout feature && git rebase dev
and Git will copy the desired set of commits.
This does make the assumption that the graph we have drawn here is accurate. Also, note that the way git cherry-pick
works is to diff each commit against its parent. That's why it's difficult to cherry-pick a merge: a merge commit has two, or sometimes even more, parents; which one should we diff against? (Git lets you provide an answer: git cherry-pick
gives you the -m
option to pick which parent to use if the commit is a merge. But we want, at least for this experiment, to throw away the merges, and git rebase
does that for us.)
The drawbacks to rebasing, especially of long and detailed feature branches, are that we have to copy a lot of commits and we may get a lot of conflicts; and then anyone else who has the original commits needs to stop using them and switch to our new copies. There is a twisty maze of fiddly little steps that can go wrong. The advantage is that we get a simplified, cleaner history in the end, at least if we get all the fiddly maze of steppy twists, er, maze of steppy fiddles of twisty ... oh no, I've been eaten by a grue!
1Some rebases literally use git cherry-pick
, and some do something equivalent instead.
2You can cherry-pick a merge, sort of; and rebase has the option to "preserve" merges; but cherry-picking a merge produces an ordinary non-merge commit, and it's not useful to attempt it in the first place, in the cases for which this is intended. Instead, a "merge preserving" rebase re-performs the merges, which has its own pitfalls. But none of that really applies here.