配置本地git環境時候,首先在 git config 中需要聲明 user.name 和 user.email,因為在以后每次commit時候都需要該信息來記錄
Git 結構和運行原理
首先我們需要了解一下git的運行原理,本地git的結構如下所示:
其中:
1、Directory:使用Git管理的一個目錄,也就是一個倉庫,包含我們的工作空間和Git的管理空間
2、WorkSpace:需要通過Git進行版本控制的目錄和文件,這些目錄和文件組成了工作空間
3、.git:存放Git管理信息的目錄,初始化倉庫的時候自動創建
4、Index/Stage:暫存區,或者叫待提交更新區,在提交進入repo之前,我們可以把所有的更新放在暫存區
5、Local Repo:本地倉庫,一個存放在本地的版本庫;HEAD會只是當前的開發分支(branch)
6、Stash:是一個工作狀態保存棧,用于保存/恢復WorkSpace中的臨時狀態
了解了以上的基本結構,我們現在可以嘗試創建本地倉庫并通過實驗來了解git操作了。
一、Create --- 創建倉庫
先cd到一個我們想要存放代碼的位置,在該路徑下使用命令 git init 初始化git倉庫,輕松創建成功。并且可以看到Git管理信息目錄.git文件,如下所示:
二、Add --- 添加操作
現在添加一個 mdr_test 文件,文件內容如下:
可以看到這就是一個簡單的斐波那契數列(Ai = Ai-1 + Ai-2),另外還有數據存儲日期
我剛剛只是完成了文件的編輯,通過使用命令 git status 可以查詢WorkSpace的狀態,從中得知 mdr_test 文件并沒有被跟蹤——track,并且提示可以使用 git add 來進行跟蹤。然后使用 git add mdr_test 命令將文件添加到待提交區(暫存區)——Index / Stage,這時再通過 git status 查看狀態,可以發現已經出現了 "new file : mdr_test" 的提示。如下所示:
最后,使用命令 git commit -m "XXX" 來提交更新就可以了。其中 -m 后面的 “XXX” 為對于本次更新的描述(message的縮寫),例如 "add function" / "bug fix" 之類的即可。需要提到的是,-m 的更新描述是必須寫的,否則無法提交更新。通過 commit 操作,所有的更新已經從 Stage 同步到本地倉庫 —— Local Repo 中了。此時再次查看 git status,返回的信息是 nothing to commit,說明已無其他待提交。如下所示:
三、Update --- 更新操作
假設我們現在需要更新剛剛提交的 mdr_test 文件中的data數據,將斐波那契數列換成大衍數列(N為奇數:An = (n * n -1) / 2,N為偶數:n * n / 2)
而這時,只是vim完成了數據修改,通過 git status 可以看到本次修改并沒有進入暫存區,只是發現了modified。如下所示:
同樣道理,我們使用 git add 和 git commit 操作來完成對本次數據更新的提交,同步到 Local Repo 中。如下所示:
需要注意的是,無論是添加文件還是修改數據,通過 commit 更新到 Repo 的都只是緩存區的變動。也就是說,無論在 WorkSpace 做了何種修改,只有沒有經過 git add 操作提交到緩存區,就無法在 git commit 的時候更新到倉庫中。
那么如果只有緩存區的更新能夠被同步到倉庫,如何確認 WorkSpace 中的文件與 Stage 中的文件相同呢?這可以使用 diff 命令。比如我們再次將 mdr_test 中的 data 更新成等比數列(An = 2^n),如下所示:
此時,緩存區中 mdr_test 的 data 依然是大衍數列,而工作區中的data已經更新成等比數列,使用 diff 可以看到兩者的不同。當我們將本次數據更新通過 git add 提交后,再使用 git diff 就找不到任何不同了(也就是沒有返回),并且能夠在 git status 中看到本次 mdr_test 文件的 modified,如下所示:
當然,也可以直接diff WorkSpace 與 Repo 中的不同,命令為 git diff HEAD。因為剛才我已經將大衍數列變成等比數列更新到緩存區,所以只需要 commit 一下就可以讓 WorkSpace 和 Repo 一致了。操作流程如下所示:
四、Revoked --- 撤銷操作
撤銷操作會出現在三個地方,即 WorkSpace 階段、Stage 階段、Repo 階段,下面分別來說
1、撤銷 WorkSpace 階段更新
我們將 mdr_test 文件中的 data 先從等比數列改回斐波那契數列,然后通過 cat 和 git status 可以看到這時候文件是在 WorkSpace 中發生的數據更新。這時候,當然我們可以手動的修改文件數據恢復到等比數列,但為了避免哪里忘記恢復,保證數據一致性,往往使用 checkout 來檢出文件的歷史版本,即使用命令:git checkout mdr_test 來回滾到 mdr_test 在 Repo 中最新的一個版本,如下所示:
需要注意的是,checkout 是歷史版本檢出,將直接覆蓋掉本地文件,當然也包括在最新版本之后所有的修改和更新內容,所以在檢出前一定要確認可以檢出,并適當做好本地文件備份
2、撤銷 Stage 階段更新
同樣我還是先把 mdr_test 的 data 中等比數列改成斐波那契數列,然后這次將更新 add 到緩存區。用 cat 和 status 可以看到 data 更新已經提交到 Stage。此時,我們使用命令:git reset HEAD mdr_test 用來恢復 Stage 中的數據。這行命令的意思其實就是從 Repo 的最新版本 HEAD 中拿出文件 mdr_test 來 reset更新掉 Stage 中的 mdr_test。更新后,再次使用 git status 查看狀態會發現,Stage 中的更新已經取消,而 WorkSpace 中的更新依然存在。操作過程如下所示:
3、撤銷 Repo 階段更新
在撤銷 Repo 階段更新之前,我們需要先使用 git log 命令查詢 git commit 的歷史版本。可以看到我的 Local Repo 中有三個版本,分別是一個 add 和兩次 update,如下所示:
Repo 的撤銷有兩種方式:使用 HEAD 指針和使用 commit id
在 Git 中,HEAD 指針指向當前最新提交,當前版本我們定義為 HEAD^,也可以表示為 HEAD~1,再前一個版本定義為 HEAD^^,也可以表示為 HEAD~2,那么往前的第n個版本自然就是 HEAD~n。所以對于我們的情況來說,只有三個版本,我們使用命令:git reset --hard HEAD^^ 就可以回滾到第一個版本。
這里,順便對比一下是否存在 --hard 對 git reset 的影響
首先,我們使用命令:git reset HEAD^
可以看到經過 reset 后,git log 查詢到之前的 commit 也被抹掉了,只留下第一次提交數據的記錄,并且 Stage 中無其他更新,其中 WorkSpace 中的數據是斐波那契數列。此處,可能有同學存在一個疑問,reset 后更新覆蓋的僅僅是 Stage 中的數據還是 Stage 和 WorkSpace 中的數據呢?這個問題在這里看不清是因為我們的 reset 后的數據和第一次 add 后的數據一致,實踐出真知,所以我們再來做一個實驗。
首先我們先設計一下,我們對 mdr_test 做兩次修改,第一次日期更新:日期更新到2017-09-14,第二次數據更新:在斐波那契數列后延續加一項,并且每一次都通過 add 和 commit 提交到倉庫中,如下圖所示:
此時 Repo 中有三個版本,我們這回只 reset 到上一個版本,也就是只修改了日期,但沒有修改數據的版本,然后通過 cat 發現 WorkSpace 中還是日期和數據均修改后的版本,并且 status 中標紅的、提示沒有 add 的 modified 也說明相同的結論:那就是 reset 只會將 Stage 中的版本回滾到 Repo 指定的版本,而不會干擾到 WorkSpace 中的文件。如下所示:
那好了,再讓我們來看看 git reset --hard HEAD^ 是什么樣的效果
首先,我們還是提交了一次斐波那契數列的數據更新,將 Repo 恢復剛才的樣子,然后使用命令:git reset --hard HEAD^ 來恢復到前一個版本。結果是 git log 中也是將數據更新的 commit ?抹掉,但是不同的是連 WorkSpace 中的 mdr_test 也被改成了第二個版本,也就是只有日期更新,沒有數據更新的版本,而且可以通過 status 看到無待更新內容。這證明包含 --hard 參數的 reset 是徹底的,將同時對 WorkSpace 和 Stage 生效。如下所示:
下面我們介紹一下 git reflog 命令
git reflog 與 git log 的區別是:git log 只包含當前分支中的 commit 記錄,而 git reflog 會記錄這個倉庫中所有分支的所有更新記錄,包括已經撤銷的更新 commit 記錄。
我們除了通過 HEAD 指針的方式去恢復到以前的版本,另外一種重要的方式就是直接指定版本號,而 reflog 能夠給我們提供完整的版本信息。比如此時我們想要恢復到等比數列(b438a33)的版本,只需要使用命令:git reset --hard b438a33 就可以了,如下所示:?
這里使用的參數 --hard 還有個對應的參數 --soft,區別是:
hard:撤銷并刪除相應的更新
soft:撤銷相應的更新,并把這些更新的內容放到 Stage 中
五、Delete --- 刪除操作
1、rm:只在 WorkSpace 刪除相應文件,如果想提交更新,以后得自己 add 到 Stage
2、git rm:在刪除 WorkSpace 相應文件的同時,將這些刪除更新到 Stage 中
如下所示: