Saving Changes in Git | How To & Examples
ADVERTISEMENT
Table of Contents
- How Do You Save Changes in Git?
- Background
- How Does Git Keep Track of Changes?
- Git Saving Definitions
- Where Does Git Save?
- Save Changes in Git
- How do I Save a Git Commit?
- Git Status
- Git Add Command
- The staging area
- Ignoring Untracked Files
- Git Commit
- Skipping the Staging Area
- Git Save Common Options
- Other Git Save Examples
- Removing Files
- Moving or Renaming Files
- Publishing Changes: How do I Save my Progress in GitHub?
- Stashing: How do I Save a Git Change Without Commit?
- What Happens When You Stash Changes?
- Git Stash to Save
- Does Git Automatically Save Changes?
- Things You Should Never Commit
- Quick Review of Saving Changes
- Next Steps
- References
How Do You Save Changes in Git?
There is no explicit "git save" command. Therefore, there are several ways to interpret this question. This article aims to be a comprehensive and practical guide to the various ways of saving changes in Git.
"Save early, save often!" - Unknown
Background
To understand Git saving, we must take a brief detour into the repository model. Git is a distributed version control system. When you clone or initialize a project, you have a full copy of it stored on your local disk in the hidden .git subdirectory.
File revisions are stored as Git objects known as blobs, trees, and commits. These objects are stored in each clone of a Git repository. So the more copies of your repo that exist out there, the more places your project history, file changes, and commits are persisted. When you commit locally and then push your changes to a remote repository, you are adding redundancy to your project.
How Does Git Keep Track of Changes?
We'll discuss four ways to save when working with Git:
- Save files to local disk. Think hitting the save button in Word. Git is not yet involved. Git cannot see files that haven't been saved to local disk.
- Committing to Git. This tracks the file state in the .git sub-directory, enabling viewing and restoring to old versions. The data still lives on your local hard drive, and is therefore susceptible to drive failure.
- Pushing your commit(s) to a remote repository. This allows others to collaborate with you, and saves the data in a redundant location, reducing the risk of data loss.
- Stashing changes locally. This is a temporary save point in Git that holds outstanding changes while resetting the working directory to the last commit. Useful if you need to switch branches momentarily.
Git Saving Definitions
- Working directory: The current set of files and folders that are currently checked out in your Git project.
- Staging area: A temporary cache location where Git stores your changes to be included in the next commit.
- .git directory (repository): Managed by Git, tracks repository history and state. One copy is stored in each clone of your Git repo.
- Git stash: A local cache location to store changes that you don't want to delete or commit. Leaves a clean working directory.
Where Does Git Save?
All file changes tracked by Git are stored in the .git directory, or repository. On your local machine, .git will be a sub-directory in your working directory. On a remote such as Gitlab, there is no working directory, and the .git directory exists by itself as a bare repository.
All Git objects are stored in Git's Object Database (repository) located in the hidden ".git/objects/" directory. These objects are Blobs, Trees, and Commits, and they are indexed by the SHA-1 hash of their content.
Save Changes in Git
"I guarantee you, if you put your data in Git...you can verify that the data you get back out is the exact same data you put in" - Linus Torvalds
Let's begin with the commit workflow - this is the most common way to "save" your changes using Git. What is a commit in Git? Think of it like a checkpoint of project state that you can share with others or roll back to later.
Committing is an essential prerequisite for publishing your changes to your team or the world. There are even benefits to committing if you only work locally and don't collaborate with others. Committing will allow you to track the history of the project, and to view or roll-back to a previous state.
Thanks to Git being distributed, everything short of publishing may be completed offline or with poor connectivity.
How do I Save a Git Commit?
Create a commit when you are ready to publish or otherwise record a snapshot of your work.
You must save your file changes to the working directory via the usual ways, prior to committing. i.e. press save button in text editor or type ctrl+s. Many editors, such as VS Code, support auto-saving to disk which is handy.
When you commit, the repository state is saved in the .git directory. It is still only saved on the local disk.
To commit use the git status, git add, and git commit commands as described in the following sections.
Git Status
The status command shows you the current state of the files in the working directory.
Each file in the working directory can be in one of two states: tracked, or untracked. Tracked files are simply files that were previously committed or are currently in the staging area. Untracked files were never committed (or were removed), and are not in the staging area.
Tracked files may additionally be in an unmodified, modified, or staged state.
When a repository is first cloned, all of the files will be in the tracked and unmodified state. As files are edited and saved to the working directory, Git sees them as modified, because they have changed since the last commit.
View your outstanding changes with the git status command:
$ git status
On branch master
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: MyController.java
Untracked files:
(use "git add <file>..." to include in what will be committed)
Helper.java
no changes added to commit (use "git add" and/or "git commit -a")
The previous example shows one tracked and modified file (MyController.java) and one untracked file (Helper.java).
Heres what it will look like if you have no modified files and no untracked files:
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
This is referred to as a clean working directory.
Common options:
-s, --short
Give the output in the short-format.
git status -- <pathspec>
Scope the output to particular files or directories. i.e. git status -- src/
If you want to see which lines changed, instead of only which files changed, then use git diff
:
git diff # Shows patch only for tracked and modified files
git diff --staged # Shows patch only for tracked and staged files
Git Add Command
Use this command to add new or modified files to the staging area prior to committing:
$ git add Helper.java MyController.java
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: MyController.java
new file: Helper.java
If you specify a directory as input to git add
, then all of the contents of that directory will be added recursively.
If you run git add on a file, and then change that file, you will need to run git add again. The add command adds a specific snapshot of the file contents, and NOT the file in general.
The staging area
This is a tree that holds the changes that are about to be committed. git status
will also show all that is staged currently. From there you can add or remove changes until you are ready to run git commit
.
Ignoring Untracked Files
Is git status
showing things that you don't care about? Gitignore is one way to hide untracked files that you never plan to commit. The usual culprits are auto-generated log files, build files, or editor files.
The hidden file .gitignore
is a special file that contains names of other files or directories. Any untracked file that is listed in .gitignore
will not show up in git status
output. Here is an example that ignores editor files:
$ cat .gitignore
# ignore Mac files
.DS_Store
*.DS_Store
#ignore IntelliJ artifacts
.idea
The .gitignore syntax is as follows:
- Blank lines or lines starting with
#
are ignored - Standard glob patterns work, and will be applied recursively throughout the entire working tree. i.e.
**/*.log
- Start patterns with a forward slash
/
to disable recursion - End patterns with a forward slash
/
to specify a directory - Negate a pattern by starting it with an exclamation mark
!
Don't re-invent the wheel! There are many .gitignore templates for common use cases available online.
Git Commit
At this point the staging area should contain the changes that you want. You can now record a snapshot by committing the changes.
Run git commit
, providing a good commit message:
$ git commit -m "My Commit Message"
[master 260489b] My Commit Message
2 files changed, 34 insertions(+), 4 deletions(-)
create mode 100644 Helper.java
Any changes that are still unstaged wonβt go into this commit. They will stay as modified files in your working directory.
Skipping the Staging Area
Oftentimes using git add
is just another command to run, and provides no additional value. In such cases, it is possible to add and commit using the --all
or -a
option:
git commit -a
This will add all modified and deleted files to the staging areas, and then commit them. This will NOT include untracked files.
Git Save Common Options
To add all files to the staging area, including untracked files, run:
git add .
Ignored files will not be included.
Remove changes from the staging area with:
git restore --staged <file>
The changes will remain in the working directory.
Other Git Save Examples
$ git restore --staged MyController.java
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: Helper.java
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: MyController.java
Removing Files
Oops! You accidentally staged or committed too much. Let's undo that!
Un-stage a file:
git restore --staged <file>
Un-track a tracked file (file will still show in working directory as untracked):
git rm --cached <file>
Delete a tracked file (file will be gone, but previous commits will still contain it if previously committed):
git rm <file>
You can also delete a file from the entire commit history. This is useful if you need to purge sensitive data such as secrets that were accidentally committed.
Moving or Renaming Files
Git doesn't actually track file renames, but it can detect renames and display such information in diffs and commit statistics later on.
mv <old file> <new file>
git rm <old file> # equivalent to git add <old file>... it just stages it for removal
git add <new file>
The above three commands can be replaced with the git move
convenience method:
git mv <old file> <new file>
git status
will show the file as staged and renamed.
Then, commit as usual.
If a file is changed drastically and also renamed during the same commit, then Git will struggle to detect the rename correctly. Avoid this by renaming in one commit, and then modifying in another.
Publishing Changes: How do I Save my Progress in GitHub?
A commit may optionally be published (pushed) to a remote Git repository using the git push command. This will save a duplicate copy on a remote machine, reducing the risk of data loss.
GitHub and Gitlab are common providers, but there are many others.
Setup remote repositories (only need to do this once):
$ git remote # list remote repositories. In this case we don't have a remote configured yet.
$ git remote add origin https://github.com/aws/aws-sdk-ruby.git
$ git remote
origin
Publish your changes:
git push
If you are publishing a new branch, then you will need to also configure an upstream remote and branch name:
git push -u origin <remote branch name>
Usually the local and remote branch names should be the same.
Stashing: How do I Save a Git Change Without Commit?
Git stash is the tool for the job when you need to shelve work without committing and pick it up later, or when you need to switch branches temporarily.
What Happens When You Stash Changes?
Stashed changes are just for you - Git's stash is not published or shared with other developers. Stashes are often short lived but they never expire. Keep in mind that stashed changes are saved on the local disk, so they are not durable to hardware failure.
Modifications are stored in a temporary set of cache files referred to as Git's stash.
Git Stash to Save
Have you ever seen this error when trying to switch branches?
$ git checkout branch1
error: Your local changes to the following files would be overwritten by checkout:
Gemfile
Please commit your changes or stash them before you switch branches.
Aborting
git stash
is one solution:
$ git stash
Saved working directory and index state WIP on master: dbaaa50 My Commit Message
$ git status # The working directory will be clean after stashing, except for untracked files
On branch master
nothing to commit, working tree clean
View the stash index:
$ git stash list
stash@{0}: WIP on master: dbaaa50 My Commit Message
If you make some more changes, then you can stash again. This time we will give our stash a custom name:
$ git stash -m "My Stash"
Saved working directory and index state On master: My Stash
$ git stash list
stash@{0}: On master: My Stash
stash@{1}: WIP on master: dbaaa50 My Commit Message
Now let's apply a stash:
$ git stash apply stash@{1} # Apply a particular stash by its id
On branch master
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: MyController.java
no changes added to commit (use "git add" and/or "git commit -a")
Delete an applied stash or one that you don't need anymore:
$ git stash drop stash@{1} # Apply does not delete the stash
Dropped stash@{1} (faebf277844c6573328320d45a0794de0a4e79af)
Apply and delete in one command:
$ git stash pop # If the stash id is ommitted, then the most recent stash (stash@{0}) is used
On branch master
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: MyController.java
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (7ffc6ae42c8f8ad431e10fc58c4817ad2bb9884c)
Stashes are not tied to a particular branch. They may be applied anywhere.
See git stash --help
for a complete list of less common options.
Does Git Automatically Save Changes?
No, Git does not support auto committing or auto-stashing. This is for the best, as you wouldn't want a new commit for every character that you've ever changed. It is recommended to configure your editor to auto-save changes to the working directory.
Things You Should Never Commit
It is best practice to not commit secrets or other sensitive information. Git is not designed to protect such information. If you choose to ignore this advice, then use private and dedicated repositories and encrypt the secrets prior to committing. Proceed at your own risk.
Quick Review of Saving Changes
Git offers multiple methods to save changes depending on what you're trying to accomplish. The primary way to save your changes is to add them to Git's staging area using the git add command and then commit using git commit. This saves your revision information in Git's repository which makes it a part of your commit history.
If you want to save uncommitted changes without making a new commit, use the git stash command to quickly save modified or staged changes to the Git's stash.
Syncing up your Git repository with a remote hosted Git repo on a service like GitHub is great for collaboration, but also for backup and saving your changes in multiple locations. Each copy of your Git repo provides redundancy which could save you in the event of device failure.
Hopefully you are now equipped with the tools and knowledge to save changes in Git with confidence.
Next Steps
Hungry for more? Why not read about the best Git books of the year?. It's free!
If you're interested in learning more about how Git works under the hood, check out our Baby Git Guidebook for Developers, which dives into Git's code in an accessible way. We wrote it for curious developers to learn how Git works at the code level. To do this we documented the first version of Git's code and discuss it in detail.
We hope you enjoyed this post! Feel free to shoot me an email at jacob@initialcommit.io with any questions or comments.
References
- Google Tech Talks: Linus Torvalds on Git - https://www.youtube.com/watch?v=4XpnKHJAok8
- Gitignore Templates - https://github.com/github/gitignore
- Introduction to GitHub - https://lab.github.com/githubtraining/introduction-to-github
- Gitlab - https://about.gitlab.com/
Final Notes
Recommended product: Decoding Git Guidebook for Developers