TL;DR: yes, your command sequence is correct.
You can shorten it slightly using git rebase experimental
.
Long
Use git checkout
or git switch
to select the commit at the tip of develop
:
git switch develop
You now have that commit as the current commit, with that branch name as the current branch name; I like to draw this as:
...--G--H <-- develop (HEAD)
\
I--J <-- feature
where the uppercase letters stand in for commit hash IDs.
Now you can create a new branch name experimental
, and make it the current branch:
git switch -c experimental # or git checkout -b experimental
producing:
...--G--H <-- develop, experimental (HEAD)
\
I--J <-- feature
A new commit you make now advances the name experimental
:
K <-- experimental (HEAD)
/
...--G--H <-- develop
\
I--J <-- feature
It's now necessary to copy commits I
and J
to new, slightly-different commits that use commit K
as the parent of the copy of I
, and use the copy of I
as the parent of J
. The git rebase
command does this kind of copying.
If you give git rebase
a branch
argument, it runs an initial git switch
(or git checkout
) on that argument. I find this unwise (there's a bug recently discovered on the Git mailing list, in this area) and prefer to do my own separate git switch
, but you can run:
git rebase --onto experimental develop feature
to eliminate the separate git checkout feature
step. I wouldn't, as I said; I would run git switch feature
first.
The set of commits that git rebase
will copy starts with a list of commits as generated by git log
or git rev-list
:
git rebase [--onto <target>] <upstream>
uses the current branch as the source of these commits, and upstream..HEAD
as the initial list of commits that might potentially be copied.1
Since your upstream
argument is either develop
or experimental
, the list will be generated by:
git log develop..HEAD
or:
git log experimental..HEAD
If you try both commands at this point in the process, you will see that they produce the same output. The reason is that the left side of the two-dot operator, A..B
, tells Git what commits not to use. The right side tells Git what commits to use, minus any overridden by the left side. Each of these operations works by finding the last commit, as named by the hash ID or branch name or HEAD
name, and working backwards.
The last commit on feature
is J
, and the next-to-last is I
, and the one behind that is H
, and so on. The exclusion list either starts at K
, then works back to H
, and continues backwards from there, or it starts at H
and works backwards from there. Commit K
isn't in the inclusion list, so if it is in the exclusion list, this is "free": nothing happens one way or another.
This means you can use either experimental
or develop
as the upstream
argument to git rebase
. Both have the same effect.
If you use the --onto
argument to git rebase
, the commit hash ID or name you supply after the literal word --onto
sets the target for the rebase operation. If you omit it, the upstream
sets the target.2 The target
that you want in this case is experimental
.
Since experimental
works as an upstream
argument, the command:
git rebase experimental
suffices, and you don't need a separate --onto
.
The git rebase
process will now run, copying each commit one at a time, in the correct order, using Git's internal detached HEAD mode:
I' <-- HEAD
/
K <-- experimental
/
...--G--H <-- develop
\
I--J <-- feature
after copying I
to I'
, and then:
I'-J' <-- HEAD
/
K <-- experimental
/
...--G--H <-- develop
\
I--J <-- feature
after copying J
to J'
. Merge conflicts can occur during the copying of each commit, but if they don't, Git will handle this all on its own. Finally, once both commits are copied, Git will yank the branch name feature
over to point to the last-copied commit, and re-attach the special name HEAD
:
I'-J' <-- feature (HEAD)
/
K <-- experimental
/
...--G--H <-- develop
\
I--J ???
and the rebase is complete.
1Specific commits may sometimes be subtracted away from this list, during or after generating the list. In your case, none will be subtracted away, so we get to ignore this, and just use the list that you'll see if you run git log
.
2There's another trick involving --fork-point
if you omit both names, but let's not get into that as it gets complicated.