10 Git Tips To Save Time And Improve Your Workflow

Git, the stupid content tracker according to its manpage, is packed with features and some can be very intimidating. So we resort to just using the same few commands we can memorize, over and over again, instead of leveraging all the power that was given to us.
Table of Contents
This article is also available in Chinese:
https://www.infoq.cn/article/tgdOxMBqYHoVslvlZqxT
Tip #1: Improve your configuration
Git is highly configurable, on a global, user, and local level.
https://git-scm.com/docs/git-config
Lookup Order
Every setting can be overwritten:
$CWD/.git/config
▼ ▼ ▼
$HOME/.gitconfig`
▼ ▼ ▼
$HOME/.config/git/config
▼ ▼ ▼
/etc/gitconfig
Change Settings
Either edit any of the files with your favorite editor or use the CLI:
# GLOBAL SETTINGS
$ git config --global <keypath> <value>
# LOCAL SETTINGS
$ git config <keypath> <value>
Values need to be quoted if they contain any white space characters.
Show Current Settings
# SHOW CURRENT SETTINGS AND THEIR ORIGIN
$ git config --list --show-origin
Helpful Config Settings
# SET YOUR IDENTITY
$ git config --global user.name "<your name>"
$ git config --global user.email <your email>
# PREFERRED EDITOR
$ git config --global core.editor <editor, e.g. vim>
# CREDENTIALS CACHE
# WINDOWS
$ git config --global credential.helper manager
# LINUX (timeout in seconds)
$ git config --global credential.helper "cache --timeout=3600"
# MACOS
$ git config --global credential.helper osxkeychain
https://git-scm.com/docs/gitcredentials
Tip #2: Aliases
To save commonly used git commands, create an alias:
# CREATE AN ALIAS
$ git config --global alias.<alias-name> "<git command>"
# USE AN ALIAS
$ git <alias-name> <more optional arguments>
Helpful Aliases
# UNDO LAST COMMIT
$ git config --global alias.undo "reset --soft HEAD^"
# AMEND STAGED CHANGES TO LAST COMMIT (keep commit message)
$ git config --global alias.amend "commit --amend --no-edit"
# COMPACTER STATUS OUTPUT
$ git config --global alias.st "status -sb"
# COLORED LOG WITH GRAPH
$ git config --global alias.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'"
# REMOVE ALL MERGED BRANCHES
$ git config --global alias.rmb "!git branch --merged | grep -v '*' | xargs -n 1 git branch -d"
# RANK CONTRIBUTERS
$ git config --global alias.rank "shortlog -n -s --no-merges"
Tip #3: Finding Commits and Changes
By Commit Message
# BY COMMIT MESSAGE (all branches)
$ git log --all --grep='<search term>'
# BY COMMIT MESSAGE (include reflog)
$ git log -g --grep='<search term>'
By Change
# BY CONTENT OF CHANGES
$ git log -S '<search term>'
By Date
# BY DATE RANGE
$ git log --after='DEC 15 2019' --until='JAN 10 2020'
Tip #4: Adding Hunks
Instead of just adding all changes of a file with git add <filepath>
, the argument --patch / -p
will stage hunks interactively.
# Patch Commands
y = stage hunk
n = don't stage this hunk
q = quit
a = stage this and all remaining hunks of the current file
d = don't stage this and all remaining hunks of the current file
/ = search for hunk (regex)
s = split into smaller hunks
e = manually edit hunk
? = print help
g = select a hunk to go to
j = leave hunk undecided, see next undecided hunk
J = leave hunk undecided, see next hunk
k = leave hunk undecided, see previous undecided hunk
J = leave hunk undecided, see previous hunk
https://git-scm.com/docs/git-add#Documentation/git-add.txt–i
Tip #5: Stash changes without committing
A stash is a temporary shelve of the current changes. With their help, we can return to the current state of the index and can reapply the stashed changes later on.
By default, only changes in currently tracked files will be stashed, new files will be ignored.
Multiple stashes can be created and applied independently.
https://git-scm.com/docs/git-stash
Creating
# CREATE NEW STASH
$ git stash
# CREATE NEW STASH (include untracked changes)
$ git stash -u/--include-untracked
# CREATE NEW STASH WITH NAME
$ git stash save "<stash name>"
# INTERACTIVE STASHING
$ git stash -p
Listing
# LIST ALL STASHES (providing "n" for other commands)
$ git stash list
Viewing
# VIEWING STASH CONTENT
$ git stash show
# VIEWING STASH DIFFS
$ git stash show -p
Applying
# APPLY LAST STASH (delete stash afterwards)
$ git stash pop
# APPLY LAST STASH (keep stash)
$ git stash apply
# APPLY SPECIFIC STASH (n = stash list number)
$ git stash pop/apply stash@{n}
# CREATE NEW BRANCH FROM STASH (n = stash list number)
$ git stash branch <new branch name> stash@{n}
# APPLY SINGLE FILE FROM STASH (n = stash list number)
$ git checkout stash@{n} -- <filepath>
Cleaning
# DROP SPECIFIC STASH (n = stash list number)
$ git stash drop stash@{n}
# DROP ALL STASHES
$ git stash clear
Tip #6: Dry Run
Many git operations can be quite destructive.
For example, a git clean -f
will remove all untracked files, without any chance to bring them back.
To avoid such a catastrophic result, many commands support a dry-run to check the result before it actually happens. Sadly, the option used isn’t always the same:
git clean -n/--dry-run
git add -n/--dry-run
git rm -n/--dry-run
# GIT MERGE PSEUDO DRY-RUN
git merge --no-commit --no-ff <branch>
git diff --cached
git merge --abort
Be aware that git commit -n
isn’t dry-run at all!
It’s actually the option --no-verify
, ignoring any pre-commit
/commit-msg
githooks.
Tip #7: Safer Force Push
It’s easy to mess up a branch by working on an older commit, creating a new head, etc.
The remote changes could be overridden with git push --force
, but they shouldn’t!
git push --force
is a destructive and dangerous operations, because it works unconditionally, and destroys any commits already push by other committers. This doesn’t have to be fatal for the repository of others, but changing history and affecting others is not a good idea.
A better option is using git push --force-with-lease
instead.
Instead of unconditionally overwriting the upstream remote, git checks for any remote changes that aren’t available locally.
If so, it fails with a “stale info” message and wants us to git fetch
first.
https://git-scm.com/docs/git-push#Documentation/git-push.txt—force-with-leaseltrefnamegt
Tip #8: Changing Commit messages
Commits are immutable, and can’t be changed. But we can amend an existing commit with a new message, instead. This will replace the original commit, so don’t use it on already pushed commits.
git commit --amend -m "<new commit message>"
https://git-scm.com/docs/git-commit#Documentation/git-commit.txt—amend
Tip #9: Changing history even more
Changing the history of a repository doesn’t stop at the last commit message.
With git rebase
, multiple commits can be changed:
# RANGE OF COMMITS
git rebase -i/--interactive HEAD~<number of commits>
# ALL COMMITS AFTER <commit-hash>
git rebase -i/--interactive <commit hash>
A reverse-ordered list of commits is presented in the configured editor. Something like this:
# <command> <commit hash> <commit message>
pick 5df8fbc revamped logic
pick ca5154e README typos fixed
pick a104aff added awesome new feature
By changing the actual content of the editor, we can give git a blueprint of what/how to rebase:
# p, pick = use commit without changes
# r, reword = change commit message
# e, edit = edit commit
# s, squash = meld into commit
# f, fixup = like "squash", but discard commit message
# x, exec = run command (rest of the line)
# d, drop = remove commit
After saving the editor, git will run this blueprint to rewrite history.
The e, edit
will pause the rebase, to edit the current state of the repository.
To finish up, run git rebase --continue
.
If any problems occur, like merge conflicts, and we want to start over, there’s always git rebase --abort
.
https://git-scm.com/docs/git-rebase
Tip #10: Archive Tracked Files
We can compress the tracked files for a specific ref in different formats (zip
or tar
):
git archive --format <format> --output <filename> <ref>
<ref>
can be a branch, commit hash, or a tag.
https://git-scm.com/docs/git-archive
Bonus Tip: The Single Dash
There is a shortcut representing the previously used branch: a single dash-
git checkout my-branch
# Current branch: my-branch
<do some git operations, e.g. adding/commiting>
git checkout develop
# Current branch: develop
git merge -
# Merges my-branch into develop
The single dash is equal to @{-1}
.
https://git-scm.com/docs/git-checkout#Documentation/git-checkout.txt-ltbranchgt
Resources
- Git SCM Documentation (Git)
- Pro Git Book (free eBook)
- GitHub Git Cheat Sheet (PDF)
- Interactive Cheat Sheet