My git tips for a more efficent workflow
I have read several articles over the years around
git
workflows, this article is a summary of where my current git
work flow is at.Configuration
git
has three levels of configuration.- System: Applies to all users on the system (set with
--system
). - Global: Applies to the current user across all repositories.
- Local: Applies only to the current repository.
Most of the time, I configure
git
at the global level to streamline my workflow across projects. However, I use local configuration when working with clients who require a dedicated work email. So far, I haven’t had the need to apply system-level git
configuration in my career.git
uses the following order of precedence for configuration (from highest to lowest)- Local (
<repo>/.git/config
) - Global (
~/.gitconfig
) - System (
/etc/gitconfig
)
There are two ways that I know of configuring
git
. You can do it via the command line with the git config
command specifying a configuration level with --global
, --local
, or --system
(If you dont specify a level it will be local by default), for example;git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
Or you can config it by opening up the
~/.gitconfig
file (or one of the other aforementioned files) and edit it yourself. This is the main way that I find myself configuring git
. The git
config file is in the INI file format, in this format you have sections denoted with [] and then key value pairs, for example;[user]
name = Your Name
email = your.email@example.com
One of the first things I configure is the template that
git
will use when writing commits. This will be used instead of the standard message and is a great way to remind yourself to write consistent and well worded git
commits.[commit]
template = ~/.gitmessage
You then have to add the
~/.gitmessage
file. In mine I have the following, which is extracted from this article on writing good commit messages# Title: Summary, imperative, start upper case, don't end with a period
# No more than 50 chars. #### 50 chars is here: #
# Remember blank line between title and body.
# Body: Explain *what* and *why* (not *how*). Include task ID (Jira issue).
# Wrap at 72 chars. ################################## which is here: #
# At the end: Include Co-authored-by for all contributors.
# Include at least one empty line before it. Format:
# Co-authored-by: name <user@users.noreply.github.com>
#
# How to Write a Git Commit Message:
# https://chris.beams.io/posts/git-commit/
#
# 1. Separate subject from body with a blank line
# 2. Limit the subject line to 50 characters
# 3. Capitalize the subject line
# 4. Do not end the subject line with a period
# 5. Use the imperative mood in the subject line
# 6. Wrap the body at 72 characters
# 7. Use the body to explain what and why vs. how
Quality of life
These are a couple of configurations that work for me, but they fit in with how I like my
When pushing and pulling my remote branches always match my local branches and I don't want to have to explicitly set them up so I have the following configuration.
git
workflow to be.When pushing and pulling my remote branches always match my local branches and I don't want to have to explicitly set them up so I have the following configuration.
[push]
default = current
[pull]
default = current
For pushing, that means, you can push the current branch to the remote branch of the same name (creating it if it doesn’t exist) without needing to set the upstream branch explicitly.
For pulling, it doesn't have as big an impact as the remote relationship needs to be established. However, with
For pulling, it doesn't have as big an impact as the remote relationship needs to be established. However, with
push.current
set you can then just pull.Aliases
I only have one shell alias defined for
That is the only alias I would put in my shell config as
Once again when defining aliases you have two options you can use the
Or you can open up the
You can check on what aliases you already have defined with the following command;
Simple aliases
Most of my aliases that I define are similar to the
git
, and that is to alias git
to g
. I use the git
command probably ten's (if not hundreds) of times a day and this really does save me alot of keystrokes (and if you use oh-my-zsh
and have the git
plugin this is already defined for you).That is the only alias I would put in my shell config as
git
has its own alias system. This is what I use for defining git
aliases.Once again when defining aliases you have two options you can use the
git config
command, for example;git config --global alias.st status
Or you can open up the
~/.gitconfig
(or one of the other aforementioned files) and define the alias with the key value pair in the [alias]
section of the config fileYou can check on what aliases you already have defined with the following command;
git config --get-regexp alias
Simple aliases
Most of my aliases that I define are similar to the
g
alias, they mainly just shorten existing git
commands. Here is a list of some aliases that I have defined.[alias]
pl = pull
cl = clone
ft = fetch
...
These aliases just help me to save keystrokes, and don't really help improve my
Tweaking the defaults
Some of my aliases that I have defined tweak the defaults. These are the aliases that have a bigger impact on improving my workflow as they make
git
workflow that much.Tweaking the defaults
Some of my aliases that I have defined tweak the defaults. These are the aliases that have a bigger impact on improving my workflow as they make
git
work for me, for example;[alias]
st = status -sb
This makes it so that when I run status I get the short output -s and the -b shows the branch information. So for example the difference in the output looks like the below.
## main...origin/main
M file1.txt
A file2.txt
D file3.txt
Instead of the default, which would look like the below;
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: file1.txt
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: file2.txt
Deleted files:
(use "git restore <file>..." to restore it)
deleted: file3.txt
Another example of an alias I added to tweak the default would be the diff command below.
[alias]
diff = diff -w --patience
This aliases uses two options
-w
This makes diff ignore white space that don't affect the actual content allowing me to focus on actual code changes- --patience means that
git
will use the patience diff algorithm. This algorithm tries to minimize the noise (like when moving functions).
Complex aliases
I have some aliases that are more complex as I haven't been able to just tweak the defaults to give me the command that I am looking for. Luckily
git
allows aliases that are shell commands by prefixing the command with a !
. You either can define it without quotes or with quotes if your command contains white spaces. I'll show you two examples I have in my git
config.Firstly its the
ls
command which is probably one of the commands I use the most during a day. The alias looks like the following;[alias]
ls = !"f() { git --no-pager log --oneline -n ${1:-10}; }; f"
This command shows the previous n commits with no pager and in the one line format (which I much prefer when just checking the previous commits). However, I wanted to be able to specify the number of commits that were output or keep it to the default of 10. You cant just pass arguments like that to aliases, you can define it as a shell command that in turn defines a shell function. Then you can pass it arguments like you can a normal shell function. In this commands case we have
${1:-10}
this will either use the first argument given or default to 10. An example usage of this would be.$ g ls
dc4ede6 (HEAD -> master, origin/master) Example commit #1
e328b4b Example commit #2
fe69015 Example commit #3
2d5c911 Example commit #4
3cb585e Example commit #5
ddb74cc Example commit #6
ddec719 Example commit #7
6ced65d Example commit #8
aaf9c5a Example commit #9
96b16f1 Example commit #10
or
$ g ls 5 dc4ede6 (HEAD -> master, origin/master) Example commit #1 e328b4b Example commit #2 fe69015 Example commit #3 2d5c911 Example commit #4 3cb585e Example commit #5
Another example would be the command I have that removes branches that have been merged and deleted but still exist locally
[alias]
rm-br = !"git fetch -p && for branch in $(git branch -vv | grep ': gone]' | awk '{print $1}'); do git branch -D $branch; done"
I wont explain this one in detail, you can work it out for yourself, but I have hopefully shown you that the
git
aliasing system is super extensible and that you should always be looking for ways to improve your workflow.Thanks for reading
7