I recently created a branch off master to revert a very large commit.
I first created a branch off master then,
I used git revert "commit tag from gitlab"
,
but I have a lot of merge conflicts now and would like to resolve them so that only the Head changes or from what I understand, resolve the merge so the changes from the large commit are excluded in the merge. The idea is to create a branch that has every commit after the faulty commit and then merge it with master after so that the faulty commit is excluded.
so if F was the bad commit the new branch would be:
Master: A->B->F->C->D
Hot fixed Branch: A->B->C->D
Thank you!
CodePudding user response:
How to:
- revert a commit in git which was a few commits back
- remove a single commit in git, and
- squash multiple commits in git using the
git reset --soft
technique
Scenario:
master
: A->B->F->C->D
You want to remove or revert commit F and end up with:
hot_fixed_branch
: A->B->C->D
A note about manually resolving conflicts
If you run git revert F
, the reason you have conflicts is because changes in C and/or D have touched the same lines as changes from F. Therefore, there's no clean way to just "undo F". If you try to undo F, it will also undo parts of C and D, so you have to manually resolve those conflicts to keep the desired parts of C and D while removing the content of F from those same lines.
While you're in the middle of resolving the git revert F
conflicts, you can optionally try the following:
# Option 1:
# keep only the changes expressed by `git diff F..HEAD`, meaning: the changes
# done by C and D
git checkout --ours
# Option 2:
# keep only the changes expressed by `git diff F..F~`, meaning: the changes
# which are **the opposite of F**, thereby exactly undoing F
git checkout --theirs
Read also my git revert
section here, in case that helps you make sense of it: Who is "us"/"ours" and "them"/"theirs" according to Git?.
However, if you do Option 1 above, you'll still retain some changes from F, since C and D were built on top of F. And, if you do Option 2 above, it may break or remove changes added by C and D, since they were built on top of F. One of the two options above may get you close to what you want, but no matter which you choose, you're still going to break something. That's why the conflicts exist in the first place! The existence of conflicts tells you that C and/or D touched the same lines as F.
So, the best thing to do is to open a new terminal and run this while resolving all conflicts, so you can at least see what was added by F, and be able to then manually make sense of it and undo it properly as you manually resolve conflicts:
- Ensure
meld
is your difftool. See my instructions here: How to use meld as your git difftool in place of git diff. Meld is a great way to compare changes in a GUI. - Run this to see the changes created by F, so you can know how to manually undo them as you resolve conflicts:
# see the changes added by F git difftool F~..F # OR, to specify just a particular folder to look at git difftool F~..F -- path/to/dir # OR, to specify just a particular file to look at git difftool F~..F -- path/to/file # OR specify a couple files git difftool F~..F -- file1 file2 # etc.
The various ways to remove or undo commit F
If any of the git commands below don't make sense, read and study all my references. I literally had those tabs open all at once to write this answer myself, referencing all of my references live while writing this answer.
There are multiple ways to do this.
- Use
git revert F
(you'll end up withA->B->F->C->D->F'
, whereF'
is the opposite ofF
, thereby undoingF
's changes):# create and check out hot_fix from B git checkout -b hot_fix B # Now revert F git revert F # manually fix conflicts, then commit them git add -A git revert --continue
- If
git revert F
is too hard to resolve, then try simply cherry-picking C and D onto B instead (you'll end up withA->B->C->D
):# create and check out hot_fix from B git checkout -b hot_fix B # Now cherry-pick C and D onto it git cherry-pick C D # manually resolve conflicts as necessary, and continue, as often as # necessary git add file1.c file2.c # etc. git cherry-pick --continue
- Doing the cherry-pick just above may require resolving the same conflicts multiple times, so it is easier to squash C and D first, and then cherry-pick the squashed result onto B (you'll end up with
A->B->E
, whereE
is a commit containing the changes ofC
andD
squashed into a single commit):# Create and check out a "master_copy" branch from "master" so you don't # mess up master git checkout -b master_copy master # squash C and D on "master_copy" into a single commit git reset --soft C~ git commit -m "Squashed commit containing both C and D" # Now check out "hot_fix" from B and cherry-pick that squashed # commit we just made onto it git checkout -b hot_fix B git cherry-pick master_copy # manually resolve conflicts, then commit them git add -A # or git add file1.c # etc. # Once done fixing and adding ALL files, then do: git cherry-pick --continue
References:
- My answer on Who is "us"/"ours" and "them"/"theirs" according to Git?
- My answer on Various ways to create a branch in git from another branch
- My answer on How to cherry-pick a single commit, multiple commits, or a range of commits
- How to do the "
git reset --soft
way" to squash commits. Search my git & Linux cmds, help, tips & tricks - Gabriel.txt document from my eRCaGuy_dotfiles repo for`git reset --soft` way
- How to use meld as your git difftool in place of git diff