DEV Community

Cover image for Git: Cheat Sheet (advanced)
Maxence Poutord
Maxence Poutord

Posted on • Edited on • Originally published at maxpou.fr

Git: Cheat Sheet (advanced)

If you find git confusing, I created this little cheat sheet! Please, note that I voluntary skipped the basic commands like git commit, git pull/push... This cheat sheet is intended for an "advanced" usage of git.

Git Cheat Sheet

🧭 Navigation - Go to the previous branch

git checkout - 
Enter fullscreen mode Exit fullscreen mode

πŸ” Get the history

# Log in one line git log --oneline # Retrieve all commits by message # Here all commit that contain 'homepage' git log --all --grep='homepage' # Retrieve all commit by author git log --author="Maxence" 
Enter fullscreen mode Exit fullscreen mode

πŸ™ˆOoops #1: I reseted an unwanted commit. How to rollback?

# Get everything you did git reflog # then reset to the desired commit (i.e. HEAD@{4}) git reset HEAD@{4} # ...or... git reset --hard <commit-sha1> 
Enter fullscreen mode Exit fullscreen mode

For more detail about this command, I wrote this post.

πŸ€¦β€β™€οΈOoops #2: I mixed-up with my local repo. How to clean it?

git fetch origin git checkout master git reset --hard origin/master # You're now up-to-date with master! 
Enter fullscreen mode Exit fullscreen mode

πŸ•΅πŸ»β€β™‚οΈDifference between my branch and master

git diff master..my-branch 
Enter fullscreen mode Exit fullscreen mode

βœ” Custom commits

# Edit last commit git commit --amend -m "A better message" # Add something to the last commit without writing message again git add . && git commit --amend --no-edit # empty commit - can be useful to re-trigger CI build... git commit --allow-empty -m "chore: re-trigger build" 
Enter fullscreen mode Exit fullscreen mode

If you don't know what to put in your commit messages, I wrote a post about conventional commits.

♻️ Squash commits

Let say I want to rebase the last 3 commits:

  1. git rebase -i HEAD~3
  2. Leave the first "pick" and replace the rest by "squash" (or "s")
  3. Tidy up the commit message and save (:wq in vi).

🎯Fixup

Let say I want to add something in the commit fed14a4c

git commit --fixup

git add . git commit --fixup HEAD~1 # or replace HEAD~1 by the commit hash (fed14a4c) git rebase -i HEAD~3 --autosquash # save&quit the file (:wq in VI) 
Enter fullscreen mode Exit fullscreen mode

πŸ•ΉExecute command on each commit when rebasing

For massives features, you might end up with a branch with a few commits inside. And then tests are failing and you want to identify the "guilty commit". You can use rebase --exec to execute a command on each commit of the history.

# Will run "npm test" command on the last 3 commit ❀️ git rebase HEAD~3 --exec "npm run test" 
Enter fullscreen mode Exit fullscreen mode

rebase --exec

πŸ¦‹Stash

Because it's not all about git stash and git stash pop ;)

# save all tracked files git stash save "your message" # list your stashes git stash list # retrieve stash and delete git stash apply stash@{1} git stash drop stash@{1} # ... or in 1 command git stash pop stash@{1} 
Enter fullscreen mode Exit fullscreen mode

πŸ—‘ Clean

# remove branches that no longer exist on remote git fetch -p # remove all branch that contains "greenkeeper" git fetch -p && git branch --remote | fgrep greenkeeper | sed 's/^.\{9\}//' | xargs git push origin --delete 
Enter fullscreen mode Exit fullscreen mode

πŸ™ GitHub = Git + Hub

I use Hub as a wrapper for git. To enable it you've to set hub as an alias for git (alias git='hub').

# Open browser and go to the repository url (GitHub only) git browse 
Enter fullscreen mode Exit fullscreen mode

Other commands are available here.

πŸ¦„ Bonus: my favourite git aliases

alias g='git' alias glog='git log --oneline --decorate --graph' alias gst='git status' alias gp='git push' alias ga='git add' alias gc='git commit -v' # 🀘 alias yolo='git push --force' # useful for daily stand-up git-standup() { AUTHOR=${AUTHOR:="`git config user.name`"} since=yesterday if [[ $(date +%u) == 1 ]] ; then since="2 days ago" fi git log --all --since "$since" --oneline --author="$AUTHOR" } 
Enter fullscreen mode Exit fullscreen mode

And you, what's your favourite git command?


Thank you for taking the time to read this post. I hope you found it useful! If you liked it, please give it a ❀️ or a πŸ¦„! Also, feel free to comment or ask questions in the section below or on Twitter @_maxpou :)


Originally published on maxpou.fr.

Top comments (58)

Collapse
 
sebastianstamm profile image
Sebastian Stamm

My favorite git utility is "reuse recorded resolution" git rerere. Sometimes if you have multiple parallel branches and try to merge or rebase between them, you get the same conflict multiple times. git rerere records your resolution and then just automatically applies it, if you encounter the same conflict again.

And it's pretty much set it and forget it because it completely works autonomously in the background. git-scm.com/docs/git-rerere

Collapse
 
maxpou profile image
Maxence Poutord

I never heard about this one until now. It looks very handy! I'll give a try on my next conflict!

Thanks for sharing πŸ‘

Collapse
 
qrzysio profile image
Qrzysio
# A good list of alises http://haacked.com/archive/2014/07/28/github-flow-aliases/ [alias] # Staging a = add aa = add --all # Branch br = branch branch-name = rev-parse --abbrev-ref HEAD branch-diff = diff master...HEAD branch-files = "!git diff master...HEAD --name-status | sed '/^D/d ; s/^.\\s\\+//'" # Commit c = commit ca = commit -a cm = commit -m cal = !git add -A && git commit # Commit all changes cam = commit -am cne = commit --no-edit amend = commit --amend amend-all = !git add --all && git commit --amend --reuse-message=HEAD # Clone cl = clone sclone = clone --depth=1 # Checkout co = checkout cb = checkout -b # Cherry-pick cp = cherry-pick # Diff d = diff --color-words dc = diff --cached df = !"git diff-index --quiet HEAD -- || clear; git --no-pager diff --patch-with-stat" # Merge m = merge # Pull up = pull plom = pull origin master plum = pull upstream master preb = !git fetch upstream && git rebase upstream/master # Push p = push pom = push origin master poh = push origin head # Stash st = stash stp = stash pop # Status/Logging s = status ss = status -sb hist = log --graph --pretty=custom # Show custom graph l = log --pretty=custom # Show custom log ll = log --stat --abbrev-commit lc = shortlog --summary --numbered # List contributors # Reset unstage = reset HEAD -- # Mixed reset (affects HEAD and Index) undo = reset --soft HEAD~1 # Undo last commit (affects HEAD only) reset = reset --hard HEAD~1 # Remove last commit (from HEAD, Index and Working Dir) # Remote r = remote -v # Submodules subpl = submodule update --init --recursive # Git flow new = !git pull origin develop && git flow feature start done = !git pull origin develop && git flow feature finish "$(git symbolic-ref --short HEAD | sed -n 's/^feature\\///p')" go = !git checkout $1 && pull master = !git checkout master && pull develop = !git checkout develop && pull mmm = !git fetch origin master && git rebase origin/master ddd = !git fetch origin develop && git rebase origin/develop # Misc publish = "!git push --set-upstream origin $(git branch-name)" # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Find commits by source code cc = "!f() { \ git log --pretty=custom --decorate --date=short -S\"$1\"; \ }; f" # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Find commits by commit message cm = "!f() { \ git log --pretty=custom --decorate --date=short --grep=\"$1\"; \ }; f" # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Credit an author on the latest commit credit = "!f() { \ if [ -n \"$1\" ] && [ -n \"$2\" ]; then \ git commit --amend --author \"$1 <$2>\" -C HEAD; \ fi \ }; f" # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # List remote branches lrb = "!f() { \ remote="${1:-origin}"; \ git ls-remote --heads "$remote"; \ }; f" # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Merge GitHub pull request on top of the current branch or, # if a branch name is specified, on top of the specified branch mpr = "!f() { \ declare currentBranch=\"$(git symbolic-ref --short HEAD)\"; \ declare branch=\"${2:-$currentBranch}\"; \ if [ $(printf \"%s\" \"$1\" | grep '^[0-9]\\+$' > /dev/null; printf $?) -eq 0 ]; then \ git fetch origin refs/pull/$1/head:pr/$1 && \ git checkout -B $branch && \ git rebase $branch pr/$1 && \ git checkout -B $branch && \ git merge pr/$1 && \ git branch -D pr/$1 && \ git commit --amend -m \"$(git log -1 --pretty=%B)\n\nClose #$1\"; \ fi \ }; f" # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Remove the tag with the specified tag name if # exists and tag the latest commit with that name retag = "!f() { \ git tag -d "$1" &> /dev/null; \ git tag $1; \ }; f" # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # So much color [color] ui = always [color "branch"] current = green bold local = green remote = yellow [color "diff"] frag = magenta meta = yellow new = green old = red [color "diff-highlight"] oldNormal = red bold oldHighlight = "red bold 52" newNormal = "green bold" newHighlight = "green bold 22" [color "status"] added = green reverse changed = yellow reverse untracked = red reverse # Git mergetool [merge] tool = opendiff [core] editor = nano abbrev = 12 attributesfile = ~/.gitattributes excludesfile = ~/.gitignore autocrlf = input mergeoptions = --no-edit ignorecase = false [pager] # Insanely beautiful diffs ==> npm install -g diff-so-fancy diff = diff-so-fancy | less --tabs=4 -RFX show = diff-so-fancy | less --tabs=4 -RFX [diff "bin"] # Use `hexdump` to diff binary files textconv = hexdump -v -C [pretty] custom = "%C(magenta)%h%C(red)%d %C(yellow)%ar %C(green)%s %C(yellow)(%an)" # β”‚ β”‚ β”‚ β”‚ └─ author name # β”‚ β”‚ β”‚ └─ message # β”‚ β”‚ └─ date (relative) # β”‚ └─ decorations (branch, heads or tags) # └─ hash (abbreviated) [help] # Correct typos autocorrect = 1 # Any GitHub repo with my username should be checked out r/w by default # http://rentzsch.tumblr.com/post/564806957/public-but-hackable-git-submodules [url "git@github.com:nicksp/"] insteadOf = "git://github.com/nicksp/" # Rewrites of repo paths [url "git@github.com:"] insteadOf = "gh:" insteadOf = "git://github.com" pushInsteadOf = "github:" pushInsteadOf = "git://github.com/" pushInsteadOf = "https://github.com/" [url "git://github.com/"] insteadOf = "github:" [url "git@gist.github.com:"] insteadOf = "gst:" pushInsteadOf = "gist:" pushInsteadOf = "git://gist.github.com/" pushInsteadOf = "https://gist.github.com/" [url "git://gist.github.com/"] insteadOf = "gist:" # Push easily http://stackoverflow.com/a/23918418/89484 [push] # Make `git push` automatically push relevant # annotated tags when pushing branches out followTags = true default = current # Use separate file for username / github token / etc [include] path = ~/.gitconfig.local [filter "lfs"] clean = git lfs clean %f smudge = git lfs smudge %f required = true [fetch] prune = true 
Enter fullscreen mode Exit fullscreen mode

Found here: github.com/nicksp/dotfiles/blob/ma...

Collapse
 
maxpou profile image
Maxence Poutord

this config is... massive!!! 😯

Collapse
 
exadra37 profile image
Paulo Renato • Edited

Awesome article... congrats :)

My favourite and most used Git alias:

$ alias | grep wip - gunwip='git log -n 1 | grep -q -c "\-\-wip\-\-" && git reset HEAD~1' gwip='git add -A; git rm $(git ls-files --deleted) 2> /dev/null; git commit --no-verify --no-gpg-sign -m "--wip-- [skip ci]"' 

The gwip commits everything with the message --wip-- [skip ci]. By everything I mean untracked files, changes not staged for commit, and changes already staged for commit.

The gunwip reverts gwip, with the caveat that changes already staged for commit will become changes not staged for commit.

I use it to save my work until I am ready to create a proper commit.

Having [skip ci] in the commit message tell the Continuous Integration pipeline to ignore the commit.

Collapse
 
vfonic profile image
Viktor

This is a great idea! This functionality should probably be included within git.

One idea that comes to my mind how you could complicate this (read: make it work "completely"). You could extend the script to open all the staged files and add a comment to the top of the file. Something like: --staged--. Then, when you "unwip", it can read that comment and stage/unstage files as needed, while also removing that comment.

Collapse
 
tpenguinltg profile image
tPenguinLTG

It already is, in a way.

If you do a git stash -u, it will create a commit for you that includes staged, unstaged and untracked files, and it knows which files were staged and which ones weren't. If you wanted to keep the changes that were stashed as they were, you can issue a git stash apply immediately after (use apply to keep the stash, or pop to discard it after applying it).

Thread Thread
 
vfonic profile image
Viktor

This is amazing! Thank you!

Git never seizes to amaze me for its unlimited functionality.

For applying the stash, you need add --index as in: git stash apply --index. This will restore the index (staged/unstaged files) along with the file changes.

Collapse
 
vadorequest profile image
Vadorequest

Awesome cheat sheet, really. Learned quite a few things despite being quite advanced with git already.

I prefer my version of git lg (logs), which also displays the date of each commit in a neat format:
gist.github.com/Vadorequest/9d1f86...

* f0d6b49 - wip (3 hours ago) <Me>

Collapse
 
maxpou profile image
Maxence Poutord • Edited

Love this one 😍 Thanks!!! Will update my dotfiles right now!

Collapse
 
vadorequest profile image
Vadorequest

Improved version:

# useful for daily stand-up # See https://dev.to/maxpou/git-cheat-sheet-advanced-3a17 git-standup() { AUTHOR=${AUTHOR:="`git config user.name`"} since=yesterday if [[ $(date +%u) == 1 ]] ; then since="2 days ago" fi git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --since "$since" --author="$AUTHOR" } 
Collapse
 
maxpou profile image
Maxence Poutord

interesting! πŸ˜ƒ

Collapse
 
glaucoleme profile image
Glauco Leme

Excellent guide! I like the idea of git-standup. β˜•

For alias, to work even with mistyping, I also use:

alias gti='git' 

Also, one thing that was really useful for me is the meta repository of Matt Walters, so I can avoid git submodule and subtree to work with multiple repositories.

Collapse
 
msk61 profile image
Mohammed El-Afifi

Nice article. I got to learn about a few things I didn't know about git before.

By the way git save is deprecated now, you should rather go by git push -m.

And for the last section about git aliases, since I'm using bash I get all these aliases and much more when I install oh-my-bash.

Collapse
 
maxpou profile image
Maxence Poutord

Hmm.. where did I wrote a command with git save?

About the aliases, I have something similar: Oh-my-zsh. But I extract those aliases in my personal dotfiles so I can get them everywhere I need them :)

Collapse
 
msk61 profile image
Mohammed El-Afifi • Edited

Sorry, it seems I completely messed up the comment. I meant to use git stash push -m instead of git stash save, as the latter is deprecated.

Thread Thread
 
maxpou profile image
Maxence Poutord

you completely right Mohammed! Thanks for pointing this out :)

Collapse
 
bigyan4424 profile image
Bigyan Thapa • Edited

Nice article, thank you for sharing. Want to some more favorite aliases to the list.

 alias gf='git fetch' alias gs='git status' alias gcm='git checkout master' 

The git -standup() one in nice. Can you please explain a bit on how to use it?

Collapse
 
nagyiistvan profile image
nagyiistvan

Nice post! Thank you!

My aliases:

# Git Command alias last = log -1 HEAD alias who = shortlog -s -- alias unstage = reset -q HEAD -- alias discard = checkout -- alias nevermind = !git reset --hard HEAD && git clean -df alias uncommit = reset --mixed HEAD~ alias save = commit -m alias resave = commit --amend alias last = log -1 HEAD --format = format:%Cred%H alias summary = status -u -s alias new-branch = checkout -b alias rename-branch = branch -m alias delete-branch = branch -D alias branches = branch -a alias stashes = stash list alias unmerged = branch --no-merged alias unstash = stash pop alias unmerged = diff --name-only --diff-filter=U alias patch = add --patch 
Collapse
 
zaratedev profile image
Jonathan Zarate

Nice! :)

My favorite git aliases:

# Git Command alias g="git" alias gl="git log --pretty=oneline --graph --decorate --all" alias gs="git status" alias ga="git add" alias gb="git branch" alias gc="git commit" alias gco="git checkout" alias gp="git pull" alias gps="git push" alias gr="git rebase" alias gri="git rebase -i HEAD~5" 

:D

Collapse
 
maxpou profile image
Maxence Poutord

we almost got the same! :D