Useful Git Commands
This post contains some useful Git commands I've discovered. Some I use frequently and others are nice to know.
Change Message on Last Commit
Sometimes we realise that we could have worded a commit message better or included more details in it. This is how to change / amend a commit message before and after a git push
. (There's a small difference between the two)
Amending Before a Push
git commit --amend
git push
Amending After a Push
# if you need to add a change as part of a previous commit
# you can also run a `git add` before the `git commit`.
git add .
git commit --amend
git push --force
# or
git push --force-with-lease
If you want to add a change to a previous commit but don't need to update the commit message, you can use the
--no-edit
flag ongit commit
The key difference between git push --force
and git push --force-with-lease
is that --force-with-lease
provides a safety check to prevent overwriting remote commits that you don't have locally.
git push --force
will unconditionally overwrite the remote branch with your local branch, even if the remote branch has commits that you don't have locally. This can lead to data loss if someone else has pushed commits to the remote branch that you haven't pulled down.
Although optional, it's a good practice to include the branch branch name as part of the --force-with-lease
flag, like so:
git push --force-with-lease=branchName
Clearer Git Diff Output
# see changes on unstaged files
`git diff --word-diff`
# see changes on staged files
`git diff --staged --word-diff`
The git diff --word-diff
and git diff --staged --word-diff
commands makes the specific word changes in a file more apparent and readable. This comes in handy to see what's changed in files that have been reformatted for instance. Reformatting could include indentation being adjusted (converting tabs to spaces as an example). In a normal git diff
it would be difficult to see what's been changed. Adding the --word-diff
flag shows changes on a word-by-word basis, rather than the default line-by-line basis.
Normal git diff
:
- The quick brown fox jumped over the lazy dog.
+ The fast brown fox leaped over the sleepy dog.
With --word-diff
:
The [-quick-]{+fast+} brown fox [-jumped-]{+leaped+} over the [-lazy-]{+sleepy+} dog.
Squashing and Renaming Commits
To rename a commit message in the Git history, we can use git rebase
# --root gets all "reachable" commits
git rebase -i --root
# In the editor, replace the word "pick" with "reword"
# or "r" on the specific commits you want to reword
# An editor will open per commit you want to reword
# Once all commits have been reworded they'll have to
# be forced pushed since we're rewriting the git history
git push --force-with-lease
We can also use git rebase
to squash multiple commits into one.
# 3 is the number of commits that you wish to rebase.
# HEAD~3 will get the last 3 commits
git rebase -i HEAD~3
# In the editor, replace the word "pick" with "squash"
# or "s" on the specific commits you want to squash
# An editor will open asking for the squash commit message
# After, an editor will open again, this time showing all
# combined commit messages from the commits to be squashed.
# Finally, we will need to force push for the rebase to take effect.
git push --force-with-lease
If we don't want the combined commit messages we can use "fixup" instead of "squash" during the interactive rebase.
Squash
- Combines the commit being squashed with the previous commit
- Prompts you to edit the combined commit message, allowing you to merge the messages from both commits
Fixup
- Also combines the commit with the previous commit
- However, it discards the commit message of the commit being fixup'd, keeping only the message from the previous commit
Restoring Unstaged and Staged Files
The git restore
command allows you to undo changes. It provides a convenient way to discard unwanted modifications, whether they are in your working directory or in the staging area / index.
To discard unstaged changes in your working directory, you can use the following command:
git restore <file>
Replace <file>
with the path to the file(s) you want to restore to their previous state. This will revert the specified file(s) to the version stored in the repository, effectively undoing any local changes made.
To discard staged changes, use this command:
git restore --staged <file>
You can use a period / dot to restore all modified files in the current directory, or a portion of the path to restore all modified files within that directory rather than specifying the file paths one by one.
git restore .
git restore src/
git restore src/components
It's common to have an alias like "grs" and "grst" setup for
git restore
. Simply addalias grs="git restore"
andalias grst="git restore --staged"
to your shell config file (.zshrc, .bashrc etc).
Reverting a Commit
The git revert
command adds a "revert commit" to the Git history, and reverses the changes in the provided commit.
git revert <commit-sha>
The default commit message looks like this:
Revert "commit message from provided commit SHA"
This reverts commit <commit-sha>
Reverting a Merge Commit
git revert -m 1 <merge-commit-sha>
- The
-m 1
option tells Git that we want to keep the parent side of the merge (which is the branch we had merged into - i.e feat/branch into main). - Finally, we provide the correct merge commit SHA / hash, which can be obtained using the
git log
orgit reflog
commands.
Listing All Untracked Files
When you've created a new directory, and inside that directory there are multiple files and other directories, running git status
will only show the name of the outermost folder with a trailing slash.
![Contentlayer showing missing fields errors in terminal](/_next/image?url=%2Fblog%2Fuseful-git-commands%2Funtracked-parent-folder.png&w=2048&q=75)
git status --untracked-files
This command will list all untracked files, as opposed to just showing the parent directory.
![Contentlayer showing missing fields errors in terminal](/_next/image?url=%2Fblog%2Fuseful-git-commands%2Fall-untracked-files.png&w=2048&q=75)
Change Commit Author
This can be useful if you use a single GitHub account which is attached to multiple organisations, and you wish to use different email addresses for repositories belonging to each organisation.
You can do this by using git config
and modifying the Git config options / variables per project.
git config user.email "[email protected]"
If you wish to modify commits from the past you can use the below, however, this is nothing to take lightly: you will create new commit objects in this process, which can become a serious problem for your collaborators - because they might have already based new work on some of the original commits.
git rebase -i <HEAD@n | commit-sha>
# mark relevant commits with "edit" keyword
git commit --amend --author="Name <[email protected]>" --no-edit
# go to next commit
git rebase --continue
Reducing .git
Folder Size
For small projects you won't see a need to shrink the size of the .git
folder. However, there are cases where the .git
folder is enormous in size. I've seen a .git
folder upward of 6GB!
There are things we can do to reduce this.
git repack -a -d -f --depth=250 --window=250
where that depth thing is just about how deep the delta chains can be (make them longer for old history - it's worth the space overhead), and the window thing is about how big an object window we want each delta candidate to scan. - Linus
The above comes from Linus Torvalds (the creator of Git) from this email.
It's recommended to run the above command overnight as it can take a long time to process.
The following commands can also be helpful in reducing the .git
folder size.
# prune all unreachable objects from the object database
git prune
# remove extra objects that are already in pack files
git prune-packed
Undoing a Merge
You can use the git reset command to return to the revision before the merge, effectively undoing it.
git reset --hard <commit-sha-before-merge>
If you don't have the commit hash and the merge was the most recent operation, you can also use the following command:
git reset --hard HEAD~1
This command can be useful if you've just completed a merge and realize that it was a mistake. By typing "HEAD~1", you're telling Git to go back to the commit before the current HEAD revision — which should be the commit before the merge!
The --hard
option means any local changes in your working copy will be discarded.
If you have important uncommitted changes you wish to keep, you can use git stash
to stash those changes. or use the --merge
flag with git reset
to reset the current branch to the state of the last commit while preserving any uncommitted changes that have not been staged.
To reapply the stashed changes, use
git stash pop
Cleaning Untracked Files
git clean
git clean -xf
git clean
cleans the working tree by recursively removing files that are not under version control, starting from the current directory.
Normally, only files unknown to Git are removed, but if the -x
option is used, ignored files are also removed; like .env
files for example. This can be useful to remove all build products for instance.
The
-n
flag doesn't actually remove anything, but show what would be removed.