LJ Archive

Terrible Ideas in Git

This article was derived from a talk that GitHub Universe faithfully rejects every year. I can't understand why....By Corey Quinn

Ad Choices Symbol

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.

Git Model

Figure 1. Git Model (Source https://nvie.com )

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.

Actually Useful Git Tricks

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.

About the Author

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.

LJ Archive