---
# System prepended metadata

title: Git基礎
tags: [好想工作室]

---

# Git基礎
###### tags: `好想工作室`
2021.04.13（Tue.）~04.14（Wed.）

## 1. 版本控制
> 參考：[連猴子都能懂得Git入門指南：Git的基本介紹](https://backlog.com/git-tutorial/tw/intro/intro1_1.html)
> 參考：[為自己學Git：什麼是 Git？為什麼要學習它？](https://gitbook.tw/chapters/introduction/what-is-git.html)
> 參考：[git：1.1 關於版本控制](https://git-scm.com/book/zh-tw/v2/%E9%96%8B%E5%A7%8B-%E9%97%9C%E6%96%BC%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6)
> 參考：[程式碼時光機：應用 Git 版本控制，即使犯錯也不怕](https://www.appcoda.com.tw/git-xcode/)

首先，GitHub 和 Git 是不同的東西，Git 是一個在你自己電腦本機端運行的版本控制系統，而 GitHub 則是一個線上服務，讓你將 Git 版本資訊儲放至雲端，就像是 iCloud 雲端硬碟，但它是專屬於程式碼的應用，而且功能更強大。

簡單來說的話，Git是一個工具、Github是一個網站。

**版本控制分為三種：**

**1. 本地端版本控制系統**
我們在一個目錄中，對檔案做備份，可能就直接複製貼上，然後改個檔名而已。而我們對這個目錄中的檔案做修改（無論是複製貼上刪除新增），每次的更動都可以說是一個版本，只是用這樣的方法，我們無法看出過去修改過了什麼。

![](https://i.imgur.com/9Q5xtDy.png)


**2. 集中化的版本控制系統**
但是上面那種方法，僅限於個人使用，於是為了可以和其他人一起協同合作，就有了集中化的版本控制。他的做法是有一個伺服器來管理所有版本的檔案，而用戶端會們會連到這台伺服器取出檔案來修改使用。但缺點是如果這一個伺服器server掛了，那伺服器上的所有資料就會遺失。（本地端版本控制也有相同問題）

![](https://i.imgur.com/Sxu6ETx.png)


**3. 分散式版本控制系統**
用戶端把整個倉儲做了鏡像。 這樣一來，假設有任何一個協同合作的伺服器故障，事後都可以用任何一個用戶端的鏡像來還原，因為每個地方都有完整的資料備份。

而且不需要伺服器，也不需要網路都可以先做，等到有網路後，在做同步上傳就可以了。

![](https://i.imgur.com/PDujkHu.png)


而Git就是屬於第3種的分散式版本控制系統。

## 2. Git基本操作
> 參考：[為自己學Git：使用者設定](https://gitbook.tw/chapters/config/user-config.html)
> 參考：[git：1.6初次設定Git](https://git-scm.com/book/zh-tw/v2/%E9%96%8B%E5%A7%8B-%E5%88%9D%E6%AC%A1%E8%A8%AD%E5%AE%9A-Git)
> 參考：[連猴子都能懂得Git入門指南](https://backlog.com/git-tutorial/tw/intro/intro1_1.html)

先放上一些專有名詞與中文的對照：


| 英文 | 中文 |
| ---- | ---- |
|Revision|上次提交的狀態與現在狀態的差異記錄|
|Commit|提交。|
|Push|推送|
|Pull|拉取|

### **1. Commit**
（A）若想把變更與新增的檔案/目錄儲存到數據庫中，執行commit
（B）執行提交後，數據庫裡會產生上次提交的狀態與現在狀態的差異記錄

### **2. Push** 本地 -> 遠程數據庫
（A）為了將本地端數據庫的修改歷史共享到遠程數據庫，必須上傳本地端數據庫中儲存的修改歷史
（B）執行Push之後，本地端的修改歷史會被上傳到遠端數據庫。
（C）遠端數據庫的修改歷史就會和本地端數據庫的修改歷史保持同步。

### **3. Pull** 遠程數據庫 -> 本地
（A）欲同步遠端數據庫以更新本地端數據庫，執行pull
（B）執行pull之後，會從遠端數據庫下載最新的修改歷史，將其同步到自己的本地端數據庫。

****

一些基本指令：
### **0. 新增檔案**
```
$touch welcome.html
```

### **1. 首次安裝Git後，設定名稱、電子信箱**
在你安裝 Git 後首先應該做的事是設定使用者名稱及電子郵件。
注意：若你有傳遞 --global 參數，只需要做這工作一次，因為在此系統，不論 Git 做任何事都會採用此資訊。
（A）`$ git config --global user.name "名字"`
（B）`$ git config --global user.email 信箱@example.com`

### **2. 檢查設定值**
`$ git config --list`

### **3. 檢查特定設定值**
`$ git config <key> `
例如說想要找之前設定username，那就打`$ git config user.name `

### **4. 取得說明文件**
如果使用Git時遇到問題，想要取得 Git 命令說明文件，有3種方法：
（A）`$ git help <verb>`
（B）`$ git <verb> --help`
（C）`$ man git-<verb>`
例如說我想找config的命令說明文件，那用（A）的方法就是打`$ git help config`

### **5. 在現有資料夾中初始化目錄**
初始化這個目錄，讓 Git 對這個目錄開始進行版控。
`$git init`

### **6. 查詢目錄狀態**
這個指令的用途是用來查詢現在這個目錄的「狀態」。
`$git status`

檔案目前的狀態是 Untracked files，意思是這個檔案尚未被加到 Git 版控系統裡，還沒開始正式被 Git「追蹤」，它只是剛剛才加入這個目錄而已。

那麼建議應該馬上追蹤這些原本就有的檔案，然後進行第一次提交。 
你可以通過多次 git add 指令來追蹤完所有你想要追蹤的檔案，然後執行 git commit 提交：
```
$ git add *.c
$ git add LICENSE
$ git commit -m 'initial project version'
```
舉例來說，我有一個welcome.html的檔案還是Untracked files，那我可以先：
```
$git add *.html //此種方法可以將所有html檔一次加到暫存區
$git add welcome.html //此種方法就是只單加welcome.html到暫存區
$git add --all //此種方法是將所有檔案都加上去
```

此時，再用`＄git status`看一下目錄狀態，就可以發現剛才那個檔案從 Untracked 變成 new file 狀態了。這表示這個檔案已經被安置到暫存區（Staging Area），等待稍後跟其它檔案一起被存到儲存庫裡。而這個暫存區也稱之索引（index）

### **7. 提交你的修改**
如果僅是透過 git add 指令把異動加到暫存區是不夠的，這樣還不算是完成整個流程。要讓暫存區的內容永久的存下來的話，使用的是 git commit 指令：
```
$ git commit -m "init commit"
```
在後面加上的 -m "init commit" 是指要要說明「你在這次的 Commit 做了什麼事」，只要使用簡單、清楚的文字說明就好，中、英文都可，重點是清楚，讓不久之後的你或是你的同事能很快的明白就行了。（指"init commit"裏頭的文字就是拿來說明你對文件做了什麼修改。

要完成 Commit 指令才算是完成整個流程！

**重點：**
「Git 每次的 Commit 都只會處理暫存區（Staging Area）裡的內容」。也就是說，如果在執行 git commit 指令的時候，那些還沒被加到暫存區裡的檔案，就不會被 Commit 到儲存庫裡。

**整理：**
在 Git 裡，主要可以分成「工作目錄（Working Directory）」、「暫存區（Staging Area）」以及「儲存庫（Repository）」三個區塊，透過不同的 Git 指令，可以把檔案移往不同的區域
![](https://i.imgur.com/J2Whsa9.png)

（A）git add 指令：檔案從工作目錄 -> 暫存區（或索引）。
（B）git commit 指令把暫存區的內容 -> 儲存庫。


### **8. 檢視提交的歷史記錄**
在產生數筆提交（commit）或者克隆（clone）一個已有歷史記錄的版本庫之後，你或許會想要檢視之前發生過什麼事； 最基本也最具威力的工具就是 git log 命令。
```
$git log
```

git log 命令有大量且多樣的選項，能精確地找出你想搜尋的結果：
（A）**顯示每筆提交所做的修改內容**：`-p`用來顯示每筆提交所做的修改內容； 你還可以加上`-2`選項，限制只輸出最後兩筆提交內容。
```
$git log -p -2
```
（譯註：使用 - + 來表示差異，- 是刪除行，+ 是新增行；未修改的上下文資訊預設是三行，用來定位有修改的地方）

（B）**若想檢視每筆提交簡略的統計資訊**：`--stat`選項在每筆提交訊息的下方列出「被更動的檔案」、「總共有多少檔案被更動」、「這些檔案中有多少行被加入或移除」； 它也會在最後印出總結訊息。
```
$ git log --stat
```

（C）**改變原本預設輸出的格式**： `--pretty`， 用來改變原本預設輸出的格式； 有數個內建的選項供你選用， 其中`oneline`選項將每一筆提交顯示成單獨一行，對於檢視大量的提交時很有用； 更進一步，`short`、`full`、`fuller` 選項輸出的格式大致相同，但分別會少一些或者多一些資訊。
```
$git --pretty=oneline
```
（譯註：「作者（author）」與「提交者（committer）」之間的差別， 作者是最初修改的人，而提交者則是最後套用該工作成果的人）

### 9. 刪除檔案
（A）**方法一**：
使用系統指令 rm 或是檔案總管之類的工具來刪除檔案：
```
$rm welcome.html
```
把刪除當作是修改，所以修改完要再add到暫存區：
```
$git add welcome.html
```
既然到暫存區，如果確定要做「刪除」這個修改的話，就可以給他commit上去：
```
$git commit -m "delet it"
```
（B）**方法二**：
不過其實可以直接讓git幫你砍掉：
他就會直接到暫存區，少了add這個步驟
```
$git rm welcome.html
```

### 10. 不是真的想把這個檔案刪掉，只是不想讓他再被 Git 控管
運用關鍵字`-cached`，加上第9點的`$git rm 檔名`即可：
```
$git rm welcome.html -cached
```
這樣一來，他並不是真的被刪除掉，而是單純把檔案從git裡面給移除掉而已。

### 11. 變更檔名
跟刪除檔案一樣，變更檔名也是一種「修改」，所以操作上其實也是差不多的，所以方法一就不打了，直接運用方法二：
```
$git mv hello.html world.html //意思是將檔名從hello改成了world
```
雖然只是改檔名，但對 Git 來說會被認為是兩個動作，一個是刪除 hello.html 檔案，一個是新增 world.html 檔案（變成 Untracked 狀態）。

### 12. 復原
要修改最後一次的 Commit 訊息，只要直接在 Commit 指令後面加上 --amend 參數即可：
```
$git commit --amend -m"修改內容"
```

### 13. 把檔案併入最近一次 Commit
剛完成Commit才發現有一個檔案忘了加到，但又不想為了這個檔案重新再發一次 Commit時，就可以使用`$git reset`以及`--amend`
（A）第一步驟：
利用`$git reset`把最後一次的 Commit 拆掉，加入新檔案後再重新 Commit。
```
$git reset
```
（B）第二步驟：
使用`--amend`進行Commit（第12點有提到`--amend`）
```
$ git commit --amend --no-edit
```
`--no-edit`參數的意思是指「我不要編輯 Commit 訊息」

### 14. 新增目錄
假入我現在加入了一個目錄，看一下狀態`$git status`，會發現 Git 的狀態沒有變化。
```
$ mkdir images
```
這是因為 Git 在計算、產生物件的時候，是根據「檔案的內容」去做計算的，所以光是新增一個目錄，Git 是沒辦法處理它的。也就是說，空的目錄無法被提交！

要解決這個問題，就只要在那個空目錄裡隨便放一個檔案就行了。
如果目前沒東西可放，或不知道該放什麼檔案，可以放一個名為 `.keep`或 `.gitkeep`的空檔案，讓 Git 能「感應」到這個目錄的存在。
```
$ touch images/.keep
```

### 15. 忽略不需要的檔案
有些比較機密的檔案，例如資料庫的存取密碼或是 AWS 伺服器的存取金鑰，或者說任何對這個專案不必要的東西，當我不想把他們放在 Git 裡面一起備份時，可以這樣處理：

先新增一個檔案叫做`.gitignore`：
```
$ touch .gitignore
```
然後點進去檔案做編輯的動作，而編輯的內容，就是你希望git去忽略的東西，例如說你這樣編輯他：
```
# 忽略 secret.yml 檔案
secret.yml

# 忽略 config 目錄下的 database.yml 檔案
config/database.yml

# 忽略所有 db 目錄下附檔名是 .sqlite3 的檔案
/db/*.sqlite3

# 忽略所有附檔名是 .tmp 的檔案
*.tmp

# 當然你要忽略自己也可以，只是通常不會這麼做
# .gitignore
```

**忽略這個忽略：**
雖然.gitignore這個檔案有列了一些忽略的規則，但其實也可以忽略這個忽略的規則。只要在git add的時候再加上`-f`的參數，就可以無視規則，強迫闖關：
```
$ git add -f 檔案名稱
```

**注意：**
如果說新增.gitignore裡想忽略掉的列表前，就已經存在其中一個符合忽略規則的檔案，則他不會被忽略，因為他在.gitignore新增之前就存在了，有點像是不溯及既往的規則。

**清除忽略的檔案：**
如果想要一口氣清除那些已經被忽略的檔案，可以使用 git clean 指令並配合 -X 參數：
```
$ git clean -fX
```
那個額外加上的 -f 參數是指強制刪除的意思，這樣一來就可清除那些被忽略的檔案。

### 16. 想知道某行程式是誰寫的
如果想知道檔案中，程式的某行是誰寫的，可以利用`$ git blame`：
```
$ git blame welcome.html
```

**只想看其中幾行：**
```
$ git blame -L 5,10 welcome.html
```
利用`-L`後面的數字即限制的行數，以上面為例，就是只會顯示出第5 ~ 10 行的資訊。

### 17. 不小心把檔案、目錄刪除了
利用`checkout .`來還原不小心用`rm`刪除的檔案、目錄：
```
$ rm *.html //刪掉全部html檔案
```
```
$ git checkout welcome.html //找回welcome.html一個檔案
$ git checkout . //可以一次找回剛剛誤刪的檔案或目錄

$ git checkout HEAD~2 welcome.html
//拿距離現在兩個版本以前的那個 welcome.html檔案
//來覆蓋現在的工作目錄裡的 welcome.html 檔案
//同時也會更新暫存區的狀態

$ git checkout HEAD~2 .
//拿距離現在兩個版本以前的檔案來覆蓋現在工作目錄的檔案
同時也更新暫存區裡的狀態
```

另外，因為整個Git的紀錄都是放在根目錄的.git目錄裡，所以如果這個目錄被刪了，也就是表示歷史紀錄也被刪了，那就真的沒得救了。

### 18. 還原版本
如果commit完後悔了，想要拆掉重新來過，可以用`reset`：
先假如我們目前的Git紀錄：
```
$ git log --oneline
0084a3e (HEAD -> master)檔案1
51adeab 檔案2
93131b2 檔案3
a04a64f 檔案4
9544e00 檔案5
```
（A）相對的方法：
```
$ git reset 0084a3e^

//而這裡剛好HEAD跟master都指向0084a3e個commit
//所以也可以像下面這樣寫：
$ git reset HEAD^
$ git reset master^
```
在最後面的`^`符號代表的是往前幾次，像這裡就是往前一次，也就是說上面程式碼代表的是，要退回`0084a3e`的前一次版本。

換句話說如果是`^^`，則是到`0084a3e`的前兩次的版本，以此類推。不過如果要五次以上的話則直接`0084a3e~5`

（B）絕對的方法：
因為`0084a3e`的前一次正好是`51adeab`，所以可以直接如下所做：
```
$ git reset 51adeab
```

**git reset參數：**

| 模式 | - - mixed（預設） | - - soft | - - hard|
| ---- | --------------- | ------ | --- |
|commit拆出來的檔案|丟回工作目錄|丟回暫存區|直接丟掉|

****

**終端機常用的指令**
> 網站：[終端機及常用指令介紹](https://gitbook.tw/chapters/command-line/command-line.html)

補充：想清空畫面上的內容，除打clear之外，也可以按 `CTRL` + `L`