Read more

Git: Restore

Julian
September 29, 2022Software engineer at makandra GmbH

tl;dr

git checkout is the swiss army of git commands. If you prefer a semantically more meaningful command for restoring tasks, use git restore instead.

With this command you can ...

  • ... do unstaging - git restore --staged
  • ... discard staged changes - git restore --staged --worktree
  • ... discard unstaged changes - git restore
  • ... restore deleted files - git restore
  • ... restore historic versions - git restore --source
  • ... recreate merge conflicts - git restore --merge
  • ... specifiy how unmerged paths are treated by using --ours, --theirs or --ignore-unmerged
  • ... choose selectively which diffs are restored - git restore --patch

You can use git restore since git 2.23.

Unstaging

You want to exclude changes for the next commit, you can do this with git restore --staged.

echo '3.1.2' > .ruby-version
echo 'Some Changes' >> README.md

git status
# Changes not staged for commit:
#   modified: .ruby-version
#   modified: README.md

git add README.md .ruby-version

# We only want to commit .ruby-version

git status
# Changes to be committed:
#   modified: .ruby-version
#   modified: README.md

git restore --staged README.md

git status
# Changes to be committed:
#   modified: .ruby-version
#
# Changes not staged for commit:
#   modified: README.md

Hint

You can also use the short version git restore -S.

Info

For this use case you can also use git reset.

Discard Changes

Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

You're implementing a new feature and after some try and error you want to discard some changes. You can do this with git restore.

Discard Staged Changes

echo '3.1.2' > .ruby-version
git add .ruby-version

git status
# Changes to be committed:
#   modified: .ruby-version

# Now we want to undo the change
git restore --staged --worktree .ruby-version

git status
# nothing to commit, working tree clean

Hint

You can also use the short version git restore -S -W.

Discard Unstaged Changes

echo '3.1.2' > .ruby-version

git status
# Changes not staged for commit:
#   modified: .ruby-version

# Now we want to undo the change
git restore .ruby-version

git status
# nothing to commit, working tree clean

Info

For this use case you can also use git checkout.

Discard changes selectively

If you decide that you only want to discard specific changes (e.g. you decide to change some implementations details), you can use the --patch (short: -p) flag to discard changes selectively, i.e. it allows you whether you want to restore a specific diff or not.

git restore -p

diff --git app/models/test.rb
index eg3c1k1..843c0a2 31143
--- app/models/test.rb
+++ app/models/test.rb
@@ -19,6 +19,10 @@ module RoutingFilter
       
         path = Rails.root / 'app'
 
+        if true
+            
+        end
+
         return path
         
(1/1) Discard this hunk from worktree [y,n,q,a,d,e,?]? ?

y - discard this hunk from worktree
n - do not discard this hunk from worktree
q - quit; do not discard this hunk or any of the remaining ones
a - discard this hunk and all later hunks in the file
d - do not discard this hunk or any of the later hunks in the file
e - manually edit the current hunk
? - print help

You can pass the path for selectively restoring changes in this (file) path as well. When the path is omitted it will check for applied change in the complete repository.

Restore Changes affected by Merge Conflicts

If you encounter merge conflicts, you have several options how to use restore.

Recreating the initial Merge Conflict

When you just resolved some merge conflicts (e.g. by using git rebase or git merge) and are not sure if you made the right merge decisions, you can use the --merge (short -m) flag to recreate the conflicted state as in the unmerged path.

If you haven't yet finished the complete merge you might want to use --ignore-unmerged

Only picking theirs or ours Changes

If you want to restore and have unmerged paths, you can specify if you want to keep your or the incoming changes by using the either --ours or --theirs.

Ignoring unmerged paths

If you want to ignore unmerged paths which would be affected by restore, you can set --ignore-unmerged.

Restore a Deleted File

You have deleted a file and then realize you still need it. You can restore the file with git restore.

rm -f README.md

ls README.md
# ls: cannot access 'README.md': No such file or directory

git restore README.md
ls README.md
# README.md

Info

For this use case you can also use git checkout.

Restore Historic Versions

You want to see if a bug was caused by certain changes, you can restore a history version of a file, e.g. from the master branch. You can do this with git restore --source.

git branch -a
# * your-feature-branch
#   master

# restore a file from master
git restore --source=master my_file.rb

# restore a file from an older version of master
git restore --source=master~5 my_file.rb

# restore a file from a commit hash
git restore --source=commit-hash my_file.rb

Hint

You can also use the short version git restore -s.

Info

For this use case you can also use git checkout.


Sources

Also see

Julian
September 29, 2022Software engineer at makandra GmbH
Posted by Julian to makandra dev (2022-09-29 17:24)