資源補充:連猴子都能懂的Git入門指南
什麼是版本控制?
做版本控制的“目的”就是:
一個檔案會有不同版本,但我希望把每個版本都保存起來
Git 就是一個:幫我做版本控制的程式
版本控制要有哪些功能
以下是「自己用資料夾做版本控制」的方式,來推導出一個版本控制應該要有哪些功能(模擬 Git 的運作機制)
- 需要新版本:就開一個新的資料夾(新增一個 commit)
- 不想加入版本控制的檔案:就不要加入資料夾(例如:電腦本身的設定檔、放有帳號密碼的檔案等等,都不需要加入版本控制)
- 避免版本號衝突:用看似亂數的東西當做資料夾名稱(版本編號),每個亂數保證不會相等
- 知道最新版本:用一個檔案來存最新的版本號
- 知道歷史記錄:用一個檔案來存「每個版本的順序」
Git 就是要幫我做到上面這些事情
Git 的實際運作機制
課程中把 Git 的不同 branch 比喻為「不同的資料夾」,只是為了讓初學者比較方便去理解版本控制的概念。
實際上 Git 在運行時,不會真的每開一個 branch 就開一個資料夾,也不會每一個 commit 就複製一次檔案。如果真的是這樣,那假設你有一個 10MB 的檔案,做完 100 個 commit 複製完 100 次之後不就超級大了嗎?
因此,聰明的 Git 真正儲存的是「檔案的差異」,例如說你在檔案第二行插入了一行文字:「hello」,Git 就只會記住「檔案第二行插入 hello」這件事情,並不會複製整個檔案。
那 Git 要怎麼知道某個時間點的檔案長什麼樣子?就把原始檔案再重新套用一次每個 commit 的差異,不就是那個時間點的樣子嗎?(不過 Git 在這件事情上應該會做一些優化就是了)
所以大家一定要清楚地知道,Git 真正儲存的是「檔案的差異」。然後第一次 git init
的時候的確是會把整份檔案都記起來,才知道最一開始長什麼樣子。對於每一個檔案,Git 也都有自己的壓縮演算法去壓縮,所以比你的檔案還小是正常的。
git init
產生 .git 資料夾
該如何對我的專案(test 資料夾),開始使用 Git 這個軟體呢?
作法就是:
- 打開 CLI,移動到 test 資料夾
- 輸入指令
git init
init
就是 initialize(初始化)的意思
在 test 資料夾使用 git init
指令後,Git 就知道我想要對這個專案做版本控制了,這時,我的專案資料夾就是一個 Git 的 Local repository
接下來,我就可以使用更多指令來做版本控制
這時,在 test 資料夾裡面,會多出一個 .git 資料夾(是一個隱藏的資料夾),Git 做版本控制需要用到的所有東西都會存在這個資料夾裡面
移除 .git 資料夾
如果我不再需要對此專案做版本控制了,只需要輸入指令 rm -rf .git
來移除掉 .git 資料夾即可
git status
查看狀態
查看目前的狀態
git add
例如:
在我的專案資料夾內,現在有三個檔案:
code.js
note.txt
password
針對專案內的每個檔案,我需要決定「這個檔案是否要加入版本控制」
git add 檔案名稱
加入版本控制
輸入指令 git add code.js
,就可以把 code.js 加入版本控制
Git 會把檔案分成兩個區域:
- untracked 不加入版本控制
- staged 加入版本控制
git add .
把該資料夾底下的所有檔案加入 staged 區域
.
就是「所有檔案」的意思
在 git add
後面加上 .
或是 資料夾名稱
,就可以把該資料夾底下的所有檔案加入版本控制
注意!
- 如果是在主目錄使用
git add .
,那就會把「Git repository」裡面的所有檔案加入 staged 區域 - 但如果是在子資料夾使用
git add .
,就只會把「所處資料夾」底下的所有檔案加到 staged 區域
git add --all
把 Git 範圍內的所有檔案加入 staged 區域
如果是想要把「Git repository」裡面的所有檔案都加到 staged 區域,可以直接使用帶參數的 git add --all
git rm --cached code.js
移出版本控制
把檔案從 staged 移回 untracked
git commit
新建一個版本
當 commit message 較多時
使用指令 git commit
就會進入一個 vim 編輯器,在這裡可以輸入 commit message
當 commit message 只有一句話時
如果 commit message 很短,那就可以直接使用指令 git commit -m 'first commit'
這個參數 m
就是「message」的意思,後面用 引號
包起來的地方就可以輸入你想要打的 commit message
git commit
出現錯誤
如果你在 git commit
的時候出現錯誤,跳出了一個要你設定帳號跟姓名的畫面,請輸入以下指令
(把名字跟 email 換成你自己的)
git config --global user.name "your name"
git config --global user.email "your email"
如果你的作業系統是 Windows,請注意後面的字串一定要用雙引號,用單引號的話會出錯:
git commit -am 'error' // 錯誤
git commit -am "success" // 正確
git commit -am 'commit message'
輸入指令 git commit -am 'forth commit'
,這裡加上了一個參數 -am
,
意思就是:把 git add .
和 git commit -m 'forth commit'
合併在一起
-a
這個參數就是 --all
的意思(通常全名會使用 --
兩個 dash 來表示),作用是:自動把修改過的檔案再次加入到 staged 區域(必須是之前已經有被 staged 過的檔案才可以),但如果是「還在 untracked 區域的檔案」就不會去理它(不會把它移到 staged 區域,也不會 commit 它)
git log
查看歷史記錄
在 git log
裡面會記錄:
- 每一個 commit 記錄的版本號(獨一無二的 ID):就是那串看起來很像亂碼的
- commit 的作者
- commit 的時間
- commit message
git log --oneline
使用參數 git log --oneline
就只會顯示比較簡短的歷史記錄
在這裡的版本號只有顯示「前 7 碼」,原因是:
「前 7 碼」也保證不會重複,因此可以只用前 7 碼來表示各版本
按下 q
就可以離開 git log
畫面
git checkout
回到某個版本
例如:
我想要切換回第一次 commit 的版本,作法如下
- 先使用
git log
去看歷史記錄,把第一次 commit 的版本號複製起來 - 輸入指令
git checkout 1bcbe402a46c5f917e4578bab7c481e9c17fe2f6
,就可以切換到第一次 commit 的版本
這時,會發生兩件事情:
- 所有的檔案都會回到第一次 commit 時的版本
- 使用
git log
去看歷史記錄,就只會剩下第一次的 commit 記錄而已了(因為現在已經回到“第一次 commit”當下的時間點)
git checkout master
回到最新版本
這時,會發生兩件事情:
- 所有的檔案都會回到最新的版本
- 使用
git log
去看歷史記錄,就會看到所有的 commit 記錄
.gitignore
要忽略的檔案
只要是被放在 .gitignore 裡面的檔案都會被 Git 忽略(不會被 commit),下面是一些會被放在 .gitignore 的檔案:
- 開發者個人相關的檔案
- 跟專案無關的檔案
- 作業系統產生的檔案(Mac 系統產生的
.DS_STORE
) - 暫存檔(
.swp
) - node_modules
例如:
在我的專案中,我想要忽略掉「password 這個檔案」(不要被 commit)
- 用
touch .gitignore
來建立 .gitignore 檔案 - 用
vim .gitignore
進入編輯器,打上我要忽略的檔案名稱
- 這樣一來,在 commit 的時候,就會忽略掉「password 這個檔案」了
注意!.gitignore 裡面的檔案不會被 commit,但是「.gitignore 這個檔案本身」必須被加入版本控制中,這樣其他的開發者才知道有哪些檔案是被忽略的
git diff
在 commit 之前,使用指令 git diff
可以看到「跟上一次 commit 的版本相比,我在每一個檔案“刪除、新增”了哪些東西」
個人開發的專案流程
以個人開發來說,以下是在一個專案開始使用 Git 的流程:
第零步:讓 Git 開始對專案資料夾做版本控制
git init
第一步:建立 .gitignore 來忽略不要的檔案
第二步:git add .
先把所有檔案都放到 staged 區域
第三步:git commit -am 'first commit'
建立第一個 commit (第一個版本)
專案建立後
情境一:有新的檔案
- 先使用
git add .
把全部檔案放到 staged 區域 - 再用
git commit -am 'second commit'
新增第二個 commit
情境二:只是修改原有的檔案
- 檔案都修改完之後,就可以直接使用
git commit -am 'second commit'
來新增第二個 commit
一條線的開發模式會遇到的問題
上面這樣的專案流程,都是在同一條分支上做開發,有可能會遇到的問題是:我在這條分支上正在開發一個新功能,新功能只做到 30% 時,這時突然出現一個緊急的 bug 需要修,修完 bug 之後發佈的新版本就會帶著那個「只完成了 30% 的新功能」到 user 的手上
為了解決這個問題,Git 提供了 branch 這個功能。我們可以在一個專案中建立很多條不同的 branch,分別負責「開發新功能」、「bug fix」等等
接著會詳細說明關於 branch 的各種指令和使用方式
Git 的平行時空:branch
在專案使用git init
時,就會產生 master 這條分支
注意!
master 就只是「預設的 branch」,或者通常是最主要的 branch,但這並不代表它是「最新的 branch」。
舉例來說,你也可以切出一條 branch 叫做「test」,然後一直往 test 加東西,但是從來都不合併回 master,那 test 就是在你專案裡的最新的 branch。
所以 master 就只是一條預設的 branch 而已,最新的東西不一定在上面。
git branch -v
查看目前分支
這裡的參數 -v
是 verbose 的意思(冗長的)
輸入指令 git branch -v
可以看到:
- 我現在有一條 master 分支
- 在此分支上,最後一次 commit 的版本號(
0128977
) - 在此分支上,最後一次的 commit message(
first commit
)
git branch 分支名稱
建立新的分支
例如:
輸入指令 git branch new-feature
,就可以建立一條新的分支叫做 new-feature
git branch -d 分支名稱
刪除分支
例如:
輸入指令 git branch -d new-feature
,就可以刪除掉 new-feature
這條分支
git checkout 分支名稱
切換到其他分支
例如:
輸入指令 git checkout new-feature
,就可以切換到 new-feature
這條分支
再用 git branch -v
查看狀態,就可以看到我目前的確是在 new-feature
分支上(前面有打上 *
號)
git merge 分支名稱
把分支合併進來
例如:
我在 master 輸入指令 git merge new-feature
,就可以把 new-feature
分支合併進來--> 會改變的是 master 分支
這時,用 git log
查看 commit 狀態,就可以看到:
在 master 分支上,就擁有了 new-feature
分支上最新的 commit 記錄
當 merge 遇到衝突時
當我在合併兩個 branch,輸入指令 git merge new-feature
後,出現這樣的訊息,代表 merge 遇到衝突了:
Auto-merging code.js
意思是「Git 本來要自動幫我合併 code.js」,但是在 code.js 發生了 conflict,因此 Automatic merge failed
(自動化 merge 失敗了),需要先 fix conflicts
然後才能 commit the result
也可以用 git status
看到 Unmerged paths (沒有被 merge 的):
both modified: code.js
意思是「code.js 這個檔案發生衝突了(被不同的人改到同一行程式碼)」
這時,就需要自己手動去 code.js 決定要保留的內容是哪一個,決定完存檔之後,就可以 commit 了 (git commit -am 'resolve conflicts'
)