Undoing Changes and Time Travelling in Git

Photo by Zulfa Nazer on Unsplash

Undoing Changes and Time Travelling in Git

Checkout

  • The git checkout command is like a Git Swiss Army knife. Many developers think it is overloaded, which is what lead to the addition of the git switch and git restore commands

  • We can use checkout to create branches, switch to new branches, restore files, and undo history!

  • We can use git checkout <commit-hash> to view a previous commit. We just need the first 7 digits of a commit hash which can be obtained through git log --oneline. Doing so will result in a Detached Head. Now the git log will change because we have moved back in time

  • git checkout supports a slightly odd syntax for referencing previous commits relative to a particular commit.

    • HEAD~1 refers to the commit before HEAD (parent)

    • HEAD~2 refers to 2 commits before HEAD (grandparent

    git checkout HEAD~1

Discarding Changes

  • Suppose you've made some changes to a file but don't want to keep them. To revert the file back to whatever it looked like when you last committed, you can use:

    • git checkout HEAD <filename> to discard any changes in that file, reverting back to the HEAD.

Restore

  • git restore is a brand new Git command that helps with undoing operations. git restore was introduced alongside git switch as alternative to some of the uses for checkout.

  • Suppose you've made some changes to a file since your last commit. You've saved the file but then realize you definitely do NOT want those changes anymore!

  • To restore the file to the contents in the HEAD, use git restore <file-name>

  • git restore <file-name> restores using HEAD as the default source, but we can change that using the --source option.

  • For example, git restore --source HEAD~1 home.html will restore the contents of home.html to its state from the commit prior to HEAD. You can also use a particular commit hash as the source.

  • git status will always remind you what to use!

    Unstaging Files with Restore

    • If you have accidentally added a file to your staging area with git add and you don't wish to include it in the next commit, you can use git restore to remove it from staging.

    • Use the --staged option like this:

        git restore --staged <file-name>
      

Git Reset

  • Suppose you've just made a couple of commits on the master branch, but you actually meant to make them on a separate branch instead. To undo those commits, you can use git reset.

  • git reset <commit-hash> will reset the repo back to a specific commit. The commits are gone but the changes will be kept in the working directory.

  • If you want to undo both the commits AND the actual changes in your files, you can use the --hard option.

  • For example, git reset --hard HEAD~1 will delete the last commit and associated changes.

Git Revert

  • git revert is similar to git reset in that they both "undo" changes, but they accomplish it in different ways.

  • git reset actually moves the branch pointer backwards, eliminating commits.

  • git revert instead creates a brand new commit which reverses/undos the changes from a commit. Because it results in a new commit, you will be prompted to enter a commit message.

  • The history of the commit is preserved but the changes are reverted

      git revert <commit-hash>
    

Which Command To Use?

  • Both git reset and git revert help us reverse changes, but there is a significant difference when it comes to collaboration.

  • If you want to reverse some commits that other people already have on their machines, you should use revert.

  • If you want to reverse commits that you haven't shared with others, use reset and no one will ever know!

Theory of Git Checkout

  • Usually, HEAD points to a specific branch reference rather than a particular commit. The HEAD is a pointer to the current branch reference and the branch reference is a pointer to the last commit made on a particular branch

  • When we make a new commit, the branch reference is updated to reflect the new commit. The HEAD remains the same because it's pointing at the branch reference. When you switch branches, HEAD is updated to point to the specific branch reference. This is all to say that HEAD usually refers to a branch NOT a specific commit.

  • When we checkout a particular commit, HEAD points at that commit rather than at the branch pointer

    Detached Head

    You have a couple of options:

    1. Stay in detached HEAD to examine the contents of the old commit. Poke around, view the files, etc.

    2. Leave and go back to wherever you were before - reattach the HEAD

       git switch master
      
    3. Create a new branch after checking out the old commit and switch to it. You can now make and save changes since HEAD is no longer detached.

      1. Suppose you want to go back to an old commit and make some new changes

      2. Checkout the old commit. Now in detached HEAD state.

      3. While in detached HEAD, Make a new branch and switch to it. Head is now back to pointing at a branch reference!

      4. Now on the new branch, make as many new commits as you want! It's like you time-traveled! We went back to an old commit and made a new branch based on it.

        git checkout <d8194d6>
        git switch -c newbranch
        git add .
        git commit -m "new commit"

Did you find this article valuable?

Support Mustafa's Blog by becoming a sponsor. Any amount is appreciated!