先來講說 Git 最本質的工做流程吧,從遠程倉庫拉取代碼到本地版本庫,簽出分支而後在本地工做區進行開發,經過暫存區將代碼提交到版本庫,最後再推送到遠程倉庫。這樣的流程不難理解,可是在實際團隊協做中,每每會涉及到本身的分支、其餘成員的分支、公共分支等分支的合併、解決衝突、推送、回退之類的操做。爲了梳理清楚 Git 的各類操做,本文將以 場景 + 解決方案 的形式組裝成操做手冊,便於本身往後查閱,以及團隊新成員能快速上手。git
Git 的核心概念分爲四個區塊,分別是遠程倉庫、本地版本庫、暫存區以及工做區,下面這張圖能清晰的描述出這四個區塊之間的關係。shell
全局說明:分支後的星號*表明的是當前分支。安全
場景 1. 假設現有兩個遠程分支 master、branchA,並已經拉取到本地版本庫,此時一同事刪除了遠程倉庫中的分支 branchA,可是你電腦上仍然能看到 origin/branchA ,怎樣才能更新倉庫呢?bash
解決方法: 獲取遠程倉庫的新分支以及刪除遠程倉庫已刪除的分支(本地版本庫中的分支)markdown
git fetch -p // --prune(修剪)的縮寫
複製代碼
場景 2. 本地新增提交記錄後,準備推到遠程前會先從遠程 git pull
拉取代碼進行更新,假設此時其餘同事已經推送新的代碼,爲何這時候拉取會自動產生一個(看似)沒有意義的提交記錄,怎樣讓提交記錄更直觀明瞭?app
原始提交記錄fetch
C---D origin/branchA
/
A---B---E branchA*
複製代碼
git pull 以後的提交記錄多了一個 Merge branch "XXX" into XXX
,也就是下例的 F 點spa
C---D origin/branchA
/ \
A---B---E---F branchA*
複製代碼
解決方法:指針
git pull --rebase
複製代碼
origin/branchA
|
A---B---C---D---E branchA*
複製代碼
git pull = git fetch + git merge // 拉取到本地版本庫 + 合併到本地工做區
git pull --rebase = git fetch + git rebase // 拉取到本地版本庫 + 工做區變基到最新版本庫日誌
場景 1. 怎麼從指定分支建立分支?
解決方法:
// 從目標分支建立本地分支,若是沒有指定目標分支,默認爲當前分支
git branch <name> [<targetBranch>]
// 建立並切換到該分支
git checkout -b <branchname> [<tragetBranch>]
複製代碼
場景 2. 怎麼將本地分支關聯到指定的遠程分支?
解決方法:
git branch --set-upstream-to=<origin/branchname>
// 或者
git branch -u <origin/branchname>
複製代碼
場景 3. 怎麼取消本地分支和遠程分支的關聯?
解決方法:
git branch --unset-upstream [<origin/branchname>]
複製代碼
場景 4. 怎麼查看分支以及其關聯的遠程分支的詳細信息?
解決方法:
git branch -vv
複製代碼
場景 5. 怎麼刪除本地分支?
解決方法:
git branch -D <branchname>
複製代碼
場景 6. 怎麼刪除遠程分支?
解決方法:
git push -d origin <branchname> // 或者 git push origin :branchname 複製代碼
場景 7. 怎麼移動/重命名分支?
解決方法:
// 將目標分支 移動/重命名 到新分支
git branch -m <branchname> [<targetBranch>]
複製代碼
場景 8. 怎麼合併分支?
解決方法:merge
合併
git merge <branchname>
複製代碼
狀況1:原始提交記錄
C---D feature
/
A---B master*
複製代碼
狀況1:合併以後的提交記錄
A---B---C---D master*、feature
複製代碼
狀況2:原始提交記錄
C---D feature
/
A---B---E master*
複製代碼
狀況2:合併以後的提交記錄
C---D feature
/ \
A---B---E---F master*
複製代碼
在多人協做開發場景下,merge 操做容易產生分支間的閉環,使得分支提交信息變得不清晰,不利於分支維護。
場景 9. 怎麼合併指定分支的指定提交記錄?
解決方法:cherry-pick
遴選
// 合併指定提交記錄
git cherry-pick <commit>
// 合併多個提交記錄
git cherry-pick <commit1> <commit2> <commit3>
// 若提交記錄在同一分支上,則能夠採用區間形式(start, end],commit1不包含,commit3包含
// 等價於合併了 commit二、commit3
git cherry-pick <commit1>..<commit3>
// 若省略了區間形式的起點,則起點默認爲兩個分支的交點
git cherry-pick ..<commit3>
複製代碼
原始提交記錄
C---D feature
/
A---B---E master*
複製代碼
git cherry-pick B..D
遴選以後的提交記錄
C---D feature / A---B---E---C'---D' master* 複製代碼
cherry-pick 操做會拷貝提交記錄到當前分支,使得當前分支的提交信息更爲清晰。
擴展:cherry-pick
操做若產生了衝突,處理流程以下
git cherry-pick --abort
(中斷則不須要後兩步操做)git add .
git cherry-pick --continue
場景 10. 從公共分支簽出特性分支進行新功能開發,開發完後準備合併回功公共分支,此時發現公共分支已經有其餘人推送新的提交記錄,此時怎麼以公共分支來更新特性分支?
**解決方法:**經過 rebase
變基,將當前分支的基點移動到目標分支上
// branchname 默認爲當前分支,可省略
git rebase <targetBranch> [<branchname>]
// 等價於下面兩條命令
git checkout <branchname>
git rebase <targetBranch>
複製代碼
原始提交記錄
C---D feature
/
A---B---E master*
複製代碼
git rebase master
變基以後的提交記錄
C'---D' feature* / A---B---E master 複製代碼
變基操做能更新當前分支的基點(起點),使當前分支包含公共分支上新的提交記錄,這樣有個好處就是若是和主分支存在衝突,能夠在特性分支上提早解決。
由於重塑了歷史提交記錄,因此變基後當前分支會和遠程分支不一致,須要採用強制推送(參見下文)覆蓋遠程分支。
擴展:rebase
操做若產生了衝突,處理流程以下
git rebase --abort
(中斷則不須要後兩步操做)git add .
git rebase --continue
場景 11. 怎麼合併多個提交記錄?
解決方法:git rebase --interactive
或者縮寫形式 git rebase -i
交互式變基
// commit 爲須要處理的提交記錄區間的父節點
git rebase -i <commit>
複製代碼
原始提交記錄
A---B---C---D feature*
複製代碼
須要合併 C、D 兩個提交記錄,git rebase -i B
pick a0a6eba feat: 新增 C 功能
pick 95f09e5 feat: 新增 D 功能
複製代碼
將 D 提交記錄合併進 C 中 ,因此把 D 記錄的 pick
改成 squash
,而後保存退出便可
pick a0a6eba feat: 新增 C 功能
squash 95f09e5 feat: 新增 D 功能
複製代碼
最終的提交記錄,E 點則包含了 C 和 D 的所有改動
A---B---E feature*
複製代碼
擴展:交互式變基過程當中會自動進入 vi 編輯模式,參數解釋以下
場景 1. 最近的一次提交記錄信息錯誤怎麼修改?
解決方法:
git commit --amend -m '新的提交信息' 複製代碼
場景 2. 最近的一次提交,發現遺漏了部分改動?
**解決方法:**出於提交信息的完整性考慮,在不新增提交記錄的狀況下將遺漏的改動合併到本次提交中。
// 將遺漏的文件改動添加到暫存區
git add <filename>
// 將暫存區中的全部文件合併到最近一次提交中,
// 若是不帶 --no-edit 參數,則在合併以後會進入提交信息修改面板
git commit --amend --no-edit
複製代碼
場景 1. 怎麼推送到遠程指定分支?
解決方法:
// 推送到遠程指定分支,並建立關聯
git push -u origin <branchname>
// 若本地分支已經與遠程分支關聯,則可省略遠程分支
git push
複製代碼
場景 2. 怎麼強制推送到遠程分支?
解決方法:
git push -f // --force 的縮寫 複製代碼
若在強制推送的過程當中,已經有其餘人推送到該遠程分支,則會使他人的提交記錄丟失,爲了更安全的推送
,可用以下命令。在他人推送了新的提交的狀況下,強制推送會被拒絕
。
git push --force-with-lease
複製代碼
擴展:須要強制推送的場景可能以下
git rebase
變基操做後git revert
操做)強制推送到遠程分支會覆蓋遠程,若不熟悉此命令,請慎用!
場景 1. 怎麼撤銷本地工做區文件的改動?
解決方法:
// 丟棄工做區某個文件的改動
git checkout -- <filePath>
// 丟棄工做區全部文件的改動
git checkout -- .
複製代碼
撤銷本地工做的改動後,文件會回退到最近一次 commit 或 add 狀態。
場景 2. 怎麼撤銷暫存區的改動?
解決方法:
// 當沒有指定 filename 時,表示撤銷暫存區中的全部文件
git reset HEAD <filename>
複製代碼
撤銷暫存區的改動後,文件會回到工做區狀態。
場景 3. 怎麼撤銷本地版本庫的改動?
解決方法:
// 回退到指定的提交記錄
git reset [<mode>] [<commit>]
複製代碼
經過指定 mode 參數,使回退以後的文件處於相應狀態:
- --soft:將當前分支重置到指定 ,當前版本與指定版本間的改動文件處於 暫存區 中,待提交狀態。
- --hard:重置 暫存區 和 工做區,自 以來暫存區和工做區中的任何修改都被 丟棄。
- --mixed(默認):將當前分支重置到指定 ,當前版本與指定版本間的改動文件處於 工做區 中,'not staged' 狀態。
擴展:HEAD 能夠理解爲一個指針,老是指向當前分支上最近一次的提交記錄。HEAD^ 表示上一個提交記錄,HEAD^^表示上兩個提交記錄,HEAD~n 表示上 n 個提交記錄。
HEAD^/HEAD~1
|
A---B---C---D---E---F branchA
| |
HEAD^^/HEAD~2 HEAD
複製代碼
場景 4. 下班前急匆匆的將代碼推到遠程倉庫,而後愉快的回家了,結果次日發現推遠程的代碼出錯了(手動滑稽 :p ),這時候怎麼撤銷遠程倉庫的改動?
解決方法:revert
逆向修改,而後從新提交併推送到遠程倉庫。
// 撤銷修改
git revert <commit>
// 推送到遠程倉庫,實現遠程倉庫的撤銷
git push
複製代碼
擴展:revert
和 reset
的區別
revert
是新增一次提交記錄,其修改內容正好抵消指定 的改動,而 reset
的撤銷效果是重置了版本庫。假設本地版本庫和遠程倉庫一致,reset
撤銷了一個提交,此時本地版本庫落後遠程倉庫一個版本,git push
推送到遠程會失敗,而 revert
新增了一個提交,本地版本庫領先遠程倉庫一個記錄,此時 git push
能夠正常推送到遠程。
場景 1. 本地 commit 提交了可是未推送到遠程倉庫,此時 git reset --hard
誤操做強制回滾,弄丟的提交記錄怎麼恢復?
**解決方法:**經過 git reflog
查看引用日誌,找到誤刪的提交記錄,而後回滾到這條被刪除的記錄
git reflog // Reference logs(引用日誌),能記錄 HEAD 和分支引用所指向的歷史
複製代碼
假設本地提交了三次 commit,而後誤操做強制回滾到第一次提交,致使第2、三次的提交所有丟失
// git reflog 引用日誌輸出格式以下
1c36188 HEAD@{0}: reset: moving to 1c36188
d921970 HEAD@{1}: commit: feature-3
1c002dd HEAD@{2}: commit: feature-2
1c36188 HEAD@{3}: commit (initial): feature-1
複製代碼
找到誤刪的提交記錄 feature-2 和 feature-3
git reset --hard d921970
複製代碼
場景 1. 當正在編寫代碼的時候,接到了一個緊急任務,須要切換分支去開發,此時未完成的代碼怎麼保存?
解決方法:
// 將所有未保存的代碼添加到貯藏區,若未填寫描述信息,則以上一次 commit 的信息記錄
git stash [push [-m <message>]]
複製代碼
場景 2. 怎麼查看貯藏區中保存的代碼?
解決方法:
git stash list
// 若存在貯藏的代碼,則輸出格式以下
stash@{0}: On feature: 新功能開發未完成,先貯藏一下 // 添加了描述信息
stash@{1}: WIP on master: 8e50dc3a feat:添加新功能xxx // 未填寫描述信息
複製代碼
場景 3. 怎麼取出貯藏的代碼?
解決方法:
// 恢復指定下標的貯藏代碼,並刪除對應的貯藏列表,index 默認爲 0
git stash pop [index]
// 等價於下面兩條命令
git stash apply [index] // 取出貯藏
git stash drop [index] // 刪除貯藏列表
複製代碼
場景 4. 怎麼清空貯藏列表?
解決方法:
git stash clear
複製代碼