需求一:如何把stage中的修改還原到work dir中
這個需求很常見,也很重要,比如我先將當前work dir
中的修改新增到stage
中,然後又對work dir
中的檔案進行了修改,但是又後悔了,如何把work dir
中的全部或部分檔案還原成stage
中的樣子呢?
來個實際場景,我先新建兩個檔案,然後把他們都加到stage
:
$ touch a.txt b.txt
$ git add .
$ git status
On branch master
Changes to be committed:
new file: a.txt
new file: b.txt
然後我又修改了a.txt
檔案:
$ echo hello world >> a.txt
$ git status
On branch master
Changes to be committed:
new file: a.txt
new file: b.txt
Changes not staged for commit:
modified: a.txt
現在,我後悔了,我認為不應該修改a.txt
,我想把它還原成stage
中的空檔案,怎麼辦?
答案是,使用 checkout 命令:
$ git checkout a.txt
Updated 1 path from the index
$ git status
On branch master
Changes to be committed:
new file: a.txt
new file: b.txt
看到了麼,輸出顯示從index
區(也就是stage
區)更新了一個檔案,也就是把work dir
中a.txt
檔案還原成了stage
中的狀態(一個空檔案)。
當然,如果work dir
中被修改的檔案很多,可以使用萬用字元全部恢復成stage
:
$ git checkout .
有一點需要指出的是,checkout
命令只會把被「修改」的檔案恢復成stage
的狀態,如果work dir
中新增了新檔案,你使用git checkout .
是不會刪除新檔案的。
需求二:比如說commit
完之後,突然發現一些錯別字需要修改,又不想為改幾個錯別字而新開一個commit
到history
區
那麼就可以使用下面這個命令:
$ git commit --amend
這樣就是把錯別字的修改和之前的那個commit
中的修改合併,作為一個commit
提交到history
區。
需求三:將history區的檔案還原到stage區
這個需求很常見,比如說我用了一個git add .
一股腦把所有修改加入stage
,但是突然想起來檔案a.txt
中的程式碼我還沒寫完,不應該把它commit
到history
區,所以我得把它從stage
中撤銷,等後面我寫完了再提交。
$ echo aaa >> a.txt; echo bbb >> b.txt;
$ git add .
$ git status
On branch master
Changes to be committed:
modified: a.txt
modified: b.txt
如何把a.txt
從stage
區還原出來呢?可以使用 git reset 命令:
$ git reset a.txt
$ git status
On branch master
Changes to be committed:
modified: b.txt
Changes not staged for commit:
modified: a.txt
你看,這樣就可以把a.txt
檔案從stage
區移出,這時候進行git commit
相關的操作就不會把這個檔案一起提交到history
區了。
上面的這個命令是一個簡寫,實際上reset
命令的完整寫法如下:
$ git reset --mixed HEAD a.txt
其中,mixed
是一個模式(mode)引數,如果reset
省略這個選項的話預設是mixed
模式;HEAD
指定了一個歷史提交的 hash 值;a.txt
指定了一個或者多個檔案。
該命令的自然語言描述是:不改變work dir中的任何資料,將stage區域中的a.txt檔案還原成HEAD指向的commit history中的樣子。就相當於把對a.txt
的修改從stage
區撤銷,但依然儲存在work dir
中,變為unstage
的狀態。
需求四:將work dir中的修改提交到history區
這個需求很簡單,先git add
然後git commit
就行了,或者一個快捷方法是使用命令git commit -a
。
需求五:將history區的歷史提交還原到work dir中
這個場景,我說一個極端一點的例子:比如我從 GitHub 上clone
了一個專案,然後亂改了一通程式碼,結果發現我寫的程式碼根本跑不通,於是後悔了,乾脆不改了,我想恢復成最初的模樣,怎麼辦?
依然是使用checkout
命令,但是和之前的使用方式有一些不同:
$ git checkout HEAD .
Updated 12 paths from d480c4f
這樣,work dir
和stage
中所有的「修改」都會被撤銷,恢復成HEAD
指向的那個history commit
。
注意,類似之前通過stage
恢復work dir
的checkout
命令,這裡撤銷的也只是修改,新增的檔案不會被撤銷。
當然,只要找到任意一個commit
的 HASH 值,checkout
命令可就以將檔案恢復成任一個history commit
中的樣子:
$ git checkout 2bdf04a some_test.go
Updated 1 path from 2bdf04a
# 前文的用法顯示 update from index
比如,我改了某個測試檔案,結果發現測試跑不過了,所以就把該檔案恢復到了它能跑過的那個歷史版本……
需求六:由於HEAD指標的回退,導致有的commit在git log命令中無法看到,怎麼得到它們的 Hash 值呢?
只要你不亂動本地的.git
資料夾,任何修改只要提交到commit history
中,都永遠不會丟失,看不到某些commit
只是因為它們不是我們當前HEAD
位置的「歷史」提交,我們可以使用如下命令檢視操作記錄:
$ git reflog
比如reset
,checkout
等等關鍵操作都會在這裡留下記錄,所有commit
的 Hash 值都能在這裡找到,所以如果你發現有哪個commit
突然找不到了,一定都可以在這裡找到。