DEV Community

Cover image for Git: Cheat Sheet (advanced)

Git: Cheat Sheet (advanced)

Maxence Poutord on December 02, 2019

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/pu...
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
 
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
 
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
 
dhirajpatra profile image
Dhiraj Patra

Nice post Maxence.

If you need to overwrite the master from the dev branch any time:

git branch -f master dev_branch [will rewrite local master branch.]

git push remote +dev_branch:master [will rewrite remote branch.]

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
 
ethikz profile image
Josh Christensen

I love the git-standup function!

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

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
 
seokjeon profile image
Se-ok Jeon

Thx for this! This is really what I wanted. Helped A LOT.
Can I translate in Korean this post? If you don't mind, I wanna share this awesome post in Korean. Surely, There will be a linke directing to this original post.

Collapse
 
maxpou profile image
Maxence Poutord

Hey, sure you can translate it in Korean. If you do so, could you please

I hope it's not too late :)

Collapse
 
weakish profile image
Jang Rush

Nice summary! I'd like to translate it to Chinese (the translated text will be published at nextfe.com). Can you give me the permission?

Collapse
 
maxpou profile image
Maxence Poutord

Hey Jang, sorry for the late reply.
Sure! You can translate it in Chinese! :) (I just ask you to mention the original post + author)
Let me know when it's published!

Collapse
 
weakish profile image
Jang Rush

Just published Chinese translation at: nextfe.com/git-cheatsheet-advanced/

Credit is given at the beginning of the translated text (also backlink to the original post).

BTW, in section "🕹Execute command on each commit when rebasing":

git rebase HEAD~3 --exec "npm run test"

I think you meant to write npm test here (as shown in the figure below), despite that npm run test and npm test are equivalent.

Thread Thread
 
maxpou profile image
Maxence Poutord

Sorry for the very late reply but thank you!

Collapse
 
sbk_india profile image
Shahzaib khan

The article gave a thoughtful process about rebasing. Thanks!

Collapse
 
maxpou profile image
Maxence Poutord

I'm glad you found it helpful 😃

Collapse
 
blueboy6 profile image
BlueBoy6

Super article, au top ! 👌

Collapse
 
maxpou profile image
Maxence Poutord

Haha me voila demasqué ! Merci :)

Collapse
 
blueboy6 profile image
BlueBoy6

Ahah ça va c'était pas bien compliqué ! 😁

Collapse
 
dracan profile image
Dan Clarke

Great post! Might be worth mentioning that for your git diff master..my-branch command - if you're already on my-branch, then you can omit the my-branch - eg. just git diff master..

Collapse
 
maxpou profile image
Maxence Poutord

thanks for the tip ;)

Collapse
 
igas profile image
Marcus Wood

Love the article! Thanks for sharing. I recently changed my habit from doing git add . to git add -A (--all). The difference is, it does not matter if you in subdirectory or not.

Collapse
 
phpcoder profile image
Slava Semushin • Edited

Thanks for sharing this!

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.

When there a lot of commits it might take too much time. In such cases, git bisect comes to the rescue -- instead of executing a command on all commits it uses binary search to identify the first commit that breaks everything.

useful for daily stand-up

git-standup() {

Perhaps, you might find this project useful: github.com/kamranahmedse/git-standup

Collapse
 
maxpou profile image
Maxence Poutord

Agree, bisect is the key when we have too many commits!

About git-standup, I don't really need it. I'm happy with my simple alias. I also grep a lot my git log :)
But, thanks for sharing! Someone else might found it handy!!!

Collapse
 
vigo profile image
Uğur "vigo" Özyılmazel

well, you don't need to create a bash alias, you can create git alias for alias glog='git log --oneline --decorate --graph' such as git config alias.glog ...

Collapse
 
maxpou profile image
Maxence Poutord

Hmmm for some reasons, I am not a big fan of git aliases :) (but I don't remember why haha)

Collapse
 
malagutti profile image
Anderson

Great article! Thank you.

Collapse
 
sebastiannielsen profile image
Sebastian-Nielsen

Question: Where did you store the "alias-code" (from the last section "Bonus: my favourite git aliases") for it to take effect?

Collapse
 
maxpou profile image
Maxence Poutord

hey, to be transparent with you everything is on my dotfile repository!

👉 github.com/maxpou/dotfiles

(in a file called alias and I source $HOME/.aliases on my zshrc/bashrc)

Collapse
 
hasii2011 profile image
Humberto A Sanchez II

Very nice article

Collapse
 
10secondsofcode profile image
Elango Sundar

awesome git cheat sheet

Collapse
 
maxpou profile image
Maxence Poutord

Thanks for the feedback!

Collapse
 
mitulislam profile image
Mitul Islam

alias yolo='git push --force' 😅

Collapse
 
maxpou profile image
Maxence Poutord

nice isn't it? 😃

Collapse
 
guillermochussir profile image
Guillermo Chussir

Very useful post! Thanks!

Collapse
 
mjcoder profile image
Mohammad Javed

Awesome article, I'll find this useful as I struggle with Git. Thanks dude.

Collapse
 
maxpou profile image
Maxence Poutord

Thanks Mohammad :)

Collapse
 
dongyuhappy profile image
dongyu

pretty good

Collapse
 
umairhm profile image
Umair Hafeez

This is brilliant! I learned a few new things, especially hub. I am gonna try it soon.

Thanks @maxpou!

Collapse
 
thuanan0105 profile image
thuanan0105

Great.
Thanks for sharing :)

Collapse
 
triantafyllosfamprikatzis profile image
Triantafyllos Famprikatzis

alias yolo made me laugh hard :D

Collapse
 
maxpou profile image
Maxence Poutord

Haha I'm glad if you liked it!
I use this command everyday 😃

Collapse
 
maxpou profile image
Maxence Poutord

Thanks Sean :)