Skip to main content

Git

· 10 min read

Open in Notion

Download and Installation

Quick start (most-used)

git status
git add .
git commit -m "message"
git pull --rebase
git push

Config

Identity

git config --global user.name "Bendy Zhang"
git config --global user.email "81795705@qq.com"
git config --global --list

Helpful defaults

git config --global init.defaultBranch main
git config --global pull.rebase true
git config --global rebase.autoStash true
git config --global fetch.prune true
git config --global rerere.enabled true

Ignore files globally

git config --global core.excludesfile ~/.gitignore_global
# edit ~/.gitignore_global

Workspace (working tree / index / HEAD)

  • Working tree (workspace): Files you see and edit in your filesystem.
  • Index / Staging area: What you get after git add; ready to be committed.
  • HEAD: The currently checked-out commit (usually the tip of the current branch).

Check workspace status

git status
git diff # workspace vs index
git diff --staged # index vs HEAD

Move changes between workspace and staging

git add <file>
git add -p
git restore --staged <file> # unstage

Discard workspace changes (dangerous)

git restore <file>
git restore .

Clean untracked files in workspace

git clean -n -d # dry run
git clean -fd # really delete

Temporarily save workspace changes (stash)

git stash
git stash pop

Repo basics

Init / clone

git init
git clone <repo>
git clone --depth 1 <repo> # shallow clone
git clone --filter=blob:none <repo> # partial clone (faster for big repos)

Remotes

git remote -v
git remote add origin <url>
git remote add upstream <url>
git remote set-url origin <new-url>
git remote rename origin old-origin
git remote remove upstream

Fetch / pull

git fetch --all --prune
git pull
git pull --rebase
git pull --rebase --autostash

Commit

Commit with empty message

git config --global alias.nccommit "commit -a --allow-empty-message -m ''"
git nccommit

Amend / change commit info

git commit --amend --no-edit
git commit --amend --author="Bendy Zhang <zbatbndy.net>" --no-edit

git commit --amend --date="Sun Aug 7 13:05 2022 +0800" --no-edit
GIT_COMMITTER_DATE="Sun Aug 7 14:10 2022 +0800" git commit --amend --no-edit

Fix the last commit without changing message (add forgotten files)

git add .
git commit --amend --no-edit

Create commits without committing staged changes (split work)

git add -p # stage interactively
git commit -m "..."

Branch

List / switch / create

git branch
git branch -a
git switch <branch>
git switch -c <new-branch>
# old style:
git checkout <branch>
git checkout -b <new-branch>

New branch from remote

git checkout -b localBranch origin/remote_branch

Push changes to new branch

git checkout -b newBranch
git push -u origin newBranch # -u is short for `-set-upstream`

Bind local branch with remote branch

git branch --set-upstream-to=origin/remote_branch your_branch

Rename branch

git branch -m oldName newName
git branch -m newName # rename current branch
git push origin :oldName newName
git push -u origin newName

Delete branches

git branch -d <branch> # safe delete (only if merged)
git branch -D <branch> # force delete

git push origin --delete <branch>

Remove multiple branches

git branch -d `git branch --list '3.2.*'`

Reset local branch to remote branch

git fetch origin && git reset --hard origin/master

Merge & rebase

git merge master
git merge master --squash # merge master branch to current branch and keep changes without commits
git rebase master
git rebase --interactive --root # squash all of your commits down to the root commit

Cherry-pick

git cherry-pick <commitid>
# if conflicts, resolve then:
git add . && git cherry-pick --continue

git cherry-pick A..B # The commit A should be older than B which will not include A. If requires A, please use `A^~B`

Diff

git diff # working tree vs index
git diff --staged # index vs HEAD
git diff HEAD # working tree + index vs HEAD
git diff <a>..<b>
git diff <a>...<b> # changes on branch b since it diverged from a

Undoing things / recovery

Unstage / discard

git reset # unstage all
git reset HEAD <file>
git restore --staged <file>
git checkout -- <file> # discard changes in working directory (legacy)
git restore <file> # discard changes (recommended)

Reset commits

git reset --soft HEAD~1 # undo commit, keep changes staged
git reset --mixed HEAD~1 # undo commit, keep changes unstaged (default)
git reset --hard HEAD~1 # undo commit and discard changes

Revert (safe for shared branches)

git revert <commit>
git revert <old>..<new> # revert a range (creates multiple commits)

Recover lost work

git reflog
git fsck --lost-found

Remove untracked files & directories

git clean -n -d # dry run
git clean -fd # remove untracked files + dirs
git clean -fX # remove ignored files
git clean -fx # remove ignored and non-ignored files

Stash usage

git stash // stash current changes
git stash pop // pop last stash and remove from history
git stash save 'message' // stash current changes with message
git stash list // show all stashes
git stash apply <id> // apply <id> stash
git stash drop <id> // delete one stash
git stash clear // delete all stashes

Log

$ git log --follow --pretty=oneline things/text.txt view log of the renamed file

Common log views

git log --oneline --decorate --graph --all
git show <commit>
git show <commit>:<path/to/file>
git blame <file>

Search history

git log -S "searchText" # pickaxe
git log -G "regex" # regex search in diffs
git log -- path/to/file # file history
git log --since="2024-01-01" --until="2024-02-01"
git log --author="zb@bndy.net" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -

Output:

added lines: 545140, removed lines: 1152979, total lines: -607839
git log --pretty=format:%h,%an,%ae,%ad,%s --name-only

Output:

acb533d,Bendy Zhang,zb@bndy.net,Sun Jul 12 16:17:14 2020 +0800,commit message
.gitignore
README.md
  • -since=<date>, --after=<date> Show commits more recent than a specific date.
  • -until=<date>, --before=<date> Show commits older than a specific date.
  • --author=<pattern>, --committer=<pattern> Limit the commits output to ones with author/committer header lines that match the specified pattern (regular expression). With more than one -author=<pattern>, commits whose author matches any of the given patterns are chosen (similarly for multiple -committer=<pattern>).

Pretty formats

Tag

# create tag
git tag -a <tagname> -m '<tagcomment>'

# show tags
git tag
git tag -n

# show specified tag
git show <tagname>

# push to remote
git push origin <tagname>

# push all tags to remote
git push origin --tags

# checkout tag
git checkout <tagname>

# delete tag
git tag -d <tagname>

# delete remote tag
git push origin <tagname>
git push --delete origin tagname

Clone subfolder (sparse-checkout)

By following ways, you will get the folder fastest that you want.

git clone -n --depth=1 --filter=blob:none git@github.com:bndynet/bndynet.git
cd bndynet
git sparse-checkout set "/_data/*" --no-cone
git checkout

Or

git init
git sparse-checkout init --no-cone
git sparse-checkout set "/_data/*" --no-cone
git remote add origin git@github.com:bndynet/bndynet.git
git config core.sparsecheckout true
echo "partialclonefilter = blob:none" >> .git/config
git pull --depth=1 origin master

Subtree

# with squash, this repo history commits will not be merged into parent repo
git subtree add --prefix=subfolder https://github.com/subrepo.git master --squash
git subtree push --prefix=dist https://github.com/subrepo.git gh-pages

Submodule

git submodule add submodule-repo path
git submodule update --init --recursive
git submodule status
git submodule sync --recursive
git rm submodule-name
git rm submodule-name --cached
# update submodule to master
cd submodule_folder
git checkout master
git pull
cd ../
git add .
git commit -m ''

Workflow (fork / PR)

with Git: Fork, Branching, Commits, and Pull Request

  1. Fork a repo.

  2. Clone the forked project to your local machine:

    git clone git@github.com:USERNAME/<forked_repo>

  3. Configure remotes:

    git remote add upstream git://github.com/<origin_repo>

  4. Create a branch for new feature:

    git checkout -b my-new-branch

  5. Develop on my-new-branch branch only, but Do not merge my-new-branch branch to the your master (as it should stay equal to upstream master)!!

  6. Commit changes to my-new-branch branch:

    git add .
    git commit -m "commit message"
  7. Push branch to GitHub, to allow your mentor to review your code:

    $ git push origin my-new-branch

  8. Repeat steps 5-7 till development is complete.

  9. Fetch upstream changes that were done by other contributors:

    $ git fetch upstream

  10. Update local master:

git checkout master
git pull upstream master

ATTENTION: any time you lost of track of your code – launch “gitk —all” in source folder, UI application come up that will show all branches and history in pretty view, explanation.

  1. Rebase my-new-branch branch on top of the upstream master:
git checkout my-new-branch
git rebase master
  1. In the process of the rebase, it may discover conflicts. In that case it will stop and allow you to fix the conflicts. After fixing conflicts, use git add . to update the index with those contents, and then just run:
git rebase --continue
  1. Push branch to GitHub (with all your final changes and actual code):

We forcing changes to your issue branch(our sand box) is not common branch, and rebasing means recreation of commits so no way to push without force. NEVER force to common branch.

git push origin my-new-branch --force
  1. Created build for testing and send it to any mentor for testing.
  2. Only after all testing is done – Send a Pull Request.

Attention: Please recheck that in your pull request you send only your changes, and no other changes!! Check it by command: git diff my-new-branch upstream/master

QA / troubleshooting

.gitignore can not ignore a file

Need run following command to remove all files from the repository and add them back.

git rm -rf --cached .
git add .

Fix line endings issues (CRLF/LF)

git config --global core.autocrlf input # mac/linux
git config --global core.autocrlf true # windows (typical)
git config --global core.safecrlf warn

Remove file from history (BFG / filter-repo)

# Prefer git-filter-repo (recommended modern tool)
# After rewriting history, force-push is required.
git filter-repo --path <path> --invert-paths
git push --force --all
git push --force --tags

Common alias

git config --global alias.s "status"
git config --global alias.a "\!git add . && git status"
git config --global alias.au "\!git add -u . && git status"
git config --global alias.aa "\!git add . && git add -u . && git status"
git config --global alias.b "branch"
git config --global alias.c "commit"
git config --global alias.cm "commit -m"
git config --global alias.ca "commit --amend"
git config --global alias.ac "\!git add . && git commit"
git config --global alias.acm "\!git add . && git commit -m"
git config --global alias.l "log --graph --all --pretty=format:'%C(yellow)%h%C(cyan)%d%Creset %s %C(white)- %an, %ar%Creset'"
git config --global alias.ll "log --stat --abbrev-commit"
git config --global alias.lg "log --color --graph --pretty=format:'%C(bold white)%h%Creset -%C(bold green)%d%Creset %s %C(bold green)(%cr)%Creset %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"
git config --global alias.llg "log --color --graph --pretty=format:'%C(bold white)%H %d%Creset%n%s%n%+b%C(bold blue)%an <%ae>%Creset %C(bold green)%cr (%ci)' --abbrev-commit"
git config --global alias.d "diff"
git config --global alias.master "checkout master"
git config --global alias.main "checkout main"
git config --global alias.alias "\!git config --list | grep 'alias\.' | sed 's/alias\.\([^=]*\)=\(.*\)/\1\ => \2/' | sort"
git config --global alias.hi "\!echo 'Hello World!'"
git config --global --unset alias.hi