Git : 每一行命令都算數

git 思維導圖

Git 工作區域

爲了說明我們日常開發中執行的一系列 Git 命令的作用是什麼,我們需要了解 Git 的工作區域的概念,幾乎每一個常見的 Git 命令操作都可以通過工作區域來解釋。

Git 本地有四個工作區域:

四個工作區域

Git 文件狀態

接下來我們來看下 Git 文件的狀態:

Git 文件狀態

Git 基本命令

在瞭解 Git 工作區域、文件狀態以及本地倉庫的相關信息之後,相信大家對於日常使用的一些命令都有了更加深刻的理解。接下來,我們一起進行一個常用命令總結:

並且如果是單人開發,自己玩的情況下,貌似這些命令就足夠了。但是,在實際的開發當中,我們往往會面對更加複雜的場景,需要一些更爲複雜的命令來處理,我們接着往下看。

git merge

在當前分支上執行 git merge master 可以將 master 的提交合並 merge 到當前分支,也就是更新本地分支。我們日常開發中,將本地代碼推到遠程倉庫,建立 Merge Request,然後點擊 Merge 按鈕其實就是在 master 分支上 merge 了開發分支。

merge

git fetch

git fetch 可以將遠程分支拉到本地:

git fetch 會將所有遠程分支都拉到本地
git fetch origin sowhat1412  指定拉取遠程origin倉庫的sowhat1412分支到本地
git pull

當我們想要更新本地分支代碼的時候,需要將遠程開發分支或者遠程 master 分支代碼拉到本地,並且合併到當前開發分支上。所以 git pull = git fetch + git merge
在當前開發分 sowhat1412 上,我們執行如下的命令:

git pull origin master 
表示將遠程master分支拉到本地並且merge到當前分支sowhat1412上
git rebase

rebase 會把你當前分支的 commit 放到公共分支的最後面, 所以叫變基。就好像你從公共分支又重新拉出來這個分支一樣。
舉例: 如果你從 master 拉了個 feature 分支出來, 然後你提交了幾個 commit, 這個時候剛好有人把他開發的東西合併到 master 了, 這個時候 master 就比你拉分支的時候多了幾個 commit, 如果這個時候你 rebase master 的話,就會把你當前的幾個 commit,放到那個人 commit 的後面。

git rebeae

merge 會把公共分支和你當前的 commit 合併在一起,形成一個新的 commit 提交

git merge

git cherry-pick

對於多分支的代碼庫,將代碼從一個分支轉移到另一個分支是常見需求。

這時分兩種情況。一種情況是,你需要另一個分支的所有代碼變動,那麼就採用合併(git merge)。另一種情況是,你只需要部分代碼變動(某幾個提交),這時可以採用 Cherry pick。

git cherry-pick 命令的作用,就是將指定的提交(commit)應用於其他分支。

$ git cherry-pick <commitHash>

上面命令就會將指定的提交 commitHash,應用於當前分支。這會在當前分支產生一個新的提交,當然它們的哈希值會不一樣。

舉例來說,代碼倉庫有 master 和 feature 兩個分支。

 a - b - c - d       Master
         \
           e - f - g Feature

現在將提交f應用到 master 分支。

# 切換到 master 分支
$ git checkout master

# Cherry pick 操作
$ git cherry-pick f

上面的操作完成以後,代碼庫就變成了下面的樣子。

    a - b - c - d - f  Master
         \
           e - f - g   Feature

從上面可以看到,master 分支的末尾增加了一個提交 f。

git cherry-pick 命令的參數,不一定是提交的哈希值,分支名也是可以的,表示轉移該分支的最新提交。

$ git cherry-pick feature
上面代碼表示將feature分支的最近一次提交,轉移到當前分支。

Cherry pick 支持一次轉移多個提交。

$ git cherry-pick <HashA> <HashB>

上面的命令將 A 和 B 兩個提交應用到當前分支。這會在當前分支生成兩個對應的新提交。

如果想要轉移一系列的連續提交,可以使用下面的簡便語法。

$ git cherry-pick A..B

上面的命令可以轉移從 A 到 B 的所有提交。它們必須按照正確的順序放置:提交 A 必須早於提交 B,否則命令將失敗,但不會報錯。

注意,使用上面的命令,提交 A 將不會包含在 Cherry pick 中。如果要包含提交 A,可以使用下面的語法。

$ git cherry-pick A^..B
git reset
該命令執行之後,HEAD指針會移動到選中commitId上,並且之前的HEAD ->commitId之間的所有修改的內容會被置於工作區,需要重新add、commit。
該命令執行之後,HEAD指針會移動到選中commitId上,並且之前的HEAD ->commitId之間的所有修改的內容會被直接置於暫存區staged中,也就是後續只需要執行commit操作就OK了
這個--hard很好理解,就是在回滾HEAD指針的時候,很強硬的將所有HEAD ->commitId之間的改動內容“全部刪除”!
爲什麼加引號?因爲前邊我們說了Git不會丟失任何你提交過的內容(只要你玩的溜),後續我們會分析原因。
git reflog
  1. 新建一個目錄 git-test,初始化一下倉庫,做一次簡單的提交
mkdir git-test  
cd git-test/  
git init  
echo 0 > test  
git add .  
git commit -am "init"
  1. 假設這幾個星期你做了三次重要代碼的提交:
linux-geek:/home/tmp/git-test # echo "important code0" >> test  
linux-geek:/home/tmp/git-test # git commit -am "important code0"  
[master 03f85a1] important code0  
 1 files changed, 1 insertions(+), 0 deletions(-)  
linux-geek:/home/tmp/git-test # echo "important code1" >> test     
linux-geek:/home/tmp/git-test # git commit -am "important code1"  
[master 83547ba] important code1  
 1 files changed, 1 insertions(+), 0 deletions(-)  
linux-geek:/home/tmp/git-test # echo "important code2" >> test     
linux-geek:/home/tmp/git-test # git commit -am "important code2"  
[master b826ae5] important code2  
 1 files changed, 1 insertions(+), 0 deletions(-)   
linux-geek:/home/tmp/git-test # cat test  
0  
important code0  
important code1  
important code2
  1. 就在這個時候,假設你頭腦突然短路,用了 git reset 這條命令:
linux-geek:/home/tmp/git-test # git reset --hard HEAD~3  
HEAD is now at 2e71cf6 init

再來看一下代碼的情況:

linux-geek:/home/tmp/git-test # cat test
0

有沒有什麼辦法補救呢?答案是肯定的!

  1. 用 git reflog 來看一下
linux-geek:/home/tmp/git-test # git reflog
2e71cf6 HEAD@{0}: HEAD~3: updating HEAD
b826ae5 HEAD@{1}: commit: important code2
83547ba HEAD@{2}: commit: important code1
03f85a1 HEAD@{3}: commit: important code0
2e71cf6 HEAD@{4}: commit (initial): init

其中,我們可以清楚地看到 HEAD 每次移動的情況以及哈希值,於是乎我們再採用 git reset 去試一下:

linux-geek:/home/tmp/git-test # git reset --hard HEAD@{1}
HEAD is now at b826ae5 important code2
  1. 我們再來看一下代碼的情況:
linux-geek:/home/tmp/git-test # cat test
0
important code0
important code1
important code2
  1. 此時用 git log 或 git log --pretty=oneline 可以看到全部的歷史記錄又恢復回來了。
git revert

revert 是回滾某個 commit ,不是回滾到某個。git revert 是用於反做某一個版本,以達到撤銷該版本的修改的目的。比如,我們 commit 了三個版本(版本一、版本二、 版本三),突然發現版本二不行(如:有 bug),想要撤銷版本二,但又不想影響撤銷版本三的提交,就可以用 git revert 命令來反做版本二,生成新的版本四,這個版本四里會保留版本三的東西,但撤銷了版本二的東西。如下圖所示:

在這裏插入圖片描述

  1. 反做,使用 “git revert -n 版本號” 命令。如下命令,我們反做版本號爲 8b89621 的版本:
git revert -n 8b89621019c9adc6fc4d242cd41daeb13aeb9861
  1. 這裏可能會出現衝突,那麼需要手動修改衝突的文件。而且要 git add 文件名來提交
git commit -m "revert add text.txt"

手動整理 commit

有這樣一個需求,代碼量不多,但是因爲你多次提交,會導致在建立 Merge Request 的時候,出現了幾十個 commitId,比如:“update”、“bug fix”、“fix again” 等,不光看起來很醜,也會給大家一種感覺,這小夥到底行不行呀?一百行代碼的開發量,提交了這麼多次才搞定。(尷尬. jpg)
爲了解決這樣的問題,我們可以巧妙的利用 git reset。比如當前的 commit 是這樣的 A-1-2-3-4-5-6-7-8,你的第一個提交是 1,那麼我們執行如下的命令:

git reset --soft A // 重置本地分支HEAD指針
git commit -m "XXX邏輯開發"
git push origin ywq_news_0702 -f // 提交到遠程分支

-f 是什麼操作?force 的含義,表示強行執行本次操作。當我們 git reset 之後,本地的 HEAD 指針指向的 commitId 會比遠程 origin 對應的落後,直接 push 會被拒絕。通過 - f 命令可以強行將本地內容 push 到遠程分支上(切記!如果是多人共同合作的開發分支或者遠程 master 操作,千萬不能加 - f 操作!!!)
經過 git reset --soft 之後,我們提的 Merge Request 裏就是一個 commitId 了,發出來的 CR 會感覺倍兒有面兒。

Git stash 臨時儲藏

當我們在當前分支開發某個需求的時候,遇到了另一個需求的聯調問題,需要切換到另一個分支上去解決問題。怎麼辦?
正常情況下,我們應該將當前分支工作區的內容 add 、commit 之後再切換分支。但是問題來了,當前需求開發了一半,我不想生成一次提交怎麼辦?
放心,這個時候我們的 git stash 命令可以幫助我們將當前工作區的內容儲藏起來。然後切換其他分支,處理完問題之後,再切換到當前分支,執行 git stash pop 取出來就完事。

git stash list // 查看當前stash裏邊的內容
git stash // 將當前工作區內容儲藏起來
git stash pop // 將stash中棧頂內容pop出來,當然也可以根據順序直接取第n個

Git 恢復修改文件

對於恢復修改的文件,就是將文件從倉庫中拉到本地工作區,即 倉庫區 ----> 暫存區 ----> 工作區。

對於修改的文件有兩種情況:

只是修改了文件,沒有任何 git 操作,直接一個命令就可回退:
$ git checkout -- aaa.txt # aaa.txt爲文件名
即編輯之後,git add 但沒有 git commit -m ....
$ git log --oneline    # 可以省略
$ git reset HEAD    # 回退到當前版本
$ git checkout -- aaa.txt    # aaa.txt爲文件名
即編輯之後,git add 和 git commit -m ....
$ git log --oneline    # 可以省略
$ git reset HEAD^    # 回退到上一個版本
$ git checkout -- aaa.txt    # aaa.txt爲文件名

參考

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/F1lIs1whEF0hEA5SVWqsXQ