Git : 每一行命令都算數
git 思維導圖
Git 工作區域
爲了說明我們日常開發中執行的一系列 Git 命令的作用是什麼,我們需要了解 Git 的工作區域的概念,幾乎每一個常見的 Git 命令操作都可以通過工作區域來解釋。
Git 本地有四個工作區域:
-
工作區(Working Directory):在 git init 之後的本地的文件目錄下,也就是大家寫代碼的地方
-
暫存區(Staged/Index):修改了代碼之後,需要先將改動 add 到暫存區,表示將要提交的改動
-
本地倉庫(Local Repository):本地 Git 倉庫,通俗講就是本地隱藏文件. git 目錄下,存儲着你的所有改動
-
遠程倉庫(Remote Repository):遠程 Git 倉庫,理論上和本地倉庫地位平等,但是主要是用於多個開發者之間 pull/push 代碼的倉庫。
四個工作區域
Git 文件狀態
接下來我們來看下 Git 文件的狀態:
Git 文件狀態
-
UnTracked: 未跟蹤,此文件在文件夾中,但並沒有加入到 git 庫,不參與版本控制。通過 git add 狀態變爲 Staged。
-
UnModify: 文件已經入庫,未修改, 即版本庫中的文件快照內容與文件夾中完全一致。這種類型的文件有兩種去處,如果它被修改,而變爲 Modified。如果使用 git rm 移出版本庫,則成爲 UnTracked 文件。
-
Modified: 文件已修改,僅僅是修改,並沒有進行其他的操作。這個文件也有兩個去處,通過 git add 可進入暫存 staged 狀態,使用 git checkout 則丟棄修改過,返回到 unmodify 狀態,這個 git checkout 即從庫中取出文件,覆蓋當前修改。
-
Staged: 暫存狀態,執行 git commit 則將修改同步到庫中,這時庫中的文件和本地文件又變爲一致,文件爲 UnModify 狀態。執行 git reset 取消暫存,文件狀態爲 Modified。
Git 基本命令
在瞭解 Git 工作區域、文件狀態以及本地倉庫的相關信息之後,相信大家對於日常使用的一些命令都有了更加深刻的理解。接下來,我們一起進行一個常用命令總結:
-
git clone:將遠程倉庫克隆到本地,也就是創建了一個本地倉庫,會出現隱藏文件. git
-
git status:查看狀態,可以看到哪些文件被修改、哪些是未跟蹤的文件
-
git diff:可以看到當前工作區和暫存區 staged 中的文件 diff
-
git add:將未跟蹤(新增加)或者修改過的文件從工作區添加到暫存區 staged 中
-
git commit:將暫存區 staged 中的內容提交到本地 Git 倉庫中
-
git push:將本地 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
- git reset commitId
該命令執行之後,HEAD指針會移動到選中commitId上,並且之前的HEAD ->commitId之間的所有修改的內容會被置於工作區,需要重新add、commit。
- git reset --soft commitId
該命令執行之後,HEAD指針會移動到選中commitId上,並且之前的HEAD ->commitId之間的所有修改的內容會被直接置於暫存區staged中,也就是後續只需要執行commit操作就OK了
- git reset --hard commitId
這個--hard很好理解,就是在回滾HEAD指針的時候,很強硬的將所有HEAD ->commitId之間的改動內容“全部刪除”!
爲什麼加引號?因爲前邊我們說了Git不會丟失任何你提交過的內容(只要你玩的溜),後續我們會分析原因。
git reflog
- 新建一個目錄 git-test,初始化一下倉庫,做一次簡單的提交
mkdir git-test
cd git-test/
git init
echo 0 > test
git add .
git commit -am "init"
- 假設這幾個星期你做了三次重要代碼的提交:
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
- 就在這個時候,假設你頭腦突然短路,用了 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
有沒有什麼辦法補救呢?答案是肯定的!
- 用 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
- 我們再來看一下代碼的情況:
linux-geek:/home/tmp/git-test # cat test
0
important code0
important code1
important code2
- 此時用 git log 或 git log --pretty=oneline 可以看到全部的歷史記錄又恢復回來了。
git revert
revert 是回滾某個 commit ,不是回滾到某個。git revert 是用於反做某一個版本,以達到撤銷該版本的修改的目的。比如,我們 commit 了三個版本(版本一、版本二、 版本三),突然發現版本二不行(如:有 bug),想要撤銷版本二,但又不想影響撤銷版本三的提交,就可以用 git revert 命令來反做版本二,生成新的版本四,這個版本四里會保留版本三的東西,但撤銷了版本二的東西。如下圖所示:
在這裏插入圖片描述
- 反做,使用 “git revert -n 版本號” 命令。如下命令,我們反做版本號爲 8b89621 的版本:
git revert -n 8b89621019c9adc6fc4d242cd41daeb13aeb9861
- 這裏可能會出現衝突,那麼需要手動修改衝突的文件。而且要 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 操作,直接一個命令就可回退:
$ 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爲文件名
參考
-
Git 十大硬圖:https://zhuanlan.zhihu.com/p/132573100
-
Git 分支規範:https://zhuanlan.zhihu.com/p/108385922
-
Git 思維導圖:https://www.jianshu.com/p/e2f553942317
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/F1lIs1whEF0hEA5SVWqsXQ