Jex’s Note

Git 常用指令

(最後更新: 2016-04-27)

git add

加入空的目錄

建立 gitkeep.keep 在空的目錄

git 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 merge

讓 commit log 紀錄您是開分支出去再 merge 回來的。

git merge --no-ff develop

Undo 某一個 merged 但未 push 的 branch

有幾種方法

1) git reset --hard HEAD~1

2) 先用 git reflog 看 log, 再把 HEAD@{1} 的 SHA1 記下來 (不是 HEAD@{0} 的, 因為那是你目前的點, 我們要還原到前一動), 再執行 git reset --hard {SHA1}

3) 用 git reset --hard {COMMIT_ID} 到早一點的點, 然候再慢慢補齊後來的 commit

如果對結果不太確定的話, 建議先用目前的 branch 建一個測試用的 branch 練習, 確定結果符合你要的再做在原本的 branch

git rebase

rebase 基本操作概念

類似 merge, 但將兩個不同的 branch 合併成同一條線且不會有 merge commit, tig 的線圖也會比較漂亮

1) rebase master

git checkout feature
git rebase master

2) merge 回 master

git checkout master
git merge feature

rebase 其他指令

如果有發生 conflict 並修好後要繼續 rebase

git rebase --continue

git merge 與 git 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 也能達到一樣效果

使用時機 :

git 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)

git commit

  • --amend : 修改最後一次commit message

git reset

  • --soft : 回復到之前的狀態, 但修改過的檔案仍不變
  • --hard : 回復到之前的狀態, 但修改過的檔案仍會保留

回復到某一個 commit id

git reset --hard {commit id}

回復上一個 commit (包含 merge) ,但條改的資料不保留,也就是回覆到上一個 commit 的初始狀態

git reset HEAD^ --hard

回復上一個 commit 但修改的資料保留下來

git reset HEAD^ --soft

回復到 origin/master 的狀態 (當 merge 或做了什麼後悔的事, 在還沒 push 前都可以使用此指令還原到 remote 的最新狀態)

git reset --hard origin/master

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

ref: stackoverflow

git 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 加回來

git cherry-pick

將某一個 branch 的某一個 commit 的變動 merge 到另一條 branch

假如 develop 只想要 merge feature 的某一個 commit, 則需要先把 feature 的那條 commit id 記下來, 再切到 develop 做 cherry-pick

git checkout develop
git cherry-pick f08515bc579a06dd9c8bd7f2dfc30ad4d5a73646

git show

查看某一個 tag 的變動

git show v1.0.0

git log

  • --stat : 列出有增減的檔案
  • --oneline : 精簡的log,每一次的commit資訊為一行,只顯示前7碼的SHA1及message

最近一次 commit 改過的檔案

git log -n 1 --stat

最近一次 commit 的更改細節

git log -n 1 -p

git reflog

顯示 git 的每個 log (包括操作 command 及 commit 內容等等)

git 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

git config

  • --list : 查看 config 內容,也可以用另種方式看 cat ~/.gitconfig
  • --unset : reset 某個 config. e.g. git config --global --unset {key}

git rm

  • --cache filename.php : 將已被 git 追蹤的檔案取消追蹤, 放心! 它並不會將檔案刪除, 而將狀態變成 Untracked
  • --cached filename.php : 結果似乎和 --cache 一樣

git push

強制 push, 之前的commit 紀錄會不見,只會有目前這個branch的commit 紀錄

git push origin master -f

建立遠端 branch

git branch dev          // 先建立 local branch
git push origin dev:refs/heads/dev

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

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

git tag

查看目前 tag

git tag
v1.0.0

新增 tag

git tag -a v1.4 -m "my version 1.4"
git push --tags

git fetch

更新 git branch -r 的名單

git fetch

(建議) 更新 remote branch list (遠端已刪除的 branch, 本機 branch 會幫你刪除,同時也會更新新增的 remote branch)

git remote update origin --prune

更新 remote branch list (遠端已刪除的 branch, 本機 branch 不會刪除,只會更新新增的 remote branch)

git fetch --all

當要 merge 遠端 branch 前,先看修改了什麼,再merge

git fetch origin dev
tig
git merge origin dev

git remote

新增遠端 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 rev-parse

latest commit hash

git rev-parse HEAD

remote branch

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

其他常用指令

  • git blame test.qq : test.php這份檔案顯示每行的編輯者
  • git branch -r : 查看遠端 branch
  • git add -u : 自動將所有的檔案都放到 stage 等待 commit, 而重要的是被刪除過的檔案也會一起放進去, 不需用 git rm 一個一個點被刪除過的檔案, 很方便!
  • tig : 顯示樹狀
  • gitk : 顯示樹狀 (GUI version)

prune 是在 grb delete 後才需要做的

減少 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 節點

ref : 小莊

更新從別人 fork 過來的 repository

IMAG1680.jpg

git remote add upstream git@...(略)...
git remote -v
git pull upstream master

註 :

git pull upstream master 也等於

git fetch upstream
git merge upstream/master

將 commit 的東西 merge 到 qa

commit 後copy commit id

git checkout qa
git pull
git cherry-pick d69ad27d92cad1021481ecab731c0cc6552432bf    // 只 merge 我剛修改的
git push
git log -1
git show 41e24b9349acc9bbc1b8853284866f498892ce4b // 查看此 commit 改了什麼

ref : 夯哥

查看 commit id 修改內容

git show 41e24b9349acc9bbc1b8853284866f498892ce4b

.gitignore not working

git rm -r --cached .
git add .
git commit -m ".gitignore is now working"

將 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`

Trouble shooting

git clone error

$ git clone https://github.com/jex-lin/conf.git
Cloning into 'conf'...
error: Problem with the SSL CA cert (path? access rights?) while accessing https://github.com/jex-lin/conf.git/info/refs
fatal: HTTP request failed

解決 :

git config --global http.sslVerify false

contributing (pull request)

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am ‘Add some feature’)
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

如何寫 commit messages

1) Subject 與 body 以一行空格分開

Derezz the master control program

MCP turned out to be evil and had become intent on world domination.
This commit throws Tron's disc into MCP (causing its deresolution)
and turns it back into a chess game.

這樣git log --oneline or git shortlog就可以只顯示主旨

2) Subject 不要超過 50 字元, 開頭要大寫, 結尾不需句號

3) 簡明的 subject

Refactor subsystem X for readability
Update getting started documentation
Remove deprecated methods
Release version 1.0.0
也可以用以下的句子
If applied, this commit will refactor subsystem X for readability
If applied, this commit will update getting started documentation
If applied, this commit will remove deprecated methods
If applied, this commit will release version 1.0.0
If applied, this commit will merge pull request #123 from user/branch

4) Body 在 72 字元換行,確保加上 git 本身的 indent 每行可以在 80 字元內

5) Body 用來解釋 what changed and why,主角不是 how

Simplify serialize.h's exception handling

Remove the 'state' and 'exceptmask' from serialize.h's stream
implementations, as well as related methods.

As exceptmask always included 'failbit', and setstate was always
called with bits = failbit, all it did was immediately raise an
exception. Get rid of those variables, and replace the setstate
with direct exception throwing (which also removes some dead
code).

As a result, good() is never reached after a failure (there are
only 2 calls, one of which is in tests), and can just be replaced
by !eof().

fail(), clear(n) and exceptions() are just never called. Delete
them.

ref : http://chris.beams.io/posts/git-commit/

其他參考 message

Other bug fixes
    * Fixed an issue that of forcibly terminating while running Peel remoate application.
    * The latest Android security patch has been applied.
    * Device security has been further enhanced.
Fixed the XXX bug in YYY

該用 present tense 還是 past tense?

兩邊都有支持者, git 本身的專案的 message 是用 present tense, 但有人覺得 present tense 是告訴別人將要做什麼, 用 past tense 比較正確, 但查了一輪下來, 支持 present tense 的人比較多一些, 我的建議是取決於團隊的 style, 當多數人用哪個就用哪個

tig

只顯示 merge 線圖

tig --merges

use Git’s default commit order

tig --topo-order

Comments