For better or worse, git has become one of the Open Source community's more ubiquitous tools. It lets you manage code effectively. It helps engineers who are far apart collaborate with each other. At its heart, it's very simple, which is why the diagram in so many blog posts inevitably looks something like the one shown in Figure 1.
The unfortunate truth that's rarely discussed in detail is that git has a dark side: it makes us feel dumb. I don't care who you are—we all hit a point wherein we shrug, give up and go scrambling for Stack Overflow (motto: "This thread has been closed as Off Topic") to figure out how best to get out of the terrible situations we've caused for ourselves. The only question is how far down the rabbit hole you can get before the madness overtakes you, and you begin raising goats for a living instead.
At its core, all git does is track changes to files and
folders. git commit
effectively takes a snapshot of the filesystem (as
represented by the items added to the staging area) at a given point in time:
cquinn@1d732dc08938 ~/demo1 % git init
Initialized empty Git repository in /home/cquinn/demo1/.git/
cquinn@1d732dc08938(master|...) ~/demo1 % git add ubuntu.iso
cquinn@1d732dc08938(master|·1) ~/demo1 % git commit
↪-m "Initial commit"
[master (root-commit) b0d3bfb] Initial commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 ubuntu.iso
cquinn@1d732dc08938(master|✓) ~/demo1 % git rm --cached
↪ubuntu.iso
rm 'ubuntu.iso'
cquinn@1d732dc08938(master|·1✓) ~/demo1 % git
↪commit -m "There I fixed it"
[master 2d86934] There I fixed it
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 ubuntu.iso
cquinn@1d732dc08938(master|...) ~/demo1 % du -hs .git
174M .git
So if you do something foolish, such as committing large binaries,
you can't just revert the commit—it's still going to live in your git
repository. If you've pushed that thing elsewhere, you get to
rewrite history forcibly, either with git-filter-branch
or the
bfg
. Either way,
it's extra work that's unpleasant to others who share your repository.
Fundamentally, all that git does is create a .git folder in the top level of the repository. This subdirectory contains files and folders that change over time. Wait, isn't there a tool for that?
cquinn@1d732dc08938 ~/demo2 % git init
Initialized empty Git repository in /home/cquinn/demo2/.git/
cquinn@1d732dc08938(master|✓) ~/demo2 % cd .git
cquinn@1d732dc08938 ~/demo2/.git % ls
HEAD branches config description hooks info objects refs
cquinn@1d732dc08938 ~/demo2/.git % git init
Initialized empty Git repository in /home/cquinn/demo2/
↪.git/.git/
I'm not sure why you would do such a thing, but the point is that you definitely could.
cquinn@1d732dc08938 ~/demo2 % git
Have you ever started typing a git command and gotten lost when googling for it? Then you find the command and paste it in:
cquinn@1d732dc08938 ~/demo2 % git git status git: 'git' is not
a git command. See 'git --help'.
Did you mean this?
ign
And then you feel dumb. Let's fix that:
# echo git @? \> /usr/local/bin/git-git
cquinn@1d732dc08938(master|✓) ~/demo2 % sudo bash
root@1d732dc08938:~/demo2# echo 'git $@' >
↪/usr/local/bin/git-git
root@1d732dc08938:~/demo2# chmod +x /usr/local/bin/git-git
And, there you go:
cquinn@1d732dc08938(master|✓) ~/demo2 % git git git git
↪git git git status
On branch master
Initial commit
nothing to commit (create/copy files and use "git add" to track)
I'm not saying this is a good idea—only that it can be done.
Ever screw up syntactically?
cquinn@1d732dc08938(master|·1) ~/demo4 % git stts
git: 'stts' is not a git command. See 'git --help'.
Did you mean this?
status
Then you sit around feeling sorry for yourself. Rejoice: git features an autocorrect setting:
cquinn@1d732dc08938(master|·1) ~/demo4 % git config
↪--global help.autocorrect 8
cquinn@1d732dc08938(master|·1) ~/demo4 % git stts
WARNING: You called a Git command named 'stts',
↪which does not exist.
Continuing under the assumption that you meant 'status'
in 0.8 seconds automatically...
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: file2.txt
The --global
tag applies this to the user's ~/.gitconfig, but
the more relevant part is the final two arguments.
help.autocorrect
enables automatic typo detection, while
8
is how many tenths of seconds you have to dive for Ctrl-C
before git does something terrible to your environment.
The last tool I want to point out is myrepos
. This tool allows
you to operate effectively on the many, many, many repositories that comprise
your company's terrible microservices architecture (fun fact: microservices
was proposed as a joke at a conference talk that people took seriously):
cquinn@Quinnversion ~ % mr list 31384 10:48:52
↪Fri 06- 8-2018
mr list: /Users/cquinn/.config/vcsh/repo.d/gitconfig.git
mr list: /Users/cquinn/.config/vcsh/repo.d/mr.git
mr list: /Users/cquinn/.config/vcsh/repo.d/ssh.git
mr list: /Users/cquinn/.config/vcsh/repo.d/tmux.git
mr list: /Users/cquinn/.config/vcsh/repo.d/vim.git
mr list: /Users/cquinn/.config/vcsh/repo.d/zsh.git
mr list: /Users/cquinn/Dropbox/src/docPR/complete/
↪amazon-cloud-directory-developer-guide
mr list: finished (7 ok)
cquinn@Quinnversion ~ % mr status 31385 10:48:53
↪Fri 06- 8-2018
mr status: /Users/cquinn/.config/vcsh/repo.d/gitconfig.git
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in
↪working directory)
modified: ../../../../.gitconfig
no changes added to commit (use "git add" and/or
↪"git commit -a")
mr status: /Users/cquinn/.config/vcsh/repo.d/mr.git
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in
↪working directory)
modified: ../../../../.mrconfig
no changes added to commit (use "git add" and/or
↪"git commit -a")
mr status: /Users/cquinn/.config/vcsh/repo.d/ssh.git
mr status: /Users/cquinn/.config/vcsh/repo.d/tmux.git
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
mr status: /Users/cquinn/.config/vcsh/repo.d/vim.git
mr status: /Users/cquinn/.config/vcsh/repo.d/zsh.git
Behind origin/master by 1 commits
M ../../../../.zsh/functions/gitstatus.py
M ../../../../.zshrc
mr status: /Users/cquinn/Dropbox/src/docPR/complete/
↪amazon-cloud-directory-developer-guide
mr status: finished (7 ok)
It also takes concurrency arguments to parallelize workloads;
mr -j8 status
results in eight working threads, for example.
Two other parting tips for you. First, if you're using GitHub, install Hub as a wrapper around git; it extends the command to embrace concepts such as forks, pull requests and pages. Second, set your prompt (there are many projects that work in various shells; poke around a bit or ask me on Twitter for up-to-the-minute suggestions) to reflect your current git status—what branch you're on, whether there's uncommitted work and so on. The subtle visual cues will help you avoid making terrible mistakes.
Corey Quinn is currently a Cloud Economist at the Quinn Advisory Group, and an advisor to ReactiveOps. Corey has a history as an engineering director, public speaker and cloud architect. He specializes in helping companies address horrifying AWS bills, and curates LastWeekinAWS.com, a weekly newsletter summarizing the latest in AWS news, blogs and tips, sprinkled with snark.