Removing unstaged changes to your individual files in Git

Monday, August 15, 2022 at 11:08 AM | 5 min read

Last modified on Friday, June 5, 2026 at 2:04 PM

#git, #git checkout, #git restore, #git staging, #git switch, #git index

Groot and Octocat reading a newspaper

Photo by Praveen Thirumurugan on unsplash.com

Table of Contents

This post was originally published in August 2022 and updated in June 2026 with current Git information.

Reverting to a version of a file before it was modified using git checkout prior to Git 2.23

Have you ever worked on project files, made changes which you have not yet committed, and then wanted to revert to the version of the file before you had made those changes? I know I have!

Removing the unstaged changes to your individual files is actually a simple process. Perhaps you don’t want to git reset all your modified files. Perhaps you just want to revert the files individually.

Let’s say I made changes in a project, and I wanted to revert to the latest version of a file before I had made the current changes to it. My targeted file name is "index.html", and it is located at the root of my project, which we can call "example-portfolio-site-namecheap". I make some markup changes to "index.html", and get something like the following back in the Terminal console after making the changes:

git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: index.html Changes not staged for commit: (use "git add <file>..." to update what will be committed) modified: index.html Untracked files: (use "git add <file>..." to include in what will be committed) portfolio.html

Then, before actually staging that file or committing those changes, if I want to revert to its previous state (before those changes were made), I would simply run the following command:

git checkout index.html

Checking out "index.html" results in "index.html" file restoration. This means that "index.html" would be restored to its last committed state.

If I checked to see if the file reverted to its previous state by running git status, I would get something like the following back:

On branch master Your branch is up to date with 'origin/master'. nothing to commit, working tree clean

If I made changes in several files, and wanted to revert to the previous state of more than one file, I could do the following:

git checkout index.html join.html # I get back the following in the Terminal console: Updated 2 paths from the index

Here, I removed the unstaged changes from the files called "index.html" and "join.html" all at once.

The meaning of "Updated 2 paths from the index"

But what does "Updated 2 paths from the index" mean? There actually is something called a Git "index", a binary file residing in ".git/index" with a sorted list of path names, each including permissions and the SHA-1 of a blob object (file). For example, in my local (and remote) repository called "example-portfolio-site-namecheap", I have the following content inside my ".git" folder:

. HEAD description info refs .. ORIG_HEAD hooks logs COMMIT_EDITMSG config index objects

The ".git" folder contains a file called "index" (".git/index"). If you want to view the contents of "index" in a human-readable way, you would run the git ls-files command. git ls-files simply outputs the file names of the project. git ls-files outputs the following for my "example-portfolio-site-namecheap" index:

# git ls-files .gitignore .stylelintrc about.html favicon.ico images/alexander-stanishev-lT5QahSnruU-unsplash.webp images/aman-jakhar-vakM4byYz2Y-unsplash.webp images/boxed-water-is-better-6aZp4_KfXT8-unsplash.webp images/boxed-water-is-better-km8IZ4xX9vA-unsplash.webp images/bryan-garces-IXUM4cJynP0-unsplash.webp images/burst-aoN3HWLbhdI-unsplash.webp images/caleb-jones-J3JMyXWQHXU-unsplash.webp images/cedric-vt-ILffJKYd1eA-unsplash.webp images/clark-van-der-beken-chcyjyRQV74-unsplash.webp images/daniel-roe-lpjb_UMOyx8-unsplash.webp images/daniil-komov-0LHKGHV982w-unsplash.webp images/dorothea-oldani-aIdp1RsXeaQ-unsplash.webp images/dorothea-oldani-taPsJHnNJ_8-unsplash.webp images/eberhard-grossgasteiger-y2azHvupCVo-unsplash.webp images/hans-veth-ZtsoAKpbsjk-unsplash.webp images/james-wainscoat-0mV5Vqs9BB8-unsplash.webp images/jason-leung-wHddViTmSvA-unsplash.webp images/john-spofford-CDODVpRt3K0-unsplash.webp images/joshua-daniel-6904Y06M2R4-unsplash.webp images/joshua-earle-ZMcLVBi9xx4-unsplash.webp images/meric-tuna-DpbFm4N2BXs-unsplash.webp images/patrice-bouchard-OeYUyI7jWwc-unsplash.webp images/peter-lloyd-M-7eSVirG54-unsplash.webp images/richard-lee-IB1nXa8ASuk-unsplash.webp images/saeed-lajami-5Lh-mrpbXZk-unsplash.webp images/sebastian-pichler-sblp4evk2gs-unsplash.webp images/sneha-cecil-o6is0IIEXnY-unsplash.webp images/steve-harrris-kAmNlU_b1G4-unsplash.webp images/vincent-van-zalinge-vUNQaTtZeOo-unsplash.webp index.html join.html package-lock.json package.json portfolio.html resume.html scripts/hamburger.js scripts/hover.js scripts/lazy-load.js scripts/link-highlight.js styles/join.css styles/main.css styles/portfolio.css styles/quote-cloud.css styles/resume.css

The "index" lists what is being tracked in the repository (project). And the git ls-files -s command lists all files tracked, including object name, mode bits, and stage number in the output. For example:

100644 e86d016a35847caa2d33f921aa78d1b4d6b18f6c 0 .gitignore

The number 100644 is the mode bit, e86d016a35847caa2d33f921aa78d1b4d6b18f6c is the latest SHA-1 (blob object hash)1 that the ".gitignore" file is part of, 0 is the stage number, and ".gitignore" is the file (object) name.

The two mode bits in Git

There are two mode bits in Git. One is represented by 644, and the other by 755. They represent unix permissions. Just like we have permissions for users on our macOS with these values. 644 means the owner of the file can read/write, group/others can read only, and 755 means read and execute access for everyone and also write access for the owner of the file. In my project, I have only 644 permissions.

Stage numbers in Git

The stage numbers, represented here by 0, are used for handling merge conflicts. According to the article Git: Understanding the Index File by Mincing Huang (this information was very difficult to find),

Slot 0: “normal”, un-conflicted, all-is-well entry.

Slot 1: “base”, the common ancestor version.

Slot 2: “ours”, the target (HEAD) version.

Slot 3: “theirs”, the being-merged-in version.

So a stage number of 0 is good!

And ".gitignore" is self-explanatory. The name of the file.

Reverting to a version of a file before it was modified using git restore

As of version 2.23, Git introduced the git restore and git switch commands. Together they were meant to take over some of git checkout's multi-tasking. Prior to Git 2.23, git checkout would update the working directory to match the version of a file in the index or the file's last commit in the current branch. This command did not switch branches and only targeted the specified file. What was good about git checkout is that it only restored specific files without affecting the rest of the current branch or other files in the working directory.2

What wasn't good about git checkout was that it handled multiple tasks with the same command. It switched branches, i.e. git checkout <branch-name>, restored files, i.e. git checkout index.html, or restored files from a specific commit, i.e. git checkout <commit-hash> -- <file>.

Git 2.23 introduced the git restore and git switch commands to address the ambiguity of the git checkout command.

git switch is specifically used to switch from one branch to another. I personally have never used this command. I still use git checkout remove-excess-backticks-mdx-format-other-file-types, for example.

If I were to use git switch instead, I would run git switch remove-excess-backticks-mdx-format-other-file-types, for example.

If I wanted to create a new branch called "fix-projects-page-structure" and then switch into it, I would run git switch -c fix-projects-page-structure. With git checkout, I would run git checkout -b fix-projects-page-structure.

Both commands have names that explicitly describe their purpose. git checkout did not. It was pretty vague.

These days, when I run the git status command after making changes in my working directory, something like the following is returned:

git status On branch remove-excess-backticks-mdx-format-other-file-types Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: data/blog/removing-unstaged-changes-to-your-individual-files-in-git.mdx no changes added to commit (use "git add" and/or "git commit -a")

git commit -a stages and commits all modified and deleted tracked files in one fell swoop before committing. However, -a does not stage new, untracked files. So you have to explicitly git add new files first so git commit -a can include them in the commit.

But if I want to restore "data/blog/removing-unstaged-changes-to-your-individual-files-in-git.mdx" to its previous commit before any of the current changes were made, as indicated above, I could use the git restore data/blog/removing-unstaged-changes-to-your-individual-files-in-git.mdx, and "data/blog/removing-unstaged-changes-to-your-individual-files-in-git.mdx" no longer would appear under "Changes not staged for commit".

Conclusion

Prior to Git 2.23, users such as myself used the git checkout command for a variety of purposes: switching branches, restoring individual modified files in the current working directory or specific commit, or even creating new branches with the -b option before checking out into them. Git 2.23 made things clearer and simpler. It introduced the git restore and git switch commands. git restore restores a modified file to its previous commit state. git switch switches from one branch to another. Both commands were named to clearly convey their intent.

Footnotes

  1. SHA-1 stands for Secure Hash Algorithm 1. In the context of Git 2.23, it is a cryptographic hash function which creates unique identifiers for objects within Git repositories.

  2. In Git, the working directory refers to the local directory where a project tracked by Git resides. It acts as a workspace where I edit, add, or delete files before staging and committing them to the local directory's repository.