git rebase --onto works with tags?

55 views Asked by At

After some thinking I think I have a similar case of rebase as the one in the documentation although with other branch names.

enter image description here

Here the docs say to apply

git rebase --onto master next topic

to get

enter image description here

In my case I have a branch called develop where master in the figure is, and a branch called MyFeature where topic is in the figure. In the point where "next" is I don't have a branch but I have a tag "OLDdevelop"

Can I apply git rebase --onto here without expressly creating a branch in the "next" position?

2

There are 2 answers

0
knittl On BEST ANSWER

Yes, you can use git rebase with tags (any ref really: tags, branches (branch heads), commits). So something like

git rebase --onto tag1 tag2 mybranch

is totally fine. Even git rebase --onto tag1 tag2 somecommit works, but you will end up in detached head state.

The full form of git rebase is:

git rebase --onto <newbase> <upstream> <branch>

The man page contains this about --onto:

--onto <newbase>

Starting point at which to create the new commits. If the --onto option is not specified, the starting point is <upstream>. May be any valid commit, and not just an existing branch name. [emphasis mine]

and about <upstream>:

<upstream>

Upstream branch to compare against. May be any valid commit, not just an existing branch name. Defaults to the configured upstream for the current branch. [emphasis mine]

A tag (usually) points to a commit, so you can use a tag in place of a commit. The tag will be peeled to reach the commit. Git calls these types of refs commit-ish:

commit-ish (also committish)

A commit object or an object that can be recursively dereferenced to a commit object. The following are all commit-ishes: a commit object, a tag object that points to a commit object, a tag object that points to a tag object that points to a commit object, etc.

0
ElpieKay On
git rebase --onto <newbase> <upstream> <branch>

Here are some pseudo-commands that explain how this git rebase works.

# Checkout <branch>
git checkout ${branch}

# Reset it to <newbase>
git reset ${newbase} --hard

# Get the commits that are to be applied
commits=$(git rev-list ${upstream}..${branch})

# Apply these commits onto the newbase one by one
for commmit in $commits;do
    git cherry-pick $commit
done

It doesn't matter if the 3 parameters are branches, tags, commits, or other commit-ish in the above pseudo-commands except the first command. At all events, the commits are applied, and a new log graph is created.

git checkout $branch behaves differently whether $branch is a branch or not.

# A branch in short format
git checkout topic
git branch
* topic

# A branch in other formats, or a non-branch ref
git checkout refs/heads/topic  # a branch in long format
git branch
* (HEAD detached at refs/heads/topic)

git checkout refs/tags/foo  # a tag in long format
git checkout foo  # a tag in short format
git checkout 5b74449ba231d4750743c2b5c00ddc2535fab38e  # a commit
git checkout refs/changes/22/339822/1  # a Gerrit ref
git checkout refs/merge-requests/99/head  # a Gitlab merge-request ref

The first switches to the branch topic, and the others lead to a detached HEAD.

Say topic points at the commit a4f6f0e8cabe00011f5fc27176284c30575c136f.

Before git-rebase,

* 8a73f8b (master) e
* 0271872 d
* 5b53145 c
| * a4f6f0e (topic) n
| * 046efc5 m
| * 544582f (next) g
| * f6516cf f
|/
* 7f2ea62 b
* 42dee2c root

After git-rebase,

# Case 1, "topic" is a branch in short format
git rebase --onto master next topic

git log --oneline --graph --decorate master next topic
* 5b74449 (HEAD -> topic) n
* 05f4607 m
* 8a73f8b (master) e
* 0271872 d
* 5b53145 c
| * 544582f (next) g
| * f6516cf f
|/
* 7f2ea62 b
* 42dee2c root



# Case 2, "topic" is a non-short-formatted-branch commit-ish, tags included
git rebase --onto master next a4f6f0e8cabe00011f5fc27176284c30575c136f
git log --oneline --graph --decorate HEAD master topic next

* 5b74449 (HEAD) n
* 05f4607 m
* 8a73f8b (master) e
* 0271872 d
* 5b53145 c
| * a4f6f0e (topic) n
| * 046efc5 m
| * 544582f (next) g
| * f6516cf f
|/
* 7f2ea62 b
* 42dee2c root

Both can work. Suppose the last commit is 5b74449 after the command. In Case 1, you don't need to do extra work. In Case 2, you need to reset topic to 5b74449. Otherwise, topic still points at a4f6f0e8cabe00011f5fc27176284c30575c136f.

git checkout topic
git reset 5b74449 --hard
git log --oneline --graph --decorate master topic next

* 5b74449 (HEAD -> topic) n
* 05f4607 m
* 8a73f8b (master) e
* 0271872 d
* 5b53145 c
| * 544582f (next) g
| * f6516cf f
|/
* 7f2ea62 b
* 42dee2c root

In your case, the result is the same whether $next is a branch, a tag, a commit, or any other commit-ish.