160

Is there a way to rebase a single commit from a branch onto another branch?

I have this branch structure:

-- -- -- -- -- (Master)
            \
              -- -- -- -- -- XX (Feature-branch)

All I want to do is to rebase the last commit of Feature-branch onto master and rollback Feature-branch one commit.

-- -- -- -- -- XX (Master)
            \
              -- -- -- -- -- (Feature-branch)

How do I do that?

Lii
  • 11,553
  • 8
  • 64
  • 88
Kevin Meyer
  • 2,816
  • 4
  • 21
  • 33
  • 3
    If you can rebase any number of commits then why do you ask about rebasing a single one? If I could ask questions in SO, I would ask what is the difference between rebasing (a single commit) and cherry-picking. – Val Jun 04 '13 at 13:03
  • 16
    Because I didn't know that cherry-picking existed, and I do "Faff about on branch", "Get request for fix on different branch", "fix it", "Commit to wrong branch", "D'OH!" enough that asking the question was useful. – Kevin Meyer Apr 01 '14 at 23:38
  • Because the command syntax to rebase a single commit from a stack is far from obvious. – ChrisPhoenix Aug 26 '22 at 02:08

4 Answers4

153

You can cherry-pick XX to master.

git checkout master
git cherry-pick <commit ID of XX>

And remove the last commit from the feature branch with git reset.

git checkout Feature-branch
git reset --hard HEAD^
tewe
  • 2,707
  • 3
  • 22
  • 18
  • 102
    how can a question specifically called 'git rebase ...' have the accepted answer than contains a cherry-pick, which is a totally different concept and sometimes per se considered unclean? – Bondax Oct 13 '15 at 09:08
  • 1
    Not sure if this is relevant, but the commit I wanted to rebase had some files that were moved, and `cherry-pick` made them appear as if they were deleted from the old location and created at the new location. I suppose rebase would have taken care of that, but by now I've pushed upstream so I can't test that. In any case, beware if you have a similar situation. – waldyrious Dec 18 '15 at 10:49
  • 1
    Note: to push you changes in `Feature-branch` to origin you'll need to `git push -f origin Feature-branch` as your `Feature-branch` is now considered to be 1 commit behind the `origin/Feature-branch`. – j-i-l Sep 13 '17 at 21:47
  • 2
    What is the practical difference between this solution and [the one by CharlesB](https://stackoverflow.com/a/14635740/452775)? – Lii Oct 04 '17 at 14:21
  • 1
    @Bondax: Because they’re not totally different concepts by any means. They‘re closely enough related that if you know rebasing, but have never heard the term “cherry-picking”, it’s pretty natural to come up with the idea of cherry-picking and describe it as “rebasing a single commit”. – PLL Aug 25 '20 at 11:50
  • "remove the last commit" -> git reset --hard. ok if I want to move the last commit. and if the commit I want to rebase is not the last ? – crillion Feb 03 '23 at 16:36
  • If you want to revert to some commit do the 1st section, then run: git push -f to force it onto remore origin/main. – Stepo Apr 23 '23 at 11:51
142
git rebase --onto master branch~1 branch 

This says "rebase the range of commits between last-before-branch and branch (that is, XX commit) on the tip of master branch"

After this operation branch tip is moved on commit XX, so you want to set it back with

git checkout branch
git reset --hard branch@{1}^

Which says "reset the branch tip to the commit before its previous state"

So a cherry pick is a simpler solution...

CharlesB
  • 86,532
  • 28
  • 194
  • 218
  • 5
    This doesn't seem to work for me, I lose the commits before XX and the branch is rebased to master with a single commit, but I never used `--onto` before so I may be doing something wrong. BTW the OP said rebase but it seems like he wants to do a cherry-pick. – tewe Jan 31 '13 at 21:59
  • 1
    my error, rebase does indeed move the branch on master, it has to be reset – CharlesB Jan 31 '13 at 22:01
  • 1
    What is the practical difference between this solution and [the one by tewe](https://stackoverflow.com/a/14635752/452775)? – Lii Oct 04 '17 at 14:19
  • 1
    @Lii the only I can see is that it uses 3 steps instead of 4 – CharlesB Oct 04 '17 at 14:46
  • @Lii This one is much more generic. I had a branch where I wanted to rebase to master, and I only wanted to rebase all but the first 4 commits (let's call the 4th one X). So I only had to do: `git rebase --onto master X branch` – alx - recommends codidact Sep 21 '20 at 14:42
98

It's pretty simple to do actually. The solution is to do an interactive rebase and "drop" all of the commits you don't want to include in the rebase.

git rebase -i <target_branch> where target_branch is the branch you want to rebase on to

Then you will edit the file that is opened and pick the commits you do want and drop (or d for short) all the commits you don't want to bring along.

hrdwdmrbl
  • 4,814
  • 2
  • 32
  • 41
  • 17
    IMO a much better solution, and it actually addresses the question. – GabrielOshiro Nov 23 '17 at 23:08
  • 4
    This should be the accepted solution given how general, intuitive, and short it is. – Pablo Arias Aug 05 '20 at 14:25
  • 1
    +1 `git rebase -i` is my favourite Git command. It is so flexible and in the same time so simple and clear exactly what is happening. You don't have to memorize any other rebase command if you learn `git rebase -i`. – Lii Sep 21 '20 at 13:11
  • Let's say I use interactive rebase to drop every commit except for my latest one. Will the result be that my local branch's latest commit will appear 'on to of' the origin master branch that I rebase on? If so, if I submit a merge request for my branch, will the git history show "master.....my_branch's_last_commit"? – Samuel Dominguez Oct 19 '20 at 00:24
  • Great answer, this is a general solution since it addresses keeping ANY number of commits in the original branch, not just one, as cherry-pick. – Arthur Colombini Gusmão Aug 09 '21 at 17:11
  • 1
    Be aware that the commits you do not pick are "lost" unless they are on a different branch. Or "left behind" like git calls it. – MiniGod Oct 29 '21 at 15:33
  • just to be sure this is a tenable solution for branches that are closely synched to each other in the realm of the number of commits one would want to manually filter through right? If we're doing this operation for divergent branches the cherry pick approach looks more attractive right? – jxramos Aug 01 '22 at 19:08
0

@Charles response is correct. Anyway I ended up using this so many times, most of all to rebase specific config on a project

  * a8f9182 (HEAD -> production) production configuration
  | * daa18b7 (pre) preproduction configuration
  |/  
  | * d365f5f (local) local configuration
  |/  
  * 27d2835 (dev) amazing new feature that will save the world
* | 56d2467 (master) boring state of the art for project
|/

that I create a new command for it:

$ cat ~/bin/git-rebaseshot 
COMMIT=$1
DEST=${2:-HEAD}
git rebase ${COMMIT}^ ${COMMIT} --onto $DEST

normally you want to autocomplete branch names for that command, so add it sourcing this function (adding to .bashrc or .profile):

_git_rebaseshot () 
{ 
    __gitcomp_nl "$(__git_refs)"
}

git autocomplete will search for it

you can use this command like this:

# rebase config on prepro on actual HEAD
$ git rebaseshot prepro 
# rebase config on local onto dev
$ git rebaseshot local dev
# rebase production config on master
$ git rebaseshot pro master

When you divide features correctly, possibities are endless.

* a8f9182 (HEAD -> postgres) BBDD config
* a8f9182 (local) local config
* a8f9182 (debug) log level config
* a8f9182 (dev) new feature
|

I guess this is what quilt people like to do.

this command will work anyway with whatever sha/ref you provide:

$ git rebaseshot <Feature branch> master
$ git rebaseshot <commit of XX> master
albfan
  • 12,542
  • 4
  • 61
  • 80
  • // , Can you link to any project where we can see this in action? – Nathan Basanese Oct 30 '16 at 21:26
  • For its nature, branches avaliable for rebaseshot are not commited outside local repo. Just create several branches on top of master (log level, database connection, configuration) and use command between them. Is plain to see the effect. – albfan Oct 30 '16 at 22:46
  • // , I ran into some problems. I'll try it again. – Nathan Basanese Oct 31 '16 at 00:37