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:
- All aliases: ohmyzsh/plugins/git/git.plugin.zsh at master · ohmyzsh/ohmyzsh
- Cheatsheet · ohmyzsh/ohmyzsh Wiki
# 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>
Fork related operations #
Pull Updates from Origin of Fork #
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 #
# 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)
- Update
.gitmodules
. - Run
git add .gitmodules
- Run
git mv /path/to/old/directory /path/to/new/directory
- 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)
- Delete the relevant section from the
.gitmodules
file. git add .gitmodules
- Delete the relevant section from
.git/config
. git rm --cached <path-to-submodule>
rm -rf .git/modules/<path-to-submodule>
rm -rf <path-to-submodule>
Pre-Commit #
Git Flow #
References:
- Learn Version Control with Git - Workflows with git-flow (中文版)
- petervanderdoes/gitflow-avh: AVH Edition of the git extensions to provide high-level repository operations for Vincent Driessen’s branching model
- Command Line Arguments · nvie/gitflow Wiki
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.
- Sign out from git
- Open
Keychain Access.app
, delete the Internet password forgithub.com
, or git config --global --unset credential.helper
- Open
- Push again, and when prompted to enter password, enter a personal access token.