二、Git基礎

Git指令基礎介紹

1. 初始化一個Git本地倉儲

假設這是一個全新的開始,我們要在個人PC上面找個資料夾讓Git落地生根,還記得第一章我們將HOME設定到D:\git下面嗎 ? 建議在HOME下開一個以你們組織為名的資料夾,在此資料夾延伸各專案資料夾。

上面這個範例做了幾件事情:

  • 在HOME(~)目錄下建立了kneda/test目錄

  • 使用cd指令移至建立的目錄

  • git init 將這個目錄初始化

至此你會看到Git Bash上面顯示了master字樣,若仔細觀察,其實D:\git\kenda\test\也長出了.git目錄,未來在.git進行版控操作都會在這個目錄有所紀錄。

2. 將本地倉儲連向遠端倉儲

現假設在遠端(此指gitlab.kenda.com.tw) 並無建立倉儲,那我們必須在遠端建立一個倉儲,才能將本地指向遠端,進行版控管理。

2.1 建立並註冊SSH Key

在開始進行之前你必須先建立SSH Key讓並在個人帳號授權此SSH Key,才能讓gitlab信任。請跟著下列步驟操作:

SSH Key 建立後回到gitlab進行註冊,右上角個人圖示,點選setting,進入設定畫面,頁面左方會有SSH Keys,點選後進入註冊頁面。

在Key 這個欄位將剛剛複製的KEY貼上,並按下Add Key,即完成註冊。

若你有留意,應該會發現gitlab會發送郵件到你的個人信箱,通知你已註冊了信任的SSH key。

2.2 建立一個遠端倉儲

登入gitlab.kenda.com.tw後選擇右上角綠色按鈕[New project] > 選擇群組 (saxon) > 輸入 Project name (gittest) > 按下 [Create project] 即建立完成。

接著頁面會自動切換到專案的初始頁面,並告訴你這個專案的repository 是空白的,並給你一些操作建議,因為我們早已初始化本地倉儲,這邊就跟著Existing folder的步驟操作

上面這個範例做了幾件事情:

  1. 在本地倉儲建立遠端倉儲連結 (此處將遠端倉儲命名為org_test)

  2. 建立README.md 檔案

  3. 將本地倉儲加入暫存庫

  4. 提交當前暫存庫說明

  5. 將本地倉儲分支(master) 推向遠端倉儲分支(org_test/master),並追蹤遠端分支。

至此你已建立本地與遠端倉儲的連結,並將本地的README.md推至遠端倉儲,你可以在gitlab按下重新整理,README.md即顯示在該頁面上。

2.3 檢查上面操作的指令

  • git remote -v : 顯示所有的遠端倉儲與ssh網址

  • git status : 顯示當前狀態,此處可以看到本地master與org_test/master已連上

  • git log : 顯示操作紀錄,此處可以看到剛剛的commit 操作紀錄,本地master,遠端org_test/maste都在同一節點。

3. 紀錄變更到版本庫中

3.1 git status : 檢查狀態

在開始之前先介紹一下git status這個指令 這個指令的用途是用來查詢現在這個目錄的「狀態」,在~/kenda/test/下執行這個指令

在這個目錄裡,現在除了 Git 幫你產生的那個 .git 隱藏目錄跟已經push過的README.md外什麼都沒有,所以上面這段訊息就是要跟你說「現在沒東西可以提交(nothing to commit)」。接下來,在這個目錄裡透過touch指令建立一個a名為mouse的空白檔案。

再次使用git status 檢查當前狀態,可以看到目前有一個Untracked的檔案,這代表mouse這個檔案,git尚未追蹤,還不認識它,它只是剛剛加入這個目錄而已。

3.2 git add : 提交至暫存

既然這個檔案尚未追蹤,那我們要如何將它交給git,讓git追蹤它呢?我們接著下git add mouse,並再次下git status 檢查當前狀態,可以看到這個檔案(mouse)從 Untracked 變成 new file 狀態了。這個表示這個檔案已經被安置到暫存區(Staging Area),等待稍後跟其它檔案一起被存到儲存庫裡。

這裡的status可以看到(use "git reset HEAD <file>..." to unstage),這是git的貼心小提示,它告訴我們若要將檔案從暫存區移開,可以使用上述指令移開,這邊的例子是git reset HEAD mouse,即將mouse從stage改為unstage。

Q1 : 除了Untracked 以外,其他常見的檔案狀態? 如果在 git add 之後又修改了那個檔案的內容?

試想一下我們剛剛將mouse add到暫存區,又再次echo一些資料到mouse這個檔案裡,現在的狀態是什麼? 我們一樣透過git status一探究竟,可以看到除了new file外多出了modified,的狀態,代表檔案已經被修改過。 對 Git 來說,echo的內容並沒有再次被加到暫存區,所以這時候在暫存區裡的資料還是你尚未echo資料前加進來的那個檔案。若本次的修改也需要提交,請再次將檔案add。

Q2 : 我好懶,不想檔名一個一個add怎麼辦?

我們可以使用git add --all,將當前目錄所有的檔案放置到暫存區。

Q3 : 有些檔案我不想add,該怎麼辦?

我們可以設定 .gitignore 將不需要add的檔案類型列在此檔案中,git會依照此清單過濾掉不需要add的檔案。

一般 .gitignore放在工作區下,此例為D:\git\kenda\test\,若無此檔案請手動新增一個。

可以重新產生的檔案(如編譯產生的檔案),或沒必要放到正式區檔案(如log紀錄或暫存),都應該放置到.gitignore中,以避免汙染遠端倉儲。

3.3 git commit : 將暫存提交至本地倉儲

如果僅是透過 git add 指令把異動加到暫存區是不夠的,這樣還不算是完成整個流程。要讓暫存區的內容永久的存下來的話,使用的是 git commit 指令:

在這個例子我們使用git commit -m " add mouse" 將這次的修改提交到本地倉儲,-m 將修改說明附加上去,若沒有使用-m 則會開啟預設的修改器,請你輸入。(若按第一章的設定會打開VS Code)

Q1: 這個訊息重要嗎 ?

很重要!在 Commit 的時候,如果沒有輸入這個訊息,Git 預設是不會讓你完成 Commit 這件事的。它最主要的目的就是告訴你自己以及其它人「這次的修改做了什麼」,若將來push到遠端庫,將萬世流芳。

Q2:我什麼時候要commit ?

一般commit的時機是在,一個功能完成的時候,假設本次將為程式增加addfun()、modfun()、delfun(),三個功能,那麼建議在這三個功能分別完成時各commit一次,將來追蹤時也可以分別檢查三個功能個改了什麼。

3.4 git log : 檢視提交紀錄

為什麼說commit的說明將萬世流芳? 我們透過git log 可以列出每次修改的commit紀錄,這樣的資訊未來push到遠端倉儲,也會一併更新上去。

下圖可以看到兩筆commit紀錄,紅字(org_test/master)部分,是遠端倉儲master分支的位置,綠字部分master,是本地倉儲master分支的位置,從此處可看到,我們的修改已比遠端倉儲多了一個commit。藍字HEAD是一種指標,代表你在當前分支的位置,至於那串像亂碼的是每個commit的身分證字號。

Q1. 我想查某人改了提交了哪些修改紀錄怎麼做?

使用git log --author user_id 可以簡單地取得該user_id所有的修改紀錄。

Q2. 我想查特定commit的資訊怎麼做?

使用git log --grep 'keyword' 可以針對commit訊息進行搜尋。

grep 是區分大小寫的,你可以添加 -i 以忽略大小寫。

Q3. 我想查特定檔案的修改紀錄怎麼做?

使用git log -p filename 可以看到該檔案每個 commit分別修改了什麼。

3.5 git push : 更新至遠端倉儲

若我們已經確認本地修改無誤,commit也有正確說明了,就可以透過git push (這裡沒有指定遠端倉儲跟分支是因為第一章已透過push -u 的指令設定過追蹤了,若你沒有追蹤,則需使用git push org_test master)將本地的修改跟commit資訊更新至指定遠端倉儲與分支。

3.6 流程彙總 : 暫存、本地倉儲、遠端倉儲,好混亂?

講到這裡可能開始有點模糊,用下面這張圖簡單解釋一下。

  • workspace : 當前專案目錄,本例是指D:\git\kenda\test\

  • index : 即保存被add 並stage檔案的暫存庫,

  • local repository : 本地版控倉儲,即保存本地所有commit的倉儲。

  • remote repository : 遠端版控倉儲,即保存各開發者commit資訊的倉儲,本例是指gitlab上的org_test/master。

從圖中可以看到我們在專案目錄所有的新增/修改/刪除,都須透過git add指令才能提交到暫存區(藍色箭頭),並再透過commit提交到本地倉儲(綠色箭頭),至此我們所有的操作都只在個人電腦生效,並未對遠端倉儲進行任何變更,而再透過push指令,則會將本地倉儲的資訊更新至遠端倉儲(黃色向右箭頭)。

4. 復原 : 我需要時光機...

注意 : 這些指令並不是總會生效,甚至可能造成資料遺失,因此你提交commit之前都必需慎重考量,而不是依賴這些指令復原。

另外:已經push到遠端的commit,是永遠無法修改/刪除的。

4.1 git commit --amend

當你提交(commit)後發現寫錯了提交訊息,你可以在提交命令上使用 --amend 選項。這個命令會再次把暫存區(staging area)拿來提交,若你加上 -m 選項並輸入新的commit訊息,即更新了commit訊息。

如果你提交後才意識到你想要把某些忘記預存(stage)的修改也一併加入到上一個提交中,你可以這樣做:

4.2 git reset

假設你已經修改了二個檔案,並且想要分別add它們,但是你卻意外地使用 git add * 把它們二個都預存了, 要如何將其中一個「移出預存區(unstage)」呢? git status 命令提示你:

reset 還能怎麼用?假設想拆掉最後一次的commit,可以使用git reset HEAD^,將當前指向的commit往前推進,下圖可以看到效果,原本 " add mouse and forgot_file " 的commit消失了。

但真的是消失了嗎? 我們再透過git log --all,將所有的分支納進來看,可以看到遠端庫org_test/master,依然指向add mouse,所以…push至遠端的commit是無法修改的。

4.3 git checkout

把檔案改爛了怎麼辦? 假設有一檔案,我們修改了幾次,發現修改方向錯誤,想要還原到完全未修改的狀態,我們可以使用git checkout來還原。下圖例echo '123'到mouse中,並透過cat顯示echo後的結果,接著進一步使用git checkout後,cat 出來的資料已經沒有123了。

4. 使用分支

4.1 git branch

如果 git branch 後面沒接任何參數,它僅會印出目前在這個專案有哪些分支。Git 預設會幫你設定一個名為 master 的分支,前面的星號 *表示現在正在這個分支上。

4.1.1 新增分支

要增加一個分支,就是在執行 git branch 指令的時候,在後面加上想要的分支的名字:

4.1.2 切換分支

透過git checkout 可以切換到另一個分支:

4.1.3 刪除分支

透過git branch -d 可以刪除分支:

咦? 為什麼刪不掉? 原來git 有自我保護機制,不能刪除自身分支,必須先切換到另一個分支才能進行刪除,先切換到master再刪除就可以了!

4.1.4 分支名稱不喜歡怎麼辦 ?

透過-m 可以修改分支名稱:

4.2 開始玩分支

我們嘗試在tiger分支上建立一個檔案tiger並提交commit,

在使用git log --all看一下現在分支長什麼樣,從下圖可以看到tiger分支的位置在" add tiger",而master仍留在"add mouse",兩個分支的指標已經分離了。

我們再進一步看一下,分支檔案的變化,tiger分支下方可以看到檔案tiger的存在,透過checkout 切換到master,可以發現到檔案tiger不見了。這正是分支的作用,透過切換分支可以不影響到原本正常作業的master分支,因此做任何修改之前,都應該從master分支切出新的分支來。

4.3 合併分支

tiger分支的任務若已經結束了,那麼我們應該將tiger分支的修改結果合併到master來。首先我們要切換到master分支,再透過git merge將tiger併進master來。

看一下檔案列表,在master分支下也可以看到tiger了。

Last updated