1 - Git

git

# mkdir blog && cd blog
# git init
# git remote add origin git@github.com:tukeof/tukeof.github.io.git
git clone git://github.com/tukeof/tukeof.github.io.git blog
cd blog
git add *.md
git commit -m 'initial project version'
git push -u origin master

remote

# 添加一个新的远程Git存储库
git remote add <shortname> <url>
git remote add origin git@github.com:tukeof/tukeof.github.io.git
git remote add origin ssh://username@127.0.0.1/reponame

git remote -v
git remote update
# 也会改变所有远程跟踪分支名称
git remote rename branch1 branch2
git remote rm branch1

git remote show origin

# 在本地新建一个tmp分支,并将远程origin仓库的master分支代码下载到本地tmp分支
git fetch origin master:tmp 
# 比较本地代码与刚刚从远程下载下来的代码的区别
git diff tmp 
# 合并temp分支到本地的master分支
git merge tmp
# 删除tmp分支
git branch -d tmp
# 取回远程主机某个分支的更新,再与本地的指定分支合并
git pull origin master:<branch_name>

add, rm, commit, log

# -a,--all,自动暂存已修改和删除的文件
git commit -a -m 'message'

# 从工作目录和暂存区域中删除文件
git rm <file>
# 仅从暂存区域中删除目录
git rm -r -cached <dir>

git mv README.md README
# mv README.md README
# git rm README.md
# git add README

# --amend
# 占用当前暂存区域并将其用于提交。如果自上次提交后没有进行任何更改,那么快照将看起来完全相同,并且将更改的是提交消息。
git commit --amend

# 撤销对暂存区的更改
git reset HEAD <file>
# 丢弃工作目录中的更改
git checkout - <file>

svn

# clone
svn co https://localhost/user/repo

svn co https://localhost/username/repo -r some_version
svn checkout https://localhost/username/repo/dir1/dir2/proj1

arc

# Phabricator
git clone https://github.com/phacility/libphutil.git
git clone https://github.com/phacility/arcanist.git

#  将 some_install_path/arcanist/bin 加入到$PATH变量中
# 权限认证
arc install-certificate
arc set-config editor "vim"

# 开分支(git branch),或查看当前分支和关联的 revision
arc feature 
# 创建或更新 revision,范围是 origin/master 到最新提交,包括暂存区的内容
arc diff
# 所有 revision 和状态
arc list
# 当前分支上的 commit 会全部被 land, 会先 pull --rebase 再 push
arc land
# 查看 diff 的范围等信息
arc which

Phabricator

// apply patch to current branch
arc patch --diff <patch_version_number> --nocommit --nobranch

2 - Git Checkout

branch

# list all branch, include local and remote
git branch -a
# delete branch
git branch -d <local_branch>
git push origin --delete <remote_branch>

# list tags
git log --pretty=oneline --abbrev-commit
git show <tag_name>
# tag a tag based on a commit, default cid is `HEAD`
git tag <tag_name> <commit_id>
git tag -a <tag_name> -m "message for tag" <commit_id>

# dangerous operation, it's better no-modification until checkout to `HEAD` 
git checkout <tag_name>
# 
git checkout -b <bra_name> <tag_name>

git tag -l "<prefix>*"
git push origin <tag_name>
# push all tags
git push origin --tags

# delete a local tag
git tag -d <tag_name>
# delete a remote tag
git push origin :refs/tags/<tag_name>

stash

# list the stash entries that you currently have. stash@{0} is the latest entry
git stash list
# save all uncommitted changes
git stash
# apply all uncommitted changes
git stash apply

merge-base

git merge-base origin/master HEAD

rebase

# Assume the following history exists and the current branch is "topic":
#       A - B - C topic
#      /  
# D - E - F -G master

git switch topic
git rebase master
git rebase master topic

# would be
#               A'--B'--C' topic
#              /
# D---E---F---G master
graph LR

E --> A --> B --> C(C topic)
D --> E --> F --> G(G master)
mkdir git-repo && cd git-repo
git init --bare
export remote_url=$(pwd)

cd ..
git clone $remote_url git
cd git

# D
echo $(uuidgen) > D && git add . && git commit -m 'D' && git push -u origin master
# D - E
echo $(uuidgen) > E && git add . && git commit -m 'E' && git push -u origin master

git checkout -b topic
git switch master
# D - E - F
echo $(uuidgen) > F && git add . && git commit -m 'F' && git push -u origin master
# D - E - F - G
echo $(uuidgen) > G && git add . && git commit -m 'G' && git push -u origin master

git switch topic
#       A
#      /  
# D - E - F
echo $(uuidgen) > A && git add . && git commit -m 'A' && git push -u origin topic
#       A - B
#      /  
# D - E - F
echo $(uuidgen) > B && git add . && git commit -m 'B' && git push -u origin topic
#       A - B - C topic
#      /  
# D - E - F master
echo $(uuidgen) > C && git add . && git commit -m 'C' && git push -u origin topic

revert

# revert to previous commit
git revert <previous-commit>

reset

# discard changes and reset files to master
git reset --hard origin/master

git reset --hard HEAD
# or save changes
git reset --soft HEAD 

# discard changes and reset to current branch
git checkout . && git clean -xdf

3 - Git Config

config

git help config
man git-config
git config --list

# 提交时转换为LF,检出时转换为CRLF
git config --global core.autocrlf true
# 提交时转换为LF,检出时不转换
git config --global core.autocrlf input
# 提交检出均不转换
git config --global core.autocrlf false

# 拒绝提交包含混合换行符的文件
git config --global core.safecrlf true
# 允许提交包含混合换行符的文件
git config --global core.safecrlf false
# 提交包含混合换行符的文件时给出警告
git config --global core.safecrlf warn

git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.s status

git config --global alias.unstage 'reset HEAD - '
git config --global alias.last 'log -1 HEAD'
# git config --global alias.l '!ls -lah'
git config --global alias.url 'remote get-url --all origin'

# git config --global core.editor "'C:/Options/Notepad++/notepad++.exe' -multiInst -nosession"
git config --global core.editor vim

.gitconfig

[user]
	name = yourname
	email = youremail
[http]
    #proxy = socks5://127.0.0.1:1080
[https]
    #proxy = socks5://127.0.0.1:1080	
[core]
    autocrlf = input
    safecrlf = true
[alias]
    co = checkout
    br = branch
    ci = commit
    s = statcus
    last = log -1 HEAD
    lg = log --graph
    url = remote get-url --all origin  

.ssh/config

ssh-keygen -t rsa -b 4096 -C "youremail"
host *
    AddKeysToAgent yes
    UseKeychain yes
    TCPKeepAlive yes
    ServerAliveInterval 60
    ServerAliveCountMax 5
    
host github.com
  user git
  hostname github.com
  port 22
  identityfile ~/.ssh/id_rsa

4 - Git Hook

pre-commit

# https://pre-commit.com/#usage
pip install pre-commit pre-commit-hooks
# write to .git/hooks/pre-commit
pre-commit install
exclude: >
  (?x)(
      ^a/|
      ^b/c/|
      ^d/e/__init__.py
  )  
default_language_version:
    python: python3
repos:
  - repo: https://github.com/PyCQA/isort
    rev: 5.9.3 # tag
    hooks:
      - id: isort # sort imports
  - repo: https://github.com/psf/black
    rev: stable
    hooks:
      - id: black # code formatter
  - repo: https://github.com/pycqa/flake8
    rev: 4.0.1
    hooks:
      - id: flake8 # check the style and quality
        language_version: python2.7
        stages: [manual]

pre-push

5 - Git How-To

undo untracked

git reset --hard <commit_id>

# show which will be removed, include untracked directories
# -n --dry-run, Don't actually remove anything
git clean -d -n
# delete anyway, no confirm
git clean -d -f

undo not staged change (removal or modification)

# in stage space but changed by external
# touch a && gti add a && rm a, then a has not staged changes

# discard changes in working directory
git restore <files>
# or
git checkout <files>
# update what will be committed
git add <files>

# caused by removal
git rm <files>
# or
git rm --cached <files>

undo add

# undo stage
git restore --staged <files>

# remove from staged space
git rm --cached d

undo commit

# message
git commit --amend -m 'message which will cover the old one'

# undo commit, but not undo add
# --soft, do not touch the index file nor the working tree
# --hard, match the working tree and index to the given tree
# --mixed, reset the index but not the working tree (default)
git reset --soft HEAD^
# undo <n> commits
git reset --soft HEAD~n

merge commit

# reapply n commits
git rebase -i HEAD~n

# pick
# squash
# ...
# squash

git add .
git rebase --continue
# git rebase --abort

git push --force

delete the last commit

# where git interprets x^ as the parent of x 
# + as a forced non-fastforward push. 
git push master +<second-last-commit>^:master

# or
git reset HEAD^ --hard
git push master -f

delete the second last commit

# this will open an editor and show a list of all commits since the commit we want to get rid of
# simply remove the line with the offending commit, likely that will be the first line
git rebase -i <second-last-commit>^

git push master -f

6 - Git Log

status

git init --bare /path/to/repo/.git
# git remote add origin /path/to/repo/.git
git clone /path/to/repo

# 显示工作目录和当前HEAD提交之间存在差异的路径
git status
# --short
# A added.c
# M modified.cc
# R renamed.h
# D deleted.hh
git status -s

diff

# 将工作目录中的内容与暂存区域中的内容进行比较
git diff
# --cached,将暂存更改与上次提交进行比较  
git diff --staged

git diff oldCommit..newCommit

log

# 按反向时间顺序列出在该存储库中进行的提交
git log
# -p,patch  显示每次提交中引入的差异(补丁输出)
# -2  仅显示最后两个条目
# --stat 在每个提交条目下方打印一个已修改文件的列表,已更改的文件数以及这些文件中添加和删除的行数
# --shortstat 仅显示--stat命令中已更改/插入/删除行。
# --pretty=oneline 在一行上打印每个提交
git log --pretty=oneline
# 短哈希 - 作者名,日期 :注释
git log --pretty=format:"%h - %an, %ar : %s"
# --graph 添加一个很好的小ASCII图,显示您的分支和合并历史记录
git log --pretty=format:"%h %s" --graph

git log -<n>
# --since, --after
# --until, --before
git log --since=2.weeks
git log --since="2008-01-15"
git log --since="2 years 1 day 3 minutes ago"

# --committer 执行提交的用户
# --author 作出修改的用户
# 在提交消息中搜索关键字
git  --author=<author> --grep="keyword"
# 可以指定--author和--grep搜索条件的多个实例
# 这将限制提交输出到与任何--author模式和任何--grep模式匹配的提交
# --all-match 会进一步将输出限制为与所有--grep模式匹配的提交

# 仅显示提交添加或删除与字符串匹配的代码的提交
git log -S function_name

Useful options for git log --pretty=format

OptionDescription of Output
%HCommit hash
%hAbbreviated commit hash
%TTree hash
%tAbbreviated tree hash
%PParent hashes
%pAbbreviated parent hashes
%anAuthor name
%aeAuthor email
%adAuthor date (format respects the –date=option)
%arAuthor date, relative
%cnCommitter name
%ceCommitter email
%cdCommitter date
%crCommitter date, relative
%sSubject