Git

📚 Basics

Concepts

  • A branch is just a pointer to a snapshot node
  • Snapshot nodes form a directed acyclic graph
  • The HEAD pointer points to the snapshot node that matches the current state of the working directory

The three areas

Working Directory

Staging Area

The three states of a file

Modified

Once a file that git already knows about has been changed since the last commit, it enters this state. (A file git doesn’t know about won’t enter the Modified state even if it changes, e.g. a new file or a file covered by .gitignore.)

Staged

A file (or some of the changes in it) that you’ve added with git add enters the Staged state.

Checking file status

Use git status to see the current state of the repo: which files have been modified and which have been staged

On branch main
Your branch is up to date with 'origin/main'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   components/blog/TocNav.vue
        modified:   content/blogs/git.md
        modified:   content/blogs/importtype-import.md
        modified:   nuxt.config.ts

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        components/content/ProseH4.vue
        content/blogs/package-json.md
        content/blogs/react-native-pitfalls.md
        content/blogs/use-githook-to-improve-publish-process.md
        content/zh/blogs/

no changes added to commit (use "git add" and/or "git commit -a")

With git status -s you get a shorter format

  • M means the file is in the Modified state
  • A means all changes to the file are in the staging area
  • AM means some of the file’s changes are staged and some are not
  • ?? means the file is unknown to git (i.e. not tracked by git)
 M components/blog/TocNav.vue
 M content/blogs/git.md
 M content/blogs/importtype-import.md
 A nuxt.config.ts
AM components/content/ProseH4.vue
?? content/blogs/package-json.md
?? content/blogs/react-native-pitfalls.md
?? content/blogs/use-githook-to-improve-publish-process.md
?? content/zh/blogs/

📄 Commands

The basics

git add ${files}
git checkout ${branchName}
git checkout -b ${newBranch}  # create a new branch from the current one and check it out
git checkout - # switch back to the previous branch

Inspecting

git status # show status
git status -s # show status as a list of files
git branch --show-current # print the current branch name
git log ${branch} # show a branch's commit history
git show ${commitHash} # show the changes in a given commit


git diff --name-only # list the names of all files currently in the Modified state
git diff --staged --name-only # list the names of all staged files, typically used for things like formatting before a commit

# list the names of all staged files, only showing added, copied and modified files
git diff --staged --diff-filter=ACM

# format every staged file with prettier
git diff --staged --name-only | xargs prettier --write 

Stashing changes

Say you’ve changed a bunch of code on branch A but it’s not finished, and you suddenly need to switch to branch B for something else. You can stash the code

git stash  # stash unstaged changes 
git stash apply
git stash pop # pop the most recent stashed changes 
git stash drop # drop the stash
git stash list  # show the stash stack

Or you can just throw it all away

git restore ${filename}  # discard unstaged changes to a file
git restore --staged ${filename} # remove a file from the staging area

Moving changes around

git merge ${branch}  # merge branch into the current branch
git cherry-pick ${commitHash} # pick the commitHash commit onto the current branch

The last commit

If you’ve committed but haven’t pushed to the remote and want to undo it, use a soft reset; the changes come back out

git reset --soft HEAD~1
git reset --soft HEAD~n # soft-reset several commits at once

If you’ve committed but haven’t pushed and want to change the commit message, use amend

git commit --amend

Syncing with the remote

git fetch --all
git pull # equivalent to fetch + merge
git pull --rebase
git commit -m ${message}
git push

Hard reset

Force-reset a local branch

git reset --hard HEAD~n
git reset --hard origin/master

Force-reset the remote (danger zone)

git reset --hard xxx
git push -f

A few tricks

You can combine the commands above into some handy shortcuts

For example, I’m on branch A and want to update branch B, then merge B into A

# as a shell function
update_merge() {
  git checkout B
  git pull
  git checkout -
  git merge B
}

# with aliases, it fits on one line
gco B && pull && gco - && git merge B

Tags

git tag v1.0.0    # tag the current HEAD
git push --tags   # push tags

alias

alias ggr="git log --oneline --decorate --graph --all"
alias gco="git checkout"
alias glog="git log"
alias gst="git status"

alias gs="git stash"
alias gsp="git stash pop"

alias gdropall="git restore ." # discard all changes in unstaged

alias gb="git branch"  # show the current branch

alias gcp="git cherry-pick"

alias gfa="git fetch --all"
alias gplr="git pull --rebase"