Skip to content

Instantly share code, notes, and snippets.

@chrisdaaz
Last active April 1, 2022 19:47
Show Gist options
  • Save chrisdaaz/74d87b04581a63eb7d0e08a5ba901078 to your computer and use it in GitHub Desktop.
Save chrisdaaz/74d87b04581a63eb7d0e08a5ba901078 to your computer and use it in GitHub Desktop.
Git History lesson for "Git and GitHub for Librarians" course

Git History

Pretty much everything we've discussed in the course so far has focused on the forward progress of a project. What if we want or need to revisit an earlier version to "undo" some changes we made?

It is first important to note that Git does not have a traditional 'undo' system like those found in a word processing application. It will be beneficial to refrain from mapping Git operations to any traditional 'undo' mental model. Additionally, Git has its own nomenclature for 'undo' operations that it is best to leverage in a discussion. This nomenclature includes terms like reset, revert, checkout, clean, and more.

Git works as a timeline management utility. Commits are snapshots of a point in time or points of interest along the timeline of a project's history. Additionally, multiple timelines can be managed through the use of branches. When 'undoing' in Git, you are usually moving back in time, or to another timeline where mistakes didn't happen.

git checkout

When you have found a commit reference to the point in history you want to visit, you can utilize the git checkout command to visit that commit. Git checkout is an easy way to “load” any of these saved snapshots onto your computer. To checkout a previous commit, you can use the git log --oneline command to view the hash references to your previous commits, then use the git checkout command to reload that specific version on your computer, which would look something like this:

git checkout a1e8fb5

The latest commit of your branch is called the HEAD, representing it's place in the commit timeline. When you git checkout a previous commit, you version of the project files no longer reference the HEAD, they reference the commit you checked out. This creates a "Detached HEAD" state because the HEAD stays at the most recent commit and the files within your working directory are now the version of the files that existed at the time of the commit you checked out.

Personally, this can be a little stressful because you don't want to be in a "Detached HEAD" state for very long. I would only recommend checking out a previous commit if you really need to check on the state of specific files at the particular time. Once you're doing examining the project at that commit, return to the HEAD commit before making any new commits. An advanced use case for checking out a previous commit might be to start a new branch from that specific point in the project's history. Are there other use cases you can think of for checking out previous commits?

Here's a visualization of the detached head state:

Checkout Previous Commit

Let's try this out with the branching repository you made for week two.

  1. Open the branching repository on your computer in your terminal.

  2. View the log of that repository's history: git log --oneline

_You should see a list of hashes and commit messages after running that command. This command will open the log in a terminal-based text editor called Vi. Exiting Vi after viewing the log requires you to enter the letter q in the terminal in order to return to the terminal shell. Fun fact: Vi (and its successor Vim) are notoriously difficult text editors to work with. This "How do I exit the Vim editor?" is one of the all-time most popular posts on StackOverflow, a website where people get help with code.

  1. Using one of those hashes (a hash is unique and will look something like 91cb610), checkout a previous commit: git checkout <hash>

You should now see a text warning in your terminal informing you of being in a "detached HEAD" state. You can look at files, compile the project, run tests, and even edit files without worrying about losing the current state of the project. Nothing you do in here will be saved in your repository.

To return back to where you started before visiting the previous commit, checkout the main branch again: git checkout main

git reset

Let's say we made two recent commits that we regret, and we want to undo those commits. We could use the git reset command to reset our HEAD at an earlier point in time. For example, using git log --oneline, I found that I need to go back three commits to 56f19ef and I don't care to save the commits I made in between because they are garbage. I can run git reset --hard 56f19ef to effectively "undo" every commit I made after that point in history. Now when I run git log --oneline, those commits are gone and I'm back to that point in time (pre-garbage commits). This approach is fine if your commits only exist on your computer and are not part of a shared remote repository's history on GitHub. Pushing your changes to a remote after resetting to a previous commit will throw an error because the histories will be different between your version and the remote. It's better to use git revert when undoing commits that also exist on remote repositories.

git revert

An alternative to git reset is git revert, which does not remove commits from your history but rather creates new commits in your history that are in the inverse of what you just did. This is essentially the same as "undoing" a commit. Reading the history looks messy with revert commits, but it's a more accurate description of the actual history of the project (because you're not acting like certain commits never happened) and will work fine when the commit's you're reverting also exist on a remote git repository on GitHub.

Feel free to mess around with the branching repository on your computer as much as you want. Since you pushed your branch to the https://github.com/chrisdaaz/branching repository, you can safely delete the entire branching folder on your computer and re-clone your specific branch with this command:

git clone --branch <your-branch-name> git@github.com:chrisdaaz/branching.git

The --branch option for the git clone command lets you specific specific branches you want to clone to your computer, as opposed to the default main or master branch of the repository.

Yes, it's OK to sometimes get so frustrated with Git you decide to delete the git repo on your computer and clone it from your GitHub repository. Just be sure that everything you need is backed-up. You don't want to accidentally delete files you listed in you .gitignore file!

git rm

A common question when getting started with Git is "How do I tell Git not to track a file (or files) any more?" The git rm command is used to remove files from a Git repository. It can be thought of as the inverse of the git add command.

The git rm command can be used to remove individual files or a collection of files. The primary function of git rm is to remove tracked files from the Git index. Additionally, git rm can be used to remove files from both the staging index and the working directory.

Remove test.md from a git repository:

git rm test.md

Remove /test folder from a git repository:

git rm -r test

On Unix-based systems (and Git Bash on Windows), the rm command is used to remove a file from the filesytem (the rm -r command is used to remove a folder). The git rm command combines the effect of the rm command with the appropriate event in the Git history. In other words, git rm test.md will first remove the test.md file from the filesystem and then add that removal event to the staging area. The full remove event will be recorded to the git history on the next commit.

Credits

This Lab is an adaptation of the "Undoing Changes" section of the Become a Git Guru tutorial by Atlassian, licensed under Creative Commons Attribution 2.5 Australia License.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment