Git 實戰手冊

1、前言

先來講說 Git 最本質的工做流程吧,從遠程倉庫拉取代碼到本地版本庫,簽出分支而後在本地工做區進行開發,經過暫存區將代碼提交到版本庫,最後再推送到遠程倉庫。這樣的流程不難理解,可是在實際團隊協做中,每每會涉及到本身的分支、其餘成員的分支、公共分支等分支的合併、解決衝突、推送、回退之類的操做。爲了梳理清楚 Git 的各類操做,本文將以 場景 + 解決方案 的形式組裝成操做手冊,便於本身往後查閱,以及團隊新成員能快速上手。git

2、必備知識點

Git 的核心概念分爲四個區塊,分別是遠程倉庫、本地版本庫、暫存區以及工做區,下面這張圖能清晰的描述出這四個區塊之間的關係。shell

Git 核心區塊

全局說明:分支後的星號*表明的是當前分支。安全

3、拉取篇

場景 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 // 拉取到本地版本庫 + 工做區變基到最新版本庫日誌

4、分支篇

場景 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 操做若產生了衝突,處理流程以下

  1. 解決衝突,或者想中斷這次操做 git cherry-pick --abort(中斷則不須要後兩步操做)
  2. 添加到暫存區 git add .
  3. 繼續遴選 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 操做若產生了衝突,處理流程以下

  1. 解決衝突,或者想中斷這次操做 git rebase --abort(中斷則不須要後兩步操做)
  2. 添加到暫存區 git add .
  3. 繼續變基 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. p,pick 選中
  2. r,reword 選中,而且修改提交信息
  3. e,edit 選中,rebase 時會暫停,容許你修改這個 commit
  4. s,squash 選中,會將當前 commit 與上一個 commit 合併
  5. f,fixup ,與 squash 相同,但不會保存當前 commit 的提交信息
  6. x,exec 執行其餘 shell 命令

5、提交篇

場景 1. 最近的一次提交記錄信息錯誤怎麼修改?

解決方法:

git commit --amend -m '新的提交信息'
複製代碼

場景 2. 最近的一次提交,發現遺漏了部分改動?

**解決方法:**出於提交信息的完整性考慮,在不新增提交記錄的狀況下將遺漏的改動合併到本次提交中。

// 將遺漏的文件改動添加到暫存區
git add <filename>

// 將暫存區中的全部文件合併到最近一次提交中,
// 若是不帶 --no-edit 參數,則在合併以後會進入提交信息修改面板
git commit --amend --no-edit
複製代碼

6、推送篇

場景 1. 怎麼推送到遠程指定分支?

解決方法:

// 推送到遠程指定分支,並建立關聯
git push -u origin <branchname>

// 若本地分支已經與遠程分支關聯,則可省略遠程分支
git push
複製代碼

場景 2. 怎麼強制推送到遠程分支?

解決方法:

git push -f // --force 的縮寫
複製代碼

若在強制推送的過程當中,已經有其餘人推送到該遠程分支,則會使他人的提交記錄丟失,爲了更安全的推送,可用以下命令。在他人推送了新的提交的狀況下,強制推送會被拒絕

git push --force-with-lease
複製代碼

擴展:須要強制推送的場景可能以下

  • 分支 git rebase 變基操做後
  • 將錯誤代碼推送到了遠程,想要丟棄此提交記錄(建議用下文介紹的 git revert 操做)

強制推送到遠程分支會覆蓋遠程,若不熟悉此命令,請慎用!

7、撤銷篇

場景 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
複製代碼

擴展:revertreset 的區別

revert 是新增一次提交記錄,其修改內容正好抵消指定 的改動,而 reset 的撤銷效果是重置了版本庫。假設本地版本庫和遠程倉庫一致,reset 撤銷了一個提交,此時本地版本庫落後遠程倉庫一個版本,git push 推送到遠程會失敗,而 revert 新增了一個提交,本地版本庫領先遠程倉庫一個記錄,此時 git push 能夠正常推送到遠程。

8、日誌篇

場景 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
複製代碼

9、貯藏篇

場景 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
複製代碼
相關文章
相關標籤/搜索