Git 介紹 + 基本指令


Posted by saffran on 2020-12-01

資源補充:連猴子都能懂的Git入門指南

什麼是版本控制?

做版本控制的“目的”就是:
一個檔案會有不同版本,但我希望把每個版本都保存起來

Git 就是一個:幫我做版本控制的程式

版本控制要有哪些功能

以下是「自己用資料夾做版本控制」的方式,來推導出一個版本控制應該要有哪些功能(模擬 Git 的運作機制)

  1. 需要新版本:就開一個新的資料夾(新增一個 commit)
  2. 不想加入版本控制的檔案:就不要加入資料夾(例如:電腦本身的設定檔、放有帳號密碼的檔案等等,都不需要加入版本控制)
  3. 避免版本號衝突:用看似亂數的東西當做資料夾名稱(版本編號),每個亂數保證不會相等
  4. 知道最新版本:用一個檔案來存最新的版本號
  5. 知道歷史記錄:用一個檔案來存「每個版本的順序」

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 這個軟體呢?

作法就是:

  1. 打開 CLI,移動到 test 資料夾
  2. 輸入指令 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 的版本,作法如下

  1. 先使用 git log 去看歷史記錄,把第一次 commit 的版本號複製起來
  2. 輸入指令 git checkout 1bcbe402a46c5f917e4578bab7c481e9c17fe2f6,就可以切換到第一次 commit 的版本

這時,會發生兩件事情:

  1. 所有的檔案都會回到第一次 commit 時的版本
  2. 使用 git log 去看歷史記錄,就只會剩下第一次的 commit 記錄而已了(因為現在已經回到“第一次 commit”當下的時間點)

git checkout master 回到最新版本

這時,會發生兩件事情:

  1. 所有的檔案都會回到最新的版本
  2. 使用 git log 去看歷史記錄,就會看到所有的 commit 記錄

.gitignore 要忽略的檔案

只要是被放在 .gitignore 裡面的檔案都會被 Git 忽略(不會被 commit),下面是一些會被放在 .gitignore 的檔案:

  • 開發者個人相關的檔案
  • 跟專案無關的檔案
  • 作業系統產生的檔案(Mac 系統產生的 .DS_STORE
  • 暫存檔(.swp
  • node_modules

例如:
在我的專案中,我想要忽略掉「password 這個檔案」(不要被 commit)

  1. touch .gitignore 來建立 .gitignore 檔案
  2. vim .gitignore 進入編輯器,打上我要忽略的檔案名稱
  3. 這樣一來,在 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 (第一個版本)

專案建立後

情境一:有新的檔案

  1. 先使用 git add . 把全部檔案放到 staged 區域
  2. 再用 git commit -am 'second commit' 新增第二個 commit

情境二:只是修改原有的檔案

  1. 檔案都修改完之後,就可以直接使用 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'


#Git







Related Posts

[ 筆記 ] JavaScript 進階 09 - What is 「this」?

[ 筆記 ] JavaScript 進階 09 - What is 「this」?

Cookie 是什麼,能吃嗎?

Cookie 是什麼,能吃嗎?

筆記、What the heck is the event loop anyway? | Philip Roberts | JSConf EU

筆記、What the heck is the event loop anyway? | Philip Roberts | JSConf EU


Comments