1. 程式人生 > >Git輕鬆入門1:本地倉庫篇

Git輕鬆入門1:本地倉庫篇

## 什麼是Git ### 版本控制系統 首先我們要明白,什麼是版本控制系統(version control systems)? 版本控制系統,顧名思義,就是能控制檔案處於哪個版本的系統。 e.g. 你在部落格園裡編輯的文章,你可以控制文章是處於某個時間點的版本。 而Git就是一款版本控制系統,而且是分散式的。 與分散式相對應的是集中式版本控制系統,它的版本庫是儲存在中央伺服器的。工作的時候你需要先從中央伺服器取得最新的版本,等工作完了,再把自己的工作推送回中央伺服器。但這有個問題:萬一哪天中央伺服器崩潰了甚至資料丟失了,那所有人不就都無法工作了? 於是分散式版本控制系統(Distributed Version Control System,簡稱 DVCS)就應運而生了。它沒有“中央伺服器”,每個人的電腦上都有一份完整的版本庫,如果某個人的電腦壞了導致版本庫丟失了,直接從別人那複製一個版本庫過來即可。 ### Git的基本工作原理 有一類版本控制系統是基於差異(delta-based)來進行版本控制的。
這類版本控制系統儲存的是新檔案和舊檔案間的區別(delta)。這裡的delta和高中物理裡的delta一樣,$Δv = v_2 - v_1$,都指的是變化量。 而Git和上述這類版本控制系統的工作原理不同:每當你對提交新專案或者對專案進行修改,它會把專案的所有檔案都壓縮儲存起來,這個壓縮檔案在Git中被稱為快照(snapshot)。每次有更新就會每次把所有檔案都壓縮起來儲存一遍,形成一系列快照。 當我們需要處於某個時間點版本的檔案時,直接根據索引,找到那個時間點的快照即可。 ## 操作指令 在開始介紹Git相關的指令前,我們先建立三個檔案。 ### 建立版本庫 所謂“版本庫(repository)”就是一個用來儲存檔案歷史版本的倉庫。所以要想對檔案進行版本控制,我們需要先初始化一個版本庫來儲存檔案的各個歷史版本。 `$ git init` 命令能把當前所在目錄變為由Git管理的倉庫(或是對該位置已有倉庫進行初始化),使得該目錄下的檔案可被Git進行版本控制。 當執行完該命令後,可以看到該目錄下生成了一個 `.git` 的資料夾。
### 把檔案新增到版本庫 建立完版本庫後,我們還需要把檔案放進去才能對其進行版本控制。 而把檔案新增到版本庫需要兩個步驟: **第一步**,用 `$ git add` 命令把需要跟蹤版本的檔案新增到暫存區。 **第二步**,用 `$ git commit` 命令告訴倉庫:我確定要對暫存區裡的檔案進行版本控制,請把暫存區裡的檔案正式放入倉庫管理。 `-m "xxx"` 是對本次提交的備註說明;`2 files changed` 指有兩個檔案變動:即我們新新增的 `README.md` 和 `demo1.txt`;`1 insertion` 指插入1行內容:即我們在 `README.md` 檔案中插入的“Git is a version control system.”。 在每次提交變動(commit)後,都會產生對應的快照。快照,我們在Git的工作原理那章提到過,它就是被放入版本庫中的所有檔案的壓縮版本。我們可以把快照理解成是遊戲的存檔。在玩遊戲時,我們每通過一關就會儲存遊戲。如果進行到某一關死了,那就可以通過讀取存檔來回到前面的某一關。Git也一樣,每次commit就是在給檔案存檔,要是哪一天檔案誤刪了或出錯了,那我們就可以讀取存檔,讓檔案恢復到存檔時的狀態。 通過 `$ git log` 命令,可以檢視當前共有幾個存檔。
### 同步修改到版本庫 當我們在工作區(工作區就是當你開啟“我的電腦”看到的目錄就叫工作區)裡對檔案進行修改後,如何把更新同步到版本庫中,讓版本庫也擁有檔案最新的版本呢? 我們先新增一行話到 `README.md` 檔案中,使其內容變為如下: ```markdown Git is a version control system. Git is distributed. ``` 現在,執行 `$ git status` 命令檢視版本庫當前狀態: 從狀態中我們可以得到2個資訊: 1. 對 `README.md` 檔案進行了修改,並且這次修改還沒有被新增到暫存區。 2. 在 `learn_git` 目錄下,`demo2.txt` 檔案沒被新增到倉庫中以進行版本追蹤。 如果我們想要進一步得知對 `README.md` 檔案進行了哪些修改,可以用 `$ git diff` 命令(這步並不是必須的)。 可以看到,新增加的改動是“Git is distributed”這行話。 確定了作出哪些修改後,再把它提交到倉庫自然就放心多了。**提交修改和之前提交新檔案需要同樣的兩個步驟:`$ git add` 和 `$ git commit`。** 我們先執行 `$ git add`,然後看看版本庫狀態。 可以看到,當我們用 `$ git add` 命令把檔案新增到暫存區後,版本庫狀態就不會再提示我們“尚未暫存已備提交的變更”,而是提示“要提交的變更”,即提示我們要用 `$ git commit` 命令提交這次變更。 那我們就再執行一下 `$ git commit` 命令,再來看看版本庫狀態。 再用`git log`檢視一下當前共有幾個存檔(快照)。 從以上兩張截圖可以看出現在已經沒有需要提交的修改了,只剩下未跟蹤的檔案 `demo2.txt` 了,剛才的那次變動已經被版本庫存檔了。 ### 把檔案從版本庫中刪除 刪除檔案也算是對檔案的一種修改。同樣也需要兩步: 1. 用 `$ git rm ` 把該檔案從工作區刪除。 2. commit這次變動,把刪除同步到版本庫。 注:在執行 `$ git rm ` 指令時,Git會預設執行 `$ git add` 命令來把這次修改提交到暫存區。 ### 回退到某個版本 我們已知目前共有2個存檔:第一個存檔的ID是`b1a6863...`,存檔備註是“added README and demo1”;第二個存檔的ID是`245fcc1...`,存檔備註是“modified README”。 在Git中,`HEAD` 是一個指標,它會指向當前所在分支的最新存檔,分支的概念我們後邊在介紹,但反正目前我們沒有建立別的分支。你讓 `HEAD` 指向哪個版本號,Git就把當前版本定位在哪。所以此時 `HEAD` 代表的就是ID為`245fcc...`的存檔,那麼在它前一個的存檔就是 `HEAD^`,前N個的存檔就是 `HEAD~N`。 有一天你可能會覺得第二個版本很糟糕,不如第一個版本,於是想坐時光機回到過去,讓一切復原到第一個版本時的樣子。這時我們可以用 `$ git reset` 命令來幫助我們穿越時空。 我們再來看一下 `README.md` 裡的內容,後面新新增的那行就不見了,內容回到了最初的時候: ```markdown Git is a version control system. ``` 再執行一下 `$ git log`。 一切真的都回到了過去!連存檔都變成和當時一樣,只有`b1a6863...`這一個了! 為什麼回退的速度這麼快呢? 因為我們每個版本的檔案都被儲存在了快照裡,Git僅僅只需要把 `HEAD` 指標從一個快照指向到另一個快照,再把目錄裡的檔案替換成那個快照裡的檔案即可。 所以建議:無論是大的變動還是小的變動,都存一下檔(即commit)。只有把這個版本存檔了,儲存進快照裡了,日後才能通過讀檔來返回到該版本。 可萬一你又後悔了,覺得一切還是像第二個版本時的那樣好,怎麼辦?通過 `$ git log` 命令已經找不到第二個版本的存檔了,是不是回不去了? 並不是的。在Git中,你的每次操作就會留下記錄,我們可以用 `$ git reflog` 來檢視所有的操作。 從中我們可以看到,備註為“modified README”的那次commit的ID是`245fcc1`。有了commit ID後,我們就又能通過 `$ git reset` 命令回到未來了! 再執行一下 `$ git log` 和開啟 `README.md` 檔案,可以確認真的回到了未來! ### 撤銷修改 撤銷修改有三種情況: 1. 修改還沒新增到暫存區。 2. 修改已經新增到暫存區,但還沒commit。 3. 修改已經commit。 #### 修改還沒新增到暫存區 我們先談第一種情況。 假設你往 `README.md` 檔案裡添加了一行“My boss is stupid. ”,而這最新修改還沒有被你新增到暫存區。所以當你執行`$ git status` 命令時,你看到Git提示你有尚未暫存已備提交的修改。 你覺得這句話可能會讓你被炒魷魚,儘管你說的是事實。於是你最終還是決定把這句話刪掉。 有兩種刪除方式: 1. 手動開啟 `README.md` 檔案,然後把這句話刪掉,讓檔案回到新增這行話前的樣子。 2. 按照Git提示的,用 `$ git restore ` 命令,來直接撤銷改檔案在工作區的變動。 你覺得第二種方式看起來顯得逼格更高點,於是你輸入如下命令。 ```zsh $ git restore README.md ``` 然後當你再開啟README.md檔案,發現那句話果然消失了;再執行 `$ git status` 命令,發現Git也沒有任何提示了。 #### 修改已經新增到暫存區但還沒commit 再來談談第二種情況:你不僅寫了那句話,還提交到了暫存區!所以當你執行 `$ git status` 命令時,你看到Git提示你有要提交的變更。 有了上次撤銷的經驗,你就放心地按照Git的提示輸入如下命令: ```zsh $ git restore --staged README.md ``` 再執行 `$ git status`,你發現現在它提示的和第一種情況一樣了,而開啟工作區裡 `README.md` 檔案,那句話還在。原來這個命令只是幫你撤銷一小步,僅僅是從暫存區移除,而不是幫你一夜回到解放前。 當然了,如果你很不幸的已經commit這次提交了,那就只能用 `$ git reset` 來版本回退了。 ## 總結 Git中檔案的所有狀態以及狀態間的轉換可以被概括為下面這張圖。 最後,總結一下今天出現過的幾個Git命令: ```zsh # 建立版本庫 $ git init # 把檔案新增到暫存區 $ git add # 把暫存區裡的變動提交到版本庫 $ git commit # 檢視當前所有快照 $ git log # 檢視版本庫當前狀態 $ git status # 檢視某檔案的變動 $ git diff # 刪除檔案,並把這次變動提交到暫存區 $ git rm # 版本間的前進和回退 $ git reset # 撤銷修改 $ git restore ``` ## 參考 1. https://sp18.datastructur.es/materials/guides/using-git.html 2. https://www.liaoxuefeng.com/wiki/896043488029600 3. http://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F 4. https://stackoverflow.com/questions/4964099/what-is-a-git-snapshot   有問題歡迎大家在評論區留言,轉載請註明