1

I have a git repo that has a series of commits, which include merges (from branches).

What are the best practices for reverting these commits while maintaining the history? For example:

$ git log --oneline
I (HEAD -> development) Some commit msg
H - Another commit msg
G - Yet another commit msg
F Merged [featurebranch] into development
E - An earlier commit msg
D Merged [earlierfeaturebranch] into development
C - Some other commit msg
B - First commit after initial
A - Initial commit

Now, I thought (incorrectly) that if I wanted to revert back to the state of repo at commit B, that I would just need to do:

$ git revert -n B..HEAD

This is wrong, because git will have a fit about this:

$ git revert -n B..HEAD
error: commit F is a merge, but no -m option was given.

Simply doing

$ git revert -n -m 1 B..HEAD

doesn't seem to work because then there's reports of conflicts regarding files that have been removed (added since commit B). That doesn't sound right, since it shouldn't matter, I just want to get back to the state of commit B and push that back to the origin.

What exactly is missing (or being glossed over here)?

I suppose a cheap way of doing this would be to checkout commit B, back that up, and then checkout development and replace the content of development with B and push that, but that doesn't sound right at all.

Gorlon
  • 23
  • 5
  • Is any of all this pushed to a remote you share with someone else or is this all private? If it is your own private repo (or if it still is locally) then quite a few steps which might otherwise be needed can be skipped. – Kim Oct 20 '22 at 06:21

1 Answers1

0

If you want to go back to the state in commit B, but maintain the history, then make sure you have the branch you wish to modify checked out with a clean status, and then you can do this:

git reset --hard B
git reset --soft I
git commit -m "Set state back to commit B"
git push

That will make a new commit (call it J) that has the same state as commit B, but with I as its parent instead of A.

Regarding your idea:

I suppose a cheap way of doing this would be to checkout commit B, back that up, and then checkout development and replace the content of development with B and push that, but that doesn't sound right at all.

Conceptually that's spot on, and the above commands are essentially doing that, but in a "gitty" way that doesn't require a separate backup.

Side Note: if you didn't care about the history you would instead just use:

git reset --hard B
git push --force-with-lease
TTT
  • 22,611
  • 8
  • 63
  • 69
  • 1
    Or in the new days, no need to move around: `git restore --staged --worktree --source=B -- .; git commit "taking it back to B"` – eftshift0 Oct 19 '22 at 18:51
  • @eftshift0 nice! But yours requires typing more characters. :D – TTT Oct 19 '22 at 19:02
  • @eftshift0 also, I know you know this, but when you say "no need to move around", it's the changing of the worktree that takes the energy, not resetting the commit pointer, so saving the extra reset is negligible. Not applicable to this question, but when you can save changing the worktree, that's when it becomes really slick. For example, something like [this answer](https://stackoverflow.com/a/66850396/184546) to how to commit to another branch. – TTT Oct 19 '22 at 19:12