雖然git
誕生距今已有12
年之久,網上各種關於git
的介紹文章數不勝數,但是依然有很多人(包括我自己在內)對於它的功能不能完全掌握。以下的介紹只是基於我個人對於git
的理解,並且可能生編硬造了一些不完全符合git
說法的詞語。目的只是爲了讓git
通俗化,使初學者也能大概瞭解如何快速上手git
。同時,下面所有討論,我們都假設只使用一個分支,也就是主分支master
的情況,雖然這種作法並不符合git
規範,但是現實情況中絕大部分用戶是直接在master
分支上進行工作的,所以在這裏我們不去引入更加複雜的各種分支的情況,也不涉及標籤tag
的操作,只講在最簡單的主分支上如何回退。
基本概念
3個步驟
正常情況下,我們的工作流就是3
個步驟,對應上圖中的3
個箭頭線:
1
2
3
|
git
add .
git commit
-m
"comment"
git
push
|
git add .
把所有文件放入暫存區
;git commit
把所有文件從暫存區
提交進本地倉庫
;git push
把所有文件從本地倉庫
推送進遠程倉庫
。
4個區
git
之所以令人費解,主要是它相比於svn
等等傳統的版本管理工具,多引入了一個暫存區(Stage
)的概念,就因爲多了這一個概念,而使很多人疑惑。其實,在初學者來說,每個區具體怎麼工作的,我們完全不需要關心,而只要知道有這麼4
個區就夠了:
- 工作區(
Working Area
) - 暫存區(
Stage
) - 本地倉庫(
Local Repository
) - 遠程倉庫(
Remote Repository
)
5種狀態
以上4
個區,進入每一個區成功之後會產生一個狀態,再加上最初始的一個狀態,一共是5
種狀態。以下我們把這5
種狀態分別命名爲:
- 未修改(
Origin
) - 已修改(
Modified
) - 已暫存(
Staged
) - 已提交(
Committed
) - 已推送(
Pushed
)
檢查修改
瞭解了基本概念之後,我們來談一談犯錯誤之後如何撤銷的問題。首先,我們要了解如何檢查這3
個步驟當中每一個步驟修改了什麼,然後纔好判斷有沒有修改成功。檢查修改的二級命令都相同,都是diff
,只是參數有所不同。
已修改,未暫存
1
|
git
diff
|
首先,我們來看一下,如果我們只是簡單地在瀏覽器裏保存了一下文件,但是還沒有做git add .
之前,我們如何檢查有哪些修改。我們先隨便拿一個文件來做一下實驗:
我們在文件開頭的第2
行胡亂加了4
個數字1234
,存盤,這時文件進入了已修改
狀態,但是還沒有進入暫存區
,我們運行git
diff
,結果如下:
1
2
3
4
5
6
7
8
9
10
|
diff
--git
a/index.md
b/index.md
index 73ff1ba..1066758
100644
---
a/index.md
+++
b/index.md
<a
href='http://www.jobbole.com/members/li754132448'>@@</a>
-1,5
+1,5
<a
href='http://www.jobbole.com/members/li754132448'>@@</a>
---
-layout:
main
+1234layout:
main
color:
black
---
|
git diff
的結果告訴我們哪些文件已經做了哪些修改。
已暫存,未提交
1
|
git
diff --cached
|
現在我們把修改放入暫存區
看一下。先執行git add .
,然後執行git diff
,你會發現沒有任何結果:
這說明git diff
這個命令只檢查我們的工作區
和暫存區
之間的差異,如果我們想看到暫存區
和本地倉庫
之間的差異,就需要加一個參數git
diff --cached
:
1
2
3
4
5
6
7
8
9
10
|
diff
--git
a/index.md
b/index.md
index 73ff1ba..1066758
100644
---
a/index.md
+++
b/index.md
<a
href='http://www.jobbole.com/members/li754132448'>@@</a>
-1,5
+1,5
<a
href='http://www.jobbole.com/members/li754132448'>@@</a>
---
-layout:
main
+1234layout:
main
color:
black
---
|
這時候我們看到的差異是暫存區
和本地倉庫
之間的差異。
已提交,未推送
1
|
git
diff master origin/master
|
現在,我們把修改從暫存區
提交到本地倉庫
,再看一下差異。先執行git commit
,然後再執行git diff --cached
,沒有差異,執行git
diff master origin/master
,可以看到差異:
在這裏,master
就是你的本地倉庫
,而origin/master
就是你的遠程倉庫
,master
是主分支的意思,因爲我們都在主分支上工作,所以這裏兩邊都是master
,而origin
就代表遠程。
撤銷修改
瞭解清楚如何檢查各種修改之後,我們開始嘗試各種撤銷操作。
已修改,未暫存
如果我們只是在編輯器裏修改了文件,但還沒有執行git add .
,這時候我們的文件還在工作區
,並沒有進入暫存區
,我們可以用:
1
|
git
checkout .
|
或者
1
|
git
reset --hard
|
來進行撤銷操作。
可以看到,在執行完git checkout .
之後,修改已被撤銷,git diff
沒有任何內容了。
一對反義詞
git add .
的反義詞是git checkout .
。做完修改之後,如果你想向前走一步,讓修改進入暫存區
,就執行git add .
,如果你想向後退一步,撤銷剛纔的修改,就執行git checkout .
。
已暫存,未提交
你已經執行了git add .
,但還沒有執行git commit -m "comment"
。這時候你意識到了錯誤,想要撤銷,你可以執行:
1
2
|
git
reset
git checkout
.
|
或者
1
|
git
reset --hard
|
git reset
只是把修改退回到了git add .
之前的狀態,也就是說文件本身還處於已修改未暫存
狀態,你如果想退回未修改
狀態,還需要執行git
checkout .
。
或許你已經注意到了,以上兩個步驟都可以用同一個命令git reset --hard
來完成。是的,就是這個強大的命令,可以一步到位地把你的修改完全恢復到未修改
的狀態。
已提交,未推送
你的手太快,你既執行了git add .
,又執行了git commit
,這時候你的代碼已經進入了你的本地倉庫
,然而你後悔了,怎麼辦?不要着急,還有辦法。
1
|
git
reset --hard
origin/master
|
還是這個git reset --hard
命令,只不過這次多了一個參數origin/master
,正如我們上面講過的,origin/master
代表遠程倉庫
,既然你已經污染了你的本地倉庫
,那麼就從遠程倉庫
把代碼取回來吧。
已推送
很不幸,你的手實在是太快了,你既git add
了,又git commit
了,並且還git push
了,這時你的代碼已經進入遠程倉庫
。如果你想恢復的話,還好,由於你的本地倉庫
和遠程倉庫
是等價的,你只需要先恢復本地倉庫
,再強制push
到遠程倉庫
就好了:
1
2
|
git
reset --hard
HEAD^
git push
-f
|
總結
以上4
種狀態的撤銷我們都用到了同一個命令git reset --hard
,前2
種狀態的用法甚至完全一樣,所以只要掌握了git reset --hard
這個命令的用法,從此你再也不用擔心提交錯誤了。
更多參考
- 獨孤求敗:Git中的各種後悔藥