4 minutes
為你自己學Git-讀書心得-part2
前言
接續前一篇,直接進入紀錄的部分。
分支相關指令
# 列出 local 分支
$ git branch
# 列出 remote 分支
$ git branch -r # 等於 --remote
# 列出 local + remote 分支
$ git branch -a
# 開新分支
$ git branch branch_name
# 在指定 commit 的位置開新分支
$ git branch branch_name 657fce7
# 更名分支
$ git branch -m old_name new_name
# 刪除分支,若該分支內容未備份於其他分支會給予提示並且不可刪除,此時使用大寫 -D 以強制刪除
$ git branch -d branch_name
# 切換分支
$ git checkout branch_name
# 存在分支則切換分支,不存在分支則新建分支並切換分支
$ git checkout -b branch_name
# 在指定 commit 的位置,存在分支則切換分支,不存在分支則新建分支並切換分支
$ git checkout -b branch_name 657fce7
# 切換到遠端分支時會發生 detached HEAD 狀態,使用追蹤參數在 local 建立追蹤分支,即可解決
$ git checkout -t origin/branch_name # 等於 --track
# 回復分支,紀錄或找到被刪除分支最新 commit 的 SHA-1 值則可回復
$ git branch branch_name b174a5a
# 列出所有節點紀錄,等同於 GUI 線圖
$ git show-branch -a
# 強制遷移 master branch 到指定位置,HEAD 要先移到其他位置,忘開分支就 commit 時可能有用
$ git branch -f master e12d8ef
合併分支相關
# 合併分支時,如果原分支比對要被合進來的分支,發現根莖相同,用線圖來看就是仍在一直線上,會啟用 快轉模式(Fast Forward),效果看起來是只平移原分支
# 合併分支時,如果原分支比對要被合進來的分支,發現根莖不相同,線圖類似 Y 形分岔,合併時會產生新的 commit,作為合併紀錄點,若發生 衝突(Conflict) 也會一併解決
# 使用以下指令可以總是產生分岔合併節點,即使刪除已合併過的分支,也可以在線圖上知道這裡曾經合併過
$ git merge branch_name --no-ff
# 重設基礎合併,此方法會將當前分支的每個 commit 照順序嫁接到指定的分支上
$ git rebase branch_name
# 此為危險操作,可用 Reflog 指令找到危險操作前最新 commit 的 SHA-1 值,並且執行 Reset 指令回復
# 或是使用危險操作皆有的額外紀錄點 ORIG_HEAD,執行 Reset 指令回復
$ git reset ORIG_HEAD --hard
# rebase 指令進行時,若發生 衝突(Conflict),處理方式和 merge 不同,此時看線圖會看到 HEAD 節點正卡出狀況的新移植節點上,此時將衝突解決,之後執行以下指令繼續 rebase
$ git rebase --continue
# 若衝突發生點不是文字檔而是圖片或其他二進位檔
$ git checkout --ours sample.jpg # 保留原分支檔
$ git checkout --theirs sample.jpg # 保留被合分支檔
# rebase 互動模式(interactively),開啟編輯器,編輯每筆 commit 前的動作,關閉後再逐筆修改需要再編輯的 commit
# 動作分別有,p(pick,預設,表使用該 commit)、r(reword,修改 commit message)、e(edit,停在該節點進 amend 流程)、s(squash,粉碎 commit,合到前一個 commit)、f(fixup,效果同 s,但不保留 commit message)、d(drop,移除 commit)
# 編輯動作時可直接調整順序,若整行刪除等同於 d 動作
# 常用狀況舉例,使用 s 動作處理多合一,使用 e 動作處理一分多(reset 之後,增加多筆 commit)
$ git rebase -i 657fce7
標籤相關
分兩種類型,輕量標籤(lightweight tag),適用於個人標記;有附註標籤(annotated tag),適用於版本發布
# 新增輕量標籤在當前 commit
$ git tag tag_name
# 新增輕量標籤在指定 commit
$ git tag tag_name 51d54ff
# 新增有附註標籤在指定 commit,-a 表要附註解,-m 效果同 commit 時
$ git tag tag_name 51d54ff -a -m "Big Cats are comming"
# 刪除標籤
$ git tag -d tag_name
# 顯示 commit 紀錄,any_point 可代入 標籤名 / SHA-1 值
$ git show any_point
暫存相關
可先 commit,之後再 reset 拆掉繼續做,或是使用 stash 指令放入暫存
# 新增暫存
$ git stash
# 新增暫存,連同 Untracked 的檔案
$ git stash -u
# 列出所有暫存,暫存訊息有預設格式,在哪個 branch + 當時在哪個 commit
$ git stash list
stash@{0}: WIP on branch_a: 053fb21 branch_a_latest_commit_message
stash@{1}: WIP on branch_b: b174a5a branch_b_latest_commit_message
# 取出暫存,不指定第幾個暫存則預設取編號最小
$ git stash apply stash@{1}
# 刪除暫存
$ git stash drop stash@{1}
# 取出並刪除暫存,等同於 apply + drop
$ git stash pop stash@{1}
撿其他 branch 的 commit
# 合併進當前位置
$ git cherry-pick 6a498ec
# 合併進當前位置,可一次撿多個,依順序
$ git cherry-pick fd23e1c 6a498ec f4f4442
# 撿過來但先不合併
$ git cherry-pick 6a498ec --no-commit
重要資料進版控,移除方法
- 軟刪除,還是有辦法復原
# 讓所有 commit 執行指定命令,刪除重要資料
$ git filter-branch --tree-filter "rm -f config/database.yml"
# 復原
$ git reset refs/original/refs/heads/master --hard
- 徹底刪除
# 讓所有 commit 執行指定命令,刪除重要資料
$ git filter-branch -f --tree-filter "rm -f config/database.yml"
# 刪除執行 filter-branch 產生的還原點
$ rm .git/refs/original/refs/heads/master
# 清除 Reflog 流程,強制讓紀錄過期,之後手動執行回收機制
$ git reflog expire --all --expire=now
# 前一行已讓紀錄過期,檢視過期的未追蹤檔
# 不加 --unreachable 則檢視所有待過期項目,包含 commit、tree、blob 物件等等
# 加 --no-reflogs 則額外檢視目前只存在於 Reflog 中的待過期項目
# 待過期項目有兩種狀態,Unreachable、Dangling,大部分會是 Unreachable,此狀態表示該項目在當前保留節點的周圍;Dangling 則表示完全與當前節點無關項目
# 舉例來說,A -> B -> C -> D,reset 後兩個節點,剩下 A -> B,此時 C 的狀態是 Unreachable,D 則是 Dangling
$ git fsck --unreachable
# 執行回收機制
$ git gc --prune=now # 等同於 git gc & git prune --expire=now
# 若已推到 remote 則強制覆蓋
$ git push -f
remote 相關
# 增加 remote,origin 是慣例用詞,代表遠端,可任意命名,最後一長串是遠端伺服器的連線位置
$ git remote add origin git@github.com:GitHub_id/repository_name.git
# 從 local 推到 remote,此時是將 local 的 master branch 推到 remote 的 master branch
# 參數 -u(--set-upstream),設定 local 端分支的上游,會 追蹤(track) 並默認為推送目標,設定一次之後,下次推送只要 git push,每個分支只有一個上游
$ git push -u origin master
# 上面的推送指令拿掉參數等同於下面的指令
$ git push origin master:master # 最後的分支名對應 local branch:remote branch,可以讓 remote 用不同名字
# 強制推送,少用
$ git push -f
# 取得遠端更新內容
$ git fetch
# 偽更新 local
$ git pull # 實際等同於 git fetch & git merge
# 偽更新 local,改用 rebase
$ git pull --rebase
# 檢視所有 remote 資訊
$ git remote -v
# 刪除遠端分支
$ git push origin :branch_name # 概念大概是推一個空的分支到遠端
# 複製 remote 專案
$ git clone git@github.com:sample_user/project_name.git
# 複製 remote 專案,並在 local 用不同的資料夾名
$ git clone git@github.com:sample_user/project_name.git new_project_name
fork
fork 其他人的專案時,為同步原專案最新進度,進行以下流程
# 增加 remote,upstream 名為慣例用詞
$ git remote add upstream git@github.com:GitHub_id/repository_name.git
# 取得遠端更新內容
$ git fetch upstream
# 合併遠端分支
$ git merge upstream/master
# 更新 fork 過來的專案
$ git push origin master
更新檔 相關
產生 patch 檔案,從來沒用過
# 範例 git log
$ git log --oneline
6e6ed76 (HEAD -> master) add product page
6aba968 update info.html
fd7cd38 (origin/master, origin/HEAD) Update about.html
2eb8fea add readme
953cbd9 update info page
# 產生更新檔,指定範圍,有點像 slice
$ git format-patch fd7cd38..6e6ed76
0001-update-info.html.patch
0002-add-product-page.patch
# 產生更新檔,指定區間,-2 代表最新兩筆 commit
$ git format-patch -2
0001-update-info.html.patch
0002-add-product-page.patch
# 產生更新檔,-o 參數指定輸出位置
$ git format-patch -2 -o /tmp/patches
/tmp/patches/0001-update-info.html.patch
/tmp/patches/0002-add-product-page.patch
# 使用更新檔
$ git am /tmp/patches/*
Git Flow
- master 穩定、可上線的版本,更新只能從別的分支合併過來,通常會加版本標籤
- develop 開發主幹,新增功能時從這裡切 feature branch 出去,完成後再合回來
- hotfix 線上版本緊急修復,從 master 切出去,完成後合到 master、develop
- release 上線前的測試分支,從 develop 切出或合併,測完後合到 master、develop,要合到 develop 是因為有些狀況要到 production 環境才會遇到,修復後合回開發分支避免之後再遇到
- feature 新功能分支,從 develop 切出,完成後合回 develop
二分法除錯
適用在大範圍 commit,不確定何時開始出錯的狀況
# 開始執行二分法,執行這行後,HEAD 會跳到兩點正中間的 commit
$ git bisect start 起始點 結束點
$ git bisect start HEAD f6b72af
# 確認該 commit 是否已出問題
$ git bisect good # 沒問題,執行這行,再跳到 HEAD 和 第一個中間點中間
$ git bisect bad # 有問題,執行這行,再跳到 f6b72af 和 第一個中間點中間,若找到出問題的 commit 就結束流程
# 中途放棄流程
$ git bisect reset
建立新目錄的分支,此方法適用於正在跑 CI/CD 流程時,仍想處理其他需求的狀況,Git v2.6.0 以上才有
$ git worktree add -b cat ../copycat
# 此方法會再與專案同一層的位置建立目錄,最後一樣可以合併回來,處理完後,新的目錄即可刪除