Undos are the most important thing when you are using computers, its something, one cannot do in any other sector cheaply. Ctrl+Z
and Ctrl+Y
are something we do very often when working, its easier way to try something new and go back if something fails or doesnt work. Git has many ways to undo work and it all depends on different scenarios, below are some simple ways, i have tried and did it.
Git commands to undo changes,
- git checkout
- git reset
- git revert
- git restore
- As this is still in “THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE”, i have not explored this.
# Undo using git checkout
Git checkout has multiple functionalities,
- Updating the working directory with content from index/staging.
- It can move HEAD to another branch or a commit. If you checkout to a commit, you end up with detached HEAD.
Here for undo, we will try the first option. This command doesn’t make any changes to the history.
Test Setup
rm -rf .git *.*
git init
echo "Committed" > fileA.txt
git commit -a -m "Initial Commit"
echo "Stage this" >> fileA.txt
git add fileA.txt
echo "Changes in working directory" >> fileA.txt
git checkout -- fileA.txt
- Checkout has multiple functions by putting two hypens — you are saying you are dealing with a file
- Copy fileA.txt from staging area to working directory
git checkout -- .
- dot stands for the current directory
- overwrites all the files in the working directory.
In the below picture, you can see the after checkout file in working directory has been replaced with file from index/staging area.
git checkout HEAD -- fileA.txt
- Copies fileA.txt from that commit point in repo to staging area
- Copies fileA.txt from staging area to working directory
In the below image,
- Blue : Shows the content of fileA.txt before the command was executed.
- Green : Show the content of fileA.txt after the command was execute.
Below options can be used to reset specific subfolders,
git checkout -- path/
git checkout HEAD -- path/
- Go into the folder you want to reset and then do
git checkout -- .
# Undo using git reset
Git reset is specifically about updating your branch, moving the tip in order to add or remove commits from the branch. This operation changes the commit history.
There are three variants with git reset
-
Soft : Doesn’t touch the index/staging or work directory, but all the changes show up as changes to be committed with git status. If you re-commit, the resulting commit will have exactly the same content as where you were before you reset. So can be used to squash commits.
-
Mixed : Default. It resets the index/staging area but does not touch the working directory. Here any differences between the latest commit and the one you reset to will show up as local modifications in git status.
-
Hard : Index/Stage and working directory are updated with data what was in commit point it was reset to.
Test Setup
This is how it looks after running the setup
Same as show above
git reset —soft
This is how it looks after git reset --soft HEAD~2
HEAD~2
means we are going back 2 commits and resetting to “Second Commit”.- By comparing the before and after reset, we can say that nothing has happened to the content in staging and working directory other than we have few “Changes to be committed”.
git reset —mixed
We run our setup scripts again and this is how it looked at second commit
Second Commit | fileA.txt | fileB.txt
| --------- | ---------
| Line 1 | Line 1
| Line 2 |
Now we run git reset --mixed HEAD~2
,
HEAD~2
means we are going back 2 commits and resetting to “Second Commit”. So we are going to be losing changes that was made in “Third Commit” and “Fourth Commit”.- Index/Stage area data was reset with data what was in “Second Commit”.
- Working directory is not affected.
fileC.txt
is not found in index/staging because it was added in “Third Commit” only.
To proceed with the changes, you need to git add .
and git commit
.
git reset —hard
We run our setup scripts again and then run git reset --hard HEAD~2
.
- fileC.txt is missing.
- Index/Stage and Working directory is reset with data that was in “Second Commit”.
- “Third Commit” and “Fourth Commit” are lost but not entirely(By default Git gives you 30 days before it cleans up those orphan commits)
Recovering from git reset --hard
- Whenever tip of the branches are updated(created or cloned, checked-out, renamed, or any commits), its recorded and that mechanism is call Reflog.
- Here we use information from Reflog and again reset —hard and go back to “Fourth Commit”
From the above output, we know, we lost data from
- Index/Stage
- Working directory
Its recommended before doing any git reset --hard
, do a git stash -u
.
You also want to do bit of cleanup after after git reset --hard
like below
git reset --hard
# '-f' is force, '-d' is remove directories.
git clean -fd
Main thing to remember is “Removing specific commit is not possible to with git reset” thats what git revert is for.
# Git Revert
git revert
command is about making a new commit that reverts the changes made by other commits. This command adds new history to the project (it doesn’t modify existing history).
Below i am going show some git revert
by examples, simple ones.
Test 1 : Plan
- Creating a file with “one”, “two”, “three” and “four” in each line + doing a commit
- Updating two to 2 and adding a newfile.txt + doing a commit
- Updating four to 4
- Revert (2). What happens is
- New commit for revert
- newfile.txt goes missing
- Updates onetwothreefour.txt, replaces 2 with two
Setup
With git revert hash --no-edit
it doesn’t start editor, basically you are accepting the default commit message for revert.
Test 2 : Plan
- Creating and appending data to file
- Updating all content in one file and adding a new file.
- Make few updates and commit
- Revert (2). What happens ?
- 123.txt reverted successfully without conflicts
- abc.txt has conflicts which has to be handled manually.
Setup
Current state of both the files
$ cat abc.txt | $ cat 123.txt
ABC | onetwothree
DEF | 456
GHI | 789
JKL |
MNO |
After git revert HEAD~2
. Below are the current state of the files.
$ cat abc.txt | $ cat 123.txt
ABC | 456
DEF | 789
GHI |
JKL |
MNO |
123.txt file
- There is no conflict.
- Changes that was made in HEAD~2 were reverted, insense in the reverted commit this file was created and added data “123” in the next commit “456” was appended and “123” was updated to “onetwothree”.
abc.txt
- We have a conflict
- Between
<<<<<<< HEAD
and=======
its what in the HEAD - Below
=======
it what before HEAD~2 - We have to manually update the abc.txt file.
After updating the file, it looks like this,
Since we have updated file abc.txt and removed all the git markers. We will have to add and revert continue
Test 3 : Plan
- Concatenating string and committing changes.
- Reverting HEAD~2. What happens ? Conflicts. That has to be changed manually.
Setup
Here we will try to revert commit “3207be7” which added string “ghi”.
In the below image, three commands were executed.
cat abc.txt
: Shows with git conflict markers, first part of the marker shows whats in HEAD and second part shows what was before the reverted commit.git show HEAD~2 -- abc.txt
: Shows change that was made in that commit. From the image, we can clearly say that+abcdefGHI
was added.git diff HEAD~2 HEAD
: Show difference between HEAD~2 to HEAD.
Resolving conflicts after updating the file abc.txt
# Git Restore
git-restore is about restoring files in the working tree from either the index or another commit.
git restore abc.txt
by default will restore file abc.txt to match the version in the index.git restore --staged abc.txt
can be used to restore a file in the index to match the version in HEAD.git restore --source=HEAD --staged --worktree abc.txt
- restore both the index and the working tree
- Equivalent to
git reset --hard
Thats all the undo’s i have for now.
# Things to remember
- Don’t add large files to git, even if you
git rm
orgit reset
they will be in there dangling. Its not easy to find them. - Don’t use master/main branch for development purposes, they should have only production ready, releasable code.
- Destructive commands in git
git checkout HEAD -- fn.ext git checkout -- . git reset --hard git clean -f
# Next steps
- Branching : Git Branching
- Internals : Git Internals
- Collaboration : Git remote repository
- Git Everyday : Git flowchart, shortcuts and references
- Origin : How it all began. What is git ? and Terminologies used in this series.
- Basics : config, init, add, rm, .gitignore, commit, log, blame, diff, tag, describe, show and stash