1

I have the scenario that I would like to undo the first commit on my branch. A simple solution would be to create a new branch, then move all but the first commits from my first branch to the new one and delete the old branch.

I imagine that this can be done in a nicer way by simply bending the base of my branch. Is such a thing possible ? I try to sketch it out:

current:

master   a - b 
branch1       \ revertB - c - d

wanted:

master   a - b 
branch1       \ - c - d 

possible?:

master a - b ---------------------
skip        \ revertB              \
branch1                             \ - c - d

I don't need the revertB commit. How can i achieve that or is this bad practice ? Thanks in advance :)

scholl123
  • 117
  • 9

3 Answers3

2

git rebase --onto b c branch1

This takes all the commits on branch1 starting from c and rebases them onto b.

Think of it like cutting off a branch on a tree and re-grafting --onto some other part of the tree (b).

Git rebase is powerful. Learn to use it! Especially interactive mode (git rebase -i). For an example of its power, and to get a sense of how git rebase works, see the the todo file explained section of https://stackoverflow.com/a/61411955/8910547

Inigo
  • 12,186
  • 5
  • 41
  • 70
  • I think that is exactly what I want. I just checked an example, wouldn't it be `git rebase --onto b revertb` ? Or am I misinterpreting `git rebase --onto ` from here: https://stackoverflow.com/questions/29914052/i-cant-understand-the-behaviour-of-git-rebase-onto – scholl123 May 11 '20 at 13:53
  • Yes, you are misinterpreting. Trust me. If you want to play it safe, create a new branch at `d` and rebase it as a trial run (using my command above but the new brach ref instead of d). – Inigo May 11 '20 at 14:02
  • Okay fair enough you are the pro here :D Just trying to understand it. I'm reading that the three parameter mode is working like this: `git rebase --onto ` so that would mean i have to do it like this: `git rebase --onto b revertb d` doesn't it ? :D sorry my brain is lagging rn – scholl123 May 11 '20 at 14:08
  • 1
    Not quite. Not . It's quite simple if you drop what you have in your head and internalize the "cutting off a branch" analogy I gave in my answer. You are taking a cut branch defined by it's beginning and end points (`c d`) and *grafting ONTO* `b`. – Inigo May 11 '20 at 14:14
  • 1
    Regarding "If you want to play it safe", there's always the `git reflog` to undo any changes you did as long as you didn't push them – lucidbrot May 11 '20 at 14:25
  • Yes, your all your old commits stay around a while (weeks or months). You always see them with `git log --graph --oneline --reflog --all`. You can always repoint and branch head back to a commit it pointed to i the past. – Inigo May 11 '20 at 14:28
  • My bad. SORRY. I kept thinking `d` was the name of your branch. I'll update my answer now! sorry. And you don't have to worry about what you've don't so far... `branch1` is unaffected, and you've just created some rebased orphan commits that don't matter and will be gc'd automatically in the future. – Inigo May 11 '20 at 14:38
  • @Inigo Thank you for helping me through is. At the end I was able to get it done through the rebase -i master. But i guess your edited command would have fixed it for me too. You helped me a lot to understand 'rebase' at least a little bit :D – scholl123 May 11 '20 at 15:16
  • 1
    @scholl123 glad it worked out for you. Let's both delete our comments above that are no longer relevant given my updated answer. The long thread is confusing for future readers and the goal of SO is questions and answers that have value for future readers. – Inigo May 11 '20 at 15:30
1

"possible?"

Definitely.

One way among others to achieve it :

# create your "skip" branch
git checkout -b skip <commitHash of "revertB">

# repair branch1 by resetting to master then recreating commits c and d
git checkout -B branch1 master
git cherry-pick c d
Romain Valeri
  • 19,645
  • 3
  • 36
  • 61
  • This manually does what `git rebase` does for you automatically, as `git rebase under the covers does cherry-picks for commits you are keeping. – Inigo May 11 '20 at 13:55
  • Yes. But doing things anatomically here is nor heavier nor more complex. And it gives a clear idea of what's to be done. Both methods belong here, I'm happy we teamed to present them. – Romain Valeri May 11 '20 at 14:18
1

If you have not yet pushed anything, you can modify your history in multiple ways. If you have pushed your state, you need to consider the impact it will have!

You'll be causing trouble for any collaborators that have created commits on top of branch1 but not yet pushed them. If nobody other than you has ever used branch1, you should be fine.

I find the most intuitive way to use

git checkout branch1
git rebase -i master

This opens a text file like this. Change the commit you want to get rid of to drop as I did below. Then save the file and exit.

drop 1a432d7 revertB
pick 2657446 c
pick 8d15847 d

# Rebase 1219262..8d15847 onto 1219262 (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

Since I modified always the same line in my testing repository for this answer, I got a merge conflict. In commit b, I wrote b. In commit c, I wrote c. In commit revertB I wrote revertB.

<<<<<<< HEAD
b
=======
c
>>>>>>> 2657446... c

You'll have to fix your merge conflicts if you encounter any. In my case, I replaced the file contents with c.

Continue the rebase after staging the fixed file:

git add myfile
git rebase --continue

You're given the option to modify the commit message of commit c. Once you confirmed it, revertB is gone from branch1.

lucidbrot
  • 5,378
  • 3
  • 39
  • 68
  • 1
    This seems quite nice, can you elaborate what the `master` in `git rebase -i master` does? – scholl123 May 11 '20 at 13:57
  • @scholl123 Without whipping out the manual to double-check: You tell git to rebase your current branch "onto" some other. I.e. any commits before the one where `branch1` branched off from `master` is not considered. – lucidbrot May 11 '20 at 14:00
  • 2
    Thanks @Inigo! Your answer was first and is cleaner. I just added mine because interactive rebase allows one to not bother with the details of the flags and to point out that it's a bad idea if the branch is used by other collaborators – lucidbrot May 11 '20 at 14:18
  • I actually LOVE ♥️♥️♥️ interactive mode. If the OP wanted to skip more commits, re-order, squash, etc, you answer is the way to go! – Inigo May 11 '20 at 14:31
  • 1
    I ended up using your method, thanks to you and @Inigo ! You guys helped a lot – scholl123 May 11 '20 at 15:18