add
Add a new file or folder
git add example.txt
Add all untracked files and folders
git add .
branch
- 都不加 : 顯示 local branch
-r
: 顯示 remote branch
-a
: 顯示所有 branch (local + remote)
-v
: 顯示branch的詳細資料
--merged
: 列出已經merge的branch
--no-merged
: 列出尚未merge的branch
Rename branch
git branch -m {old_name} {new_name}
Rename current branch
git branch -m {new_name}
刪除branch
git branch -d developXD
git branch -D {branch name}
git branch —no-merged
Remove outdated local branch
git checkout master
git branch —merged
or git branch -vv
[origin/bug/more-phan-fixes: gone] [REFACTOR] Phan fixes
blame
Show the editor of each line
git blame test.qq
merge
讓 commit log 紀錄您是開分支出去再 merge 回來的。
git merge --no-ff develop
Undo 某一個 merged 但未 push 的 branch, 有幾種方法:
git reset --hard HEAD~1
- 先用
git reflog
看 log, 再把 HEAD@{1}
的 SHA1 記下來 (不是 HEAD@{0}
的, 因為那是你目前的點, 我們要還原到前一動), 再執行 git reset --hard {SHA1}
- 用
git reset --hard {COMMIT_ID}
到早一點的點, 然候再慢慢補齊後來的 commit
如果對結果不太確定的話, 建議先用目前的 branch 建一個測試用的 branch 練習, 確定結果符合你要的再做在原本的 branch
rebase
rebase 基本操作概念
-
rebase master
git checkout feature
git rebase master
-
merge 回 master
git checkout master
git merge feature
類似 merge, 但將兩個不同的 branch 合併成同一條線且不會有 merge commit, tig 的線圖也會比較漂亮
如果有發生 conflict 並修好後要繼續 rebase
git rebase --continue
修改 past commit message
git rebase -i 637bd5ac^
Choose “reword” for the commit in question, then you’ll have an opportunity to edit the message.
conflict standard vs diff3
Example 1
[standerd]
<<<<<<< HEAD
GreenMessage.send(include_signature: true) // HEAD
=======
BlueMessage.send(include_signature: false) // target branch
>>>>>>> merged-branch
[diff3]
<<<<<<< HEAD
GreenMessage.send(include_signature: true) // HEAD
||||||| merged common ancestor
BlueMessage.send(include_signature: true) // origin
=======
BlueMessage.send(include_signature: false) // target branch
>>>>>>> merged-branch
[Solution]
GreenMessage.send(include_signature: false)
Example 2
[standard]
List Of Best Emojis
Rainbow 🌈
<<<<<<< HEAD
Pizza 🍕
Unicorn 🦄
=======
Avocado 🥑
>>>>>>> topic-branch
[diff3]
List Of Best Emojis
Rainbow 🌈
<<<<<<< HEAD
Pizza 🍕
Unicorn 🦄
||||||| merged common ancestors
Pizza 🍕
=======
Avocado 🥑
>>>>>>> topic-branch
[solution]
Rainbow 🌈
Pizza 🍕
Unicorn 🦄
Avocado 🥑
Edit specific commit
git rebase -i HEAD~3
- Mark
e
for the commit that you want to change
- Add or remove files
git commit —amend
git rebase —continue
rebase a specific commit
- (edit files)
- git add
- git commit –fixup {Parent commit id}
- git rebase -i HEAD~3 (assume we want to rebase on the second commit)
- mark fixup on the commit that you want it to go (called commit C)
- change the order of commit C to middle, because it will rebase onto the commit above it
merge vs rebase
兩者結果是一樣的,產生的 history 不同點 :
- git merge
- 當 dev merge master, dev 會在另一條線做 merge, 所以 history 會看起來比較亂
- conflict 只需要處理一次
- git rebase
- 當 dev rebase master, dev 會接在 master 最後面, 即使 master 有新的 commit 也會接在最後面, 所以它的 history 會很漂亮, 就只有一條
- conflict 需要一個一個處理, 假如 dev 有兩個新的 commit id, 就需要處理兩次
- 或用
git pull --rebase
也能達到一樣效果
checkout
新增 develop branch 並且切換過去
$ git checkout -b develop
新增由 develop 分支出來的 myfeature
$ git checkout -b myfeature develop
使檔案回復成最近一次commit的狀態
$ git checkout -- test.php
強制回復己在 add 狀態被修改過的檔案, Untracked files 則不受影響
git checkout -f
所有track中且修改過的檔案回復成最近一次commit的狀態
git checkout .
切到某一個 commit
git checkout 62a4a5c9a6e8a323a1ea12ec54ac35da7ce1b662
從某個 commit 切回原本 branch
git checkout -
相當於 git checkout master (不一定是 master 取決於在哪一個 branch)
commit
--amend
: 修改最後一次commit message
How to write commmit message
- add a new line between subject and body
- word count should be below 50 words
- use imperative mood and present tense
- e.g. Add feature
- e.g. Fix bug
- categories
- Initial
- Add
- Update
- Refactor
- Fix
- Build
- Remove
reset
--soft
: 回復到之前的狀態, 但修改過的檔案仍不變
--hard
: 回復到之前的狀態, 但修改過的檔案仍會保留
回復到某一個 commit id
git reset --hard {commit id}
回復上一個 commit (包含 merge)
git reset HEAD^ --hard
但修改的資料不保留,也就是回覆到上一個 commit 的初始狀態
回復上一個 commit 但修改的資料保留下來
git reset HEAD^ --soft
回復到 origin/master 的狀態
git reset --hard origin/master
當 merge 或做了什麼後悔的事, 在還沒 push 前都可以使用此指令還原到 remote 的最新狀態
git reset HEAD^ --hard
如何救回
git reflog # 會有你的每條 git 操作的 log, 左邊有 commit id, 記下你要回復的 commit id
git reset --hard bceefb7 # 這樣就可以救回了
(補充)如果不放心可以自行測試
$ git init
Initialized empty Git repository in .git/
$ echo "testing reset" > file1
$ git add file1
$ git commit -m 'added file1'
Created initial commit 1a75c1d: added file1
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 file1
$ echo "added new file" > file2
$ git add file2
$ git commit -m 'added file2'
Created commit f6e5064: added file2
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 file2
$ git reset --hard HEAD^
HEAD is now at 1a75c1d... added file1
$ cat file2
cat: file2: No such file or directory
$ git reflog
1a75c1d... HEAD@{0}: reset --hard HEAD^: updating HEAD
f6e5064... HEAD@{1}: commit: added file2
$ git reset --hard f6e5064
HEAD is now at f6e5064... added file2
$ cat file2
added new file
revert
與 reset 不同的事他回復是會有紀錄的,log 會疊下去,所以如果你不小心 push 了一個 commit 到 master,你想回復,用 revert 會比 reset 簡單操作
回復上一個 commit
git revert HEAD
回復到某一個 commit id
git revert {commit_id}
將某一個已經 commit/merge 的 commit_id 回復
git revert --strategy resolve 305a9bd07ae40c585ac2f2761dea4374e7fee93e
回復後它會有一個 commit 紀錄, 如果想再把它加回來, 無法用 merge (Already up-to-date.
), 但可以用 cherry-pick 加回來
cherry-pick
-x
: Preserve original commit id
在某一個branch加上每個commit(from another branch)
git checkout develop
git cherry-pick f08515bc579a06dd9c8bd7f2dfc30ad4d5a73646
將某一個 branch 的某一個 commit 的變動 merge 到另一條 branch, 假如 develop 只想要 merge feature 的某一個 commit, 則需要先把 feature 的那條 commit id 記下來, 再切到 develop 做 cherry-pick
rerere
It is used for cherry-pick
Especially for fixing conflict when cherry-pick latest commits from develop branch against an old tag commit
Basically, it records the change you made and apply the fixes to next conflicts
See more: https://git-scm.com/docs/git-rerere
show
查看某一個 tag 的變動
git show v1.0.0
查看 commit id 修改內容
git show 41e24b9349acc9bbc1b8853284866f498892ce4b
log
--stat
: 列出有增減的檔案
--oneline
: 精簡的log,每一次的commit資訊為一行,只顯示前7碼的SHA1及message
最近一次 commit 改過的檔案
git log -n 1 --stat
最近一次 commit 的更改細節
git log -n 1 -p
reflog
顯示 git 的每個 log (包括操作 command 及 commit 內容等等)
diff
- default or
--
: 非 staged 裡的與目前版本的差異
--staged
or --cached
(1.6版前) : stage 裡的與目前版本的差異
HEAD^
: 比較上一個commit的差異
branch
: 比較branch的差異
--stat
: 查看差異的概要 (顯示一堆 +
, -
)
sha1..sha1
: 比較兩個commit差異
未加入 stage 時直接下
git diff qq.php
已加入 stage 要下
git diff HEAD qq.php
比較兩個 branch
git diff develop master
diff-tree
List modified files on a specific commid id
git diff-tree --no-commit-id --name-only -r cf5f610b67ce52c8d6e39a526e6405675f430bd8
config
--list
: 查看 config 內容,也可以用另種方式看 cat ~/.gitconfig
--unset
: reset 某個 config. e.g. git config --global --unset {key}
將 diff 以 vimdiff 方式顯示
git config --global diff.tool vimdiff # 使用 difftool 就可以啟動 vimdiff
git config --global difftool.prompt false # 執行上面指令, 會問你是否執行, 很麻煩所以關掉它
git config --global alias.d difftool # 使用 `git d` 相當於 `git difftool`
rm
--cache filename.php
: 將已被 git 追蹤的檔案取消追蹤, 放心! 它並不會將檔案刪除, 而將狀態變成 Untracked
--cached filename.php
: 結果似乎和 --cache
一樣
push
強制 push, 之前的commit 紀錄會不見,只會有目前這個branch的commit 紀錄
git push origin master -f
(建議) 強制 push, 但不想覆蓋別人已經推的
git push --force-with-lease
when you do need to do a forced push but still ensure you don’t overwrite other’s work
建立遠端 branch
git branch dev // 先建立 local branch
git push origin dev:refs/heads/dev
push to a private repo
git push https://dummy@github.com/dummy/example.git
pull
git pull --rebase
等於以下兩步
git fetch
git merge origin dev
git pull –rebase
會使 merge commit 不見, 改用 :
git fetch origin
git rebase -p origin/develop
在目前 branch 下 git pull origin {branch_name}
: 代表將 {branch_name} (remote branch) merge 到目前這個 branch
pull from a private repo
git pull https://dummy@github.com/dummy/example.git
stash
放進暫存
git stash
查看暫存
git stash list
取出暫存
git stash pop
清空暫存
git stash clear
如果暫存有多個,可以指定要還原哪個commit ID
git stash [commit ID]
stash specific files
git add a1.go // files that You don't want to stash
git stash save --keep-index // this command will stash the rest of files
tag
查看目前 tag
git tag
v1.0.0
新增 tag
git tag -a v1.4 -m "my version 1.4"
git push --tags
fetch
更新 git branch -r
的名單
git fetch
更新 remote branch list
git fetch --all
遠端已刪除的 branch, 本機 branch 不會刪除,只會更新新增的 remote branch
當要 merge 遠端 branch 前,先看修改了什麼,再merge
git fetch origin dev
tig
git merge origin dev
clean up local cache of remote branch
git fetch —prune
remote
(建議) 更新 remote branch list
git remote prune origin
或
git remote update origin --prune
遠端已刪除的 branch, 本機 branch 會幫你刪除,同時也會更新新增的 remote branch
新增遠端 repo
git remote add origin https://git.heroku.com/my-app.git
查看remote有哪些branch
git branch -r
git remote show origin
有時候用 git branch -r
, 查看 remote branch 時, 發現明明刪掉的 branch 為何還在? 使用 git remote prune origin
來更新暫存檔, 就可以取得最新的 remote branch list
git pull branch from forked repo
cd into/cloned/fork-repo
git remote add upstream git://github.com/ORIGINAL-DEV-USERNAME/REPO-YOU-FORKED-FROM.git
git fetch upstream
git pull upstream master
show remote origin list
git remote -v
Update local repo and remote repo on github
- Update github repo via settings
git remote set-url origin git@github.com:username/your-repo.git
git remote -v
to check if it’s all set
- rename local folder
- done
- (optional for go project) update module name in
go.mod
rev-parse
latest commit hash
git rev-parse HEAD
clean
Remove untracked file and directory.
git clean -df
submodule
Basic commands
Add a new submodules
git submodule add https://github.com/msanders/snipmate.vim.git bundle/snipmate.vim
Create the local config file for submodules if it doesn’t exist
git submodule init
Fetch all data from submodules (after cloning a repo)
git submodule update
it will check out the commit that was most recently registered in the main repository. This does not necessarily align with the most recent commit in the submodule’s remote repository
Pull the latest changes from all submodules
git submodule update --remote
git will ignore the commit recorded in the parent repository, and instead, it will fetch and check out the latest commit from the remote of the submodule
Clone a repo and automatically clone all its submodules
git clone --recurse-submodules https://github.com/msanders/snipmate.vim.git
Remove a submodule
Delete the relevant section from .gitmodules
and .git/config
oldPath="bundle/snipmate.vim"
git config -f .git/config --remove-section "submodule.${oldPath}"
git config -f .gitmodules --remove-section "submodule.${oldPath}"
Run git rm –cached path_to_submodule (no trailing slash).
$ git rm --cached "${oldPath}"
Commit and delete the untracked submodule files.
rm 'bundle/snipmate.vim'
rm -rf "${oldPath}"
rm -rf ".git/modules/${oldPath}"
git add .gitmodules
git commit -m "Removed ${oldPath}"
tig (not built-in command)
只顯示 merge 線圖
tig --merges
use Git’s default commit order
tig --topo-order
Operation in practice
Add a new submodule
git submodule add https://github.com/test-user/practice_submodule.git
git submodule init
Pull submodule for the first time
git submodule init
git submodule update
Pull submodule in the existing repo
git submodule update
Remote branch operations
create
git push origin develop:refs/heads/branch_to_create
git fetch origin
git branch --track branch_to_create origin/branch_to_create
git checkout branch_to_create
track
git fetch origin
git branch --track branch_to_track origin/branch_to_track
delete remote branch
git push origin --delete <branch_name>
rename
# 如果 branch 已存在要先 remove branch
git push origin --delete <branch_name>
git push origin develop:refs/heads/branch_to_rename # 這步只是將遠端 develop copy 成 branch_to_rename, develop 還在
git fetch origin
git branch --track branch_to_rename origin/branch_to_rename
git checkout branch_to_rename
git push origin :refs/heads/develop
git branch -D develop
publish
git push origin branch_to_publish:refs/heads/branch_to_publish
git fetch origin
git branch -u origin/branch_to_publish branch_to_publish
git checkout branch_to_publish
減少 Merge branch 'master' of XXX
這種多餘節點
加上--rebase
:
git pull --rebase
若在不同的 branch
要接遠端的 master
更新的話,執行:
git pull --rebase origin master
不過使用--rebase
是在沒有conflict
的情況下使用,它並不會像git merge
那樣聰明地處理conflict
,原理是rebase
並沒有參考parent
節點做同步
遇到conflict
作法
如果產生了衝突回復 pull
前的狀態 :
git rebase --abort
然候合併code與 master
同步 :
git merge origin master
但這樣就還是會產生 Merge branch 'master' of XXX
節點
更新從別人 fork 過來的 repository

git remote add upstream git@...(略)...
git remote -v
git pull upstream master
註 :
git pull upstream master
也等於
git fetch upstream
git merge upstream/master
Fixup and Squash
Fixup a specific commit id
git commit --fixup fb2f677
squash commits into one
git rebase -i HEAD~2
git rebase -i --autosquash ac5db87 (previous commit id)
Mark squash
or fixup
at the beginning of commits
ref
Troubleshooting
.gitignore not working
git rm -r --cached .
git add .
git commit -m ".gitignore is now working"
git clone error
$ git clone https://github.com/xxx/ccc.git
Cloning into 'conf'...
error: Problem with the SSL CA cert (path? access rights?) while accessing https://github.com/xxx/ccc.git/info/refs
fatal: HTTP request failed
解決 :
git config --global http.sslVerify false
contributing (pull request)
- Fork it
- Create your feature branch (git checkout -b my-new-feature)
- Commit your changes (git commit -am ‘Add some feature’)
- Push to the branch (git push origin my-new-feature)
- Create new Pull Request
Code review best practices
- Context
- What is the code doing?
- Know the scope of the code
- Readability
- Coding style - Is the code wrtten in a similar style to other code in the project?
- Are variables named appropriately? Can it be easy to understand in the future?
- Reusability
- Does the code follow SOLID principles?
- Refactor the code if needed
- Is it easy to be maintained? Would it introduce new tech debt?
- Security
- Does the code handle errors well?
- Does the code introduce vulnerabilities?
- Does the code filter user input carefully?
- Does the code output contain any confidential infomation?
- Stability
- Does the code manage concurrency properly?
- Does the code introduce race condition?
- Is the sql performant enough?
- Test Coverage
- Does it have enough unit or integration tests to cover its functionalities?
ref: