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 has multiple functionalities,
Here for undo, we will try the first option. This command doesn't make any changes to the history.
Test Setup
1rm -rf .git *.*2
3git init4echo "Committed" > fileA.txt5git commit -a -m "Initial Commit"6
7echo "Stage this" >> fileA.txt8git add fileA.txt9
10echo "Changes in working directory" >> fileA.txt
git checkout -- fileA.txt
git checkout -- .
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
In the below image,
Below options can be used to reset specific subfolders,
git checkout -- path/
git checkout HEAD -- path/
git checkout -- .
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
1rm -rf .git *.*2
3git init4echo "Line 1" > fileA.txt 5git add .6git commit -m "Initial Commit"7
8echo "Line 2" >> fileA.txt9echo "Line 1" > fileB.txt10git add .11git commit -m "Second Commit"12
13echo "Line 3" >> fileA.txt14echo "Line 2" >> fileB.txt15echo "Line 1" > fileC.txt16git add .17git commit -a -m "Third Commit"18
19echo "Line 4" >> fileA.txt20echo "Line 3" >> fileB.txt21echo "Line 2" >> fileC.txt22git commit -a -m "Fourth Commit"23
24echo "Line 5 - Staged" > fileA.txt25git add fileA.txt26echo "Line 4 - Working directory" >> fileB.txt27echo "Line 5 - Working directory" >> fileC.txt
This is how it looks after running the setup
1Initial Commit | fileA.txt2 | ---------3 | Line 14
5Second Commit | fileA.txt | fileB.txt 6 | --------- | ---------7 | Line 1 | Line 18 | Line 2 |9
10Third Commit | fileA.txt | fileB.txt | fileC.txt11 | --------- | --------- | ---------12 | Line 1 | Line 1 | Line 113 | Line 2 | Line 2 |14 | Line 3 | |15
16Fourth Commit | fileA.txt | fileB.txt | fileC.txt17 | --------- | --------- | ---------18 | Line 1 | Line 1 | Line 119 | Line 2 | Line 2 | Line 220 | Line 3 | Line 3 |21 | Line 4 | |22
23# Current state 24--------------------------------------------------------------------------25Working directory | Staged26---------------------------------------------------------| -------27fileB.txt | fileC.txt | fileA.txt28--------- | ---------- | ---------29Line 1 | Line 1 | Line 5 - Staged30Line 2 | Line 2 |31Line 3 | Line 5 - Working directory |32Line 4 - Working directory | |
Same as show above
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".
We run our setup scripts again and this is how it looked at second commit
1Second Commit | fileA.txt | fileB.txt 2 | --------- | ---------3 | Line 1 | Line 14 | 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". 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
.
We run our setup scripts again and then run git reset --hard HEAD~2
.
Recovering from git reset --hard
From the above output, we know, we lost data from
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
1git reset --hard2
3# '-f' is force, '-d' is remove directories.4git 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
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.
Setup
1rm -rf .git *.*2
3git init4echo "one" > onetwothreefour.txt5echo "two" >> onetwothreefour.txt6echo "three" >> onetwothreefour.txt7echo "four" >> onetwothreefour.txt8git add .9git commit -m "Adding file onetwothreefour.txt"10
11sed -i 's/two/2/' onetwothreefour.txt12touch newfile.txt13git add . 14git commit -m "Update two to 2 and added new file"15
16sed -i 's/four/4/' onetwothreefour.txt17git commit -a -m "Update four to 4"
With git revert hash --no-edit
it doesn't start editor, basically you are accepting the default commit message for revert.
Setup
1rm -rf .git *.*2
3git init4echo "abc" > abc.txt5git add .6git commit -m "Added abc in abc.txt"7
8echo "def" >> abc.txt9git commit -a -m "Added def in abc.txt"10
11echo "ghi" >> abc.txt12dd if=abc.txt of=output.txt conv=ucase13rm abc.txt14mv output.txt abc.txt15echo "123" >> 123.txt16git add .17git commit -m "Added ghi, CAPS in abc.txt and 123 to 123.txt"18
19echo "JKL" >> abc.txt20echo "456" >> 123.txt21sed -i 's/123/onetwothree/' 123.txt22git commit -a -m "Added JKL in abc.txt and 456 to 123.txt"23
24echo "MNO" >> abc.txt25echo "789" >> 123.txt26git commit -a -m "Added MNO in abc.txt and 789 to 123.txt"27
28Sushanth@Sushanth-VAIO MINGW64 /d/GITs/test429$ git lol30* e561d30 - (HEAD -> master) Added MNO in abc.txt and 789 to 123.txt (9 seconds ago) <Sushanth Bobby Lloyds>31* 81cf3dd - Added JKL in abc.txt and 456 to 123.txt (9 seconds ago) <Sushanth Bobby Lloyds>32* efada58 - Added ghi, CAPS in abc.txt and 123 to 123.txt (10 seconds ago) <Sushanth Bobby Lloyds>33* 7ace0d0 - Added def in abc.txt (11 seconds ago) <Sushanth Bobby Lloyds>34* 54f77e1 - Added abc in abc.txt (11 seconds ago) <Sushanth Bobby Lloyds>
Current state of both the files
1$ cat abc.txt | $ cat 123.txt2ABC | onetwothree3DEF | 4564GHI | 7895JKL |6MNO |
After git revert HEAD~2
. Below are the current state of the files.
1$ cat abc.txt | $ cat 123.txt2ABC | 4563DEF | 7894GHI |5JKL |6MNO |
123.txt file
abc.txt
<<<<<<< HEAD
and =======
its what in the HEAD=======
it what before HEAD~2After updating the file, it looks like this,
1$ cat abc.txt2abc3def4GHI5JKL6MNO7
8# follow the git instructions9$ git status10On branch master11You are currently reverting commit efada58.12 (fix conflicts and run "git revert --continue")13 (use "git revert --abort" to cancel the revert operation)14
15Unmerged paths:16 (use "git reset HEAD <file>..." to unstage)17 (use "git add/rm <file>..." as appropriate to mark resolution)18
19 deleted by them: 123.txt20 both modified: abc.txt21
22no changes added to commit (use "git add" and/or "git commit -a")
Since we have updated file abc.txt and removed all the git markers. We will have to add and revert continue
1Sushanth@Sushanth-VAIO MINGW64 /d/GITs/test4 (master|REVERTING)2$ git add .3warning: LF will be replaced by CRLF in 123.txt.4The file will have its original line endings in your working directory.5
6# Once you enter the below command, it opens the default editor, i am going with default message, so :wq7Sushanth@Sushanth-VAIO MINGW64 /d/GITs/test4 (master|REVERTING)8$ git revert --continue9[master ed6bbb7] Revert "Added ghi, CAPS in abc.txt and 123 to 123.txt"10 1 file changed, 3 insertions(+), 3 deletions(-)11
12Sushanth@Sushanth-VAIO MINGW64 /d/GITs/test4 (master)
Setup
1rm -rf .git *.*2
3git init4echo -n "abc" > abc.txt5git add .6git commit -m "Added abc in abc.txt"7
8echo -n "def" >> abc.txt9git commit -a -m "Added def in abc.txt"10
11echo -n "GHI" >> abc.txt12git commit -a -m "Added ghi in abc.txt"13
14echo -n "JKL" >> abc.txt15git commit -a -m "Added JKL in abc.txt"16
17echo -n "MNO" >> abc.txt18git commit -a -m "Added MNO in abc.txt"19
20Sushanth@Sushanth-VAIO MINGW64 /d/GITs/test4 (master)21$ cat abc.txt22abcdefGHIJKLMNO23
24Sushanth@Sushanth-VAIO MINGW64 /d/GITs/test4 (master)25$ git lol26* 193f710 - (HEAD -> master) Added MNO in abc.txt (11 seconds ago) <Sushanth Bobby Lloyds>27* ba22751 - Added JKL in abc.txt (12 seconds ago) <Sushanth Bobby Lloyds>28* 3207be7 - Added ghi in abc.txt (12 seconds ago) <Sushanth Bobby Lloyds>29* 713ab79 - Added def in abc.txt (13 seconds ago) <Sushanth Bobby Lloyds>30* 1d3ed23 - Added abc in abc.txt (13 seconds ago) <Sushanth Bobby Lloyds>
Here we will try to revert commit "3207be7" which added string "ghi".
1$ git revert HEAD~22error: could not revert 3207be7... Added ghi in abc.txt3hint: after resolving the conflicts, mark the corrected paths4hint: with 'git add <paths>' or 'git rm <paths>'5hint: and commit the result with 'git commit'6
7Sushanth@Sushanth-VAIO MINGW64 /d/GITs/test4 (master|REVERTING)
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
1$ cat abc.txt2abcdefJKLMNO3
4Sushanth@Sushanth-VAIO MINGW64 /d/GITs/test4 (master|REVERTING)5$ git status6On branch master7You are currently reverting commit 3207be7.8 (fix conflicts and run "git revert --continue")9 (use "git revert --abort" to cancel the revert operation)10
11Unmerged paths:12 (use "git reset HEAD <file>..." to unstage)13 (use "git add <file>..." to mark resolution)14
15 both modified: abc.txt16
17no changes added to commit (use "git add" and/or "git commit -a")18
19Sushanth@Sushanth-VAIO MINGW64 /d/GITs/test4 (master|REVERTING)20$ git add .21
22Sushanth@Sushanth-VAIO MINGW64 /d/GITs/test4 (master|REVERTING)23$ git revert --continue24[master 8a42d6f] Revert "Added ghi in abc.txt"25 1 file changed, 1 insertion(+), 1 deletion(-)26
27Sushanth@Sushanth-VAIO MINGW64 /d/GITs/test4 (master)
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
git reset --hard
Thats all the undo's i have for now.
git rm
or git reset
they will be in there dangling. Its not easy to find them.1git checkout HEAD -- fn.ext2git checkout -- .3git reset --hard4git clean -f