Skip to main content
Git

Git #

References:

Basic terms:

  • tree: folder
  • blob: file
  • HEAD: the current state that you’re looking at
  • stage: telling git about the changes
  • commit: record the staged changes

Useful Aliases #

Oh-My-Zsh Git Plugin #

References:

# git status
gst

# git pull
gl

# git push
gp

# git checkout master/main
gcm

# git commit -m
gcmsg "message here"

# All cached changes
# git diff --cached
# git diff --staged
gds

# git diff --word-diff
gdw

# git log --oneline --decorate --graph
glog

# git remote --verbose
grv

Change git CLI language #

Does not work in tmux for reasons unknown.

In ~/.zshrc:

# Use languages other than default shell language
alias git='LANG="zh_CN.UTF-8" git'

Observe Things #

Logs #

See all files changed with each commit: (credit)

git log --stat

One-line graphic log: (credits: new, old); (tformat is preferred over format, see credit)

git log --all --decorate --oneline --graph

# old version:
git log --graph --full-history --all --date=short --pretty=tformat:'%Cred%h%Creset %C(bold green)%ad%Creset %s %C(yellow)%d%Creset %Cblue<%an>%Creset'

Diff #

Difference between most recent commit and second most recent commit: (credit)

git diff HEAD^ HEAD

Difference between branches: (credit)

git diff branch_1..branch_2

Difference between staged (added) changes and most recent commit:

git diff --staged

Only list changed file names and status: (Doc: Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R), have their type (i.e. regular file, symlink, submodule, …) changed (T), are Unmerged (U), are Unknown (X), or have had their pairing Broken (B))

git diff --name-status <commit>

Files #

Show all files under git version control:

# r for recursive
git ls-tree -r --full-tree --name-only HEAD
# or
git ls-files

Find out how many files are being tracked: (credit)

# All files
git ls-files | wc -l

# Some kind of file (e.g. Markdown)
git ls-files | grep "\.md$" | wc -l

Blame #

Reference: Git - git-blame Documentation

# Inspect lines 20~30
git blame a-file.md -L 20,30

# Inspect from start of file to line 30
git blame a-file.md -L ,30

# Inspect from line 20 to end of file
git blame a-file.md -L 20,

Remote #

Show all remote URLs:

git remote --verbose
git remote -v

Commit-Level Operations #

Interactive chunk-staging:

git add --patch somefile.txt
git add -p somefile.txt

Change last commit message:

git commit --amend

Gently squash last two (or any number of) commits: (credit)

git reset --soft HEAD~2
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"

Undo last commit: (credit)

git reset HEAD~1

Apply Late .gitignore #

Commit first:

git commit -m "add/change .gitignore"
subl .gitignore

Add something to .gitignore, for example:

# Things to ignore
**/.DS_Store
**/*.pdf

# Things to keep
!roadmap.pdf

Apply changes:

git rm -r --cached . && git add . && git commit -m "apply .gitignore"

git bisect #

TBA

Repository-Level Operations #

Branches #

See all branches:

git branch

# See some more info
git branch --verbose
git branch -v

Create branch:

git branch my-new-branch
git checkout my-new-branch

# or
git checkout -b my-new-branch

Change branch name:

git branch -m master main
git push -u origin main

# external action required: go to origin (e.g. GitHub)
# and change the default branch

git push origin --delete master

Move files changes to another branch: (credit)

# p for patch
git checkout <other_branch_name> <files/to/grab in/list/separated/by/spaces> -p

Merge master to new branch: (credit)

git pull origin master

Delete local branch:

git checkout master
git branch -d my-old-branch

Delete all gone local branches: (credit)

git remote prune origin

Delete remote branch:

git push -d origin my-old-branch

Merge Master into Branch #

Credit: GitHub

# From your project repository, bring in the changes and test.
git fetch origin
git checkout -b "my-branch" "origin/my-branch"
git merge "master"

# Merge the changes and update on remote.
git checkout "master"
git merge --no-ff "my-branch"
git push origin "master"

Remove sensitive data from history #

Haven’t tried: Removing sensitive data from a repository - GitHub Docs

Split Repo #

Credit: git subtree - Detach (move) subdirectory into separate Git repository - Stack Overflow

# Prepare the old repo
cd <big-repo>
git subtree split -P <name-of-folder> -b <name-of-new-branch>

# Create new repo
mkdir ~/<new-repo> && cd ~/<new-repo>
git init
git pull </path/to/big-repo> <name-of-new-branch>

# Clean old repo
cd <big-repo>
git rm -rf <name-of-folder>

Pull Updates from Origin of Fork #

Ref: git - Pull new updates from original GitHub repository into forked GitHub repository - Stack Overflow

Keep own changes #

Scenario: I have a forked repository on GitHub, and want to pull the updates from the original repository, while keeping my own changes.

Step 1: Clone to local machine.

Step 2: Add original repository as remote (the name does not matter, can even call it spoon)

git remote add upstream https://github.com/something/something.git
git fetch upstream

Step 3: Open a new branch (optional)

If it takes a long time to resolve all the merge conflicts, this step will preserve the main branch for production until merge is ready.

git checkout -b merge-upstream main
git checkout -b merge-upstream master

Step 4: Run merge

If want to keep all fork commits:

git merge upstream/main main
git merge upstream/master master
# or with step 3
git merge upstream/main merge-upstream
git merge upstream/master merge-upstream

If want clean commit history:

git rebase upstream/main
git rebase upstream/master

Resolve any conflict, and commit.

Step 5: Merge new branch to main (optional)

Needed if have used Step 3.

git checkout main
git checkout master

git merge merge-upstream
git branch -d merge-upstream

Done.

Use everything upstream #

Scenario: I have used Vercel Deploy Button and now having a private non-fork repository. I did not change anything and only want to stick to the upstream.

First clone it to local machine.

Add original repository as remote, call it upstream: (the name does not matter, can even call it spoon)

Step 1: Clone to local machine.

Step 2: Add original repository as remote, call it upstream: (the name does not matter, can even call it spoon)

git remote add upstream https://github.com/something/something.git
git fetch upstream

Step 3: Run merge

# resolve all conflicts using upstream code:
# https://stackoverflow.com/a/10697551
git merge upstream/main main --allow-unrelated-histories --strategy-option theirs

Done.

Contribute selected code to fork #

Step 1: Add upstream and pull

Step 2: Make branch based on upstream

git checkout -b fix-1 upstream/main
git checkout -b fix-1 upstream/master

Step 3: Change the files and commit

Step 4: Push to own remote fork

git push origin fix-1

Step 5: Create PR with https://github.com/<original/repo>/pull/new/fix-1

Done.

Submodule #

Add #

Credit 1, 2

# Default branch
git submodule add <remote-URL>

# Specify branch
git submodule add -b <branch-name> <remote-URL> <preferred-folder-name>

# Track a specific branch after init
git submodule set-branch --branch <branch-name> <folder-name>

Initial pull submodule:

git submodule update --init --recursive

Update (Fetch) #

Update all submodules: (credit)

git submodule update --remote

Show list of all submodules: (credit)

# Either will work
git ls-tree -r HEAD
git submodule status

Edit #

Make changes to submodules (in .gitmodules), then do git submodule update --init --recursive --remote (credit)

[submodule "something"]
    path = something
    url = https://github.com/something/something.git
    ignore = untracked
    branch = new-branch

Change the directory name for a submodule: (credit)

  1. Update .gitmodules.
  2. Run git add .gitmodules
  3. Run git mv /path/to/old/directory /path/to/new/directory
  4. Run git add .

Change remote URL for a submodule: (credit)

git submodule set-url <path> <newurl>

Remove #

The easy way: git rm <path-to-submodule>

The hard way: (credit)

  1. Delete the relevant section from the .gitmodules file.
  2. git add .gitmodules
  3. Delete the relevant section from .git/config.
  4. git rm --cached <path-to-submodule>
  5. rm -rf .git/modules/<path-to-submodule>
  6. rm -rf <path-to-submodule>

Pre-Commit #

Git Flow #

References:

Installation:

brew install git-flow-avh

Initialise: (-d means using default branch names)

git flow init -d

Write new feature:

# Before beginning
git flow feature start my-new-feature

# After final commit
git flow feature finish my-new-feature

Debugging #

UTF-8 character in filename are displayed in numbers #

git config --global core.quotepath false

Repository not found. #

Don’t have write access to remote repository. Ask a maintainer/admin to add you as contributor.

How to find whether I have write access? #

In browser, open a random file of the repository, and hit the edit button. If can edit, then have write access. (credit)

refusing to allow an OAuth App to create or update workflow #

Use token to login.

  1. Sign out from git
    • Open Keychain Access.app, delete the Internet password for github.com, or
    • git config --global --unset credential.helper
  2. Push again, and when prompted to enter password, enter a personal access token.