# Git 技術整理
###### tags: `Git`
[]()
> [name=AndyChiang] [time=Sun, Jan 17, 2021 3:15 PM] [color=#00CDAF]
我看的教學:[六角Git & GitHub 教學手冊](https://w3c.hexschool.com/git/cfdbd310)
## Git
### 認識Git
簡單講,Git可以幫你記下哪些時候,做了哪些變更,並可以隨時回頭看原先的版本,避免開一堆副本搞得眼花撩亂。
[Git下載流程](https://w3c.hexschool.com/git/3f9497cd)
而SourseTree則是讓Git視覺化,方便使用。
[SourseTree載點](https://www.sourcetreeapp.com/)
### 使用終端機
使用終端機才能真正學會Git的指令,以及理解什麼指令在做什麼事情。
* Win:*Git Bash*
* Mac:*終端機*
#### 常用終端機指令
| Windows | MacOS / Linux | 說明 |
| -------- | ------------- | --- |
|cd [路徑] |cd [路徑] |前往資料夾路徑|
|cd |pwd |取得目前所在的位置|
|dir |ls |顯示資料夾裡的檔案|
|mkdir |mkdir |新增資料夾|
|無指令 |touch |開新檔案|
|copy |cp |複製檔案|
|move |mv |移動檔案|
|del |rm |刪除檔案|
|cls |clear |清除畫面上的內容|
### 設定個人資料
* 輸入姓名:`git config --global user.name "your name"`
* 輸入個人的 email:`git config --global user.email "your email"`
* 查詢 git 設定內容:`git config --list`
### 基本架構

### Git 建立本地數據庫
```
git init
```
想要建立本地數據庫時:
1. 先在你的電腦新增一個資料夾
2. 終端機輸入這個指令`cd <該資料夾的路徑>`
3. 輸入`git init`指令
#### 查詢當前狀態
```
git status
```
可以知道檔案目前的狀態,git的狀態有以下圖片的這四種。

#### 將檔案加入索引
```
git add <檔案名稱>
```
比較常用的是`git add .`,將所有檔案全部加入索引。
#### 提交新版本
```
git commit -m "<填寫版本資訊>"
```
目前在索引內的檔案會被提交成一個新版本,填寫版本資訊則是為了在你未來回頭看的時候,知道這次commit在幹嘛。
#### 查詢版本
```
git log
```
你如果得到以下回饋,代表你有成功commit上去了。
```
commit b6c3c771cd8939bcd25a8c50089fdf0cd3eab98d (HEAD -> master)
Author: 你的姓名 <你的 Email>
Date: 你的版本更新時間
版本資訊
```
### Git 建立遠端數據庫
#### 兩個熱門的遠端數據庫比較
* [GitHub](https://github.com/):擁有 GitHub Pages 功能,可擁有私人數據庫,免費方案是 3 人以下,適合想要有一個公開對外網站的團隊。
* [Bitbucket](https://bitbucket.org/product/pricing):可擁有私人數據庫,免費方案是五人以下團隊,適合公司專案的小型團隊。
**我這裡都用GitHub當作練習**
基本上要連上遠端數據庫有兩種方式,兩種都差不多。
1. 先建立好本地端,建立新的遠端,然後本地端綁上遠端,將本地的檔案push上去。
2. 先建立好遠端,然後clone(之後講)遠端在自己電腦,直接在clone下來的資料夾內新增你的檔案,git會自動幫你連上遠端。
#### 本地數據庫綁定遠端數據庫
```
git remote add <遠端數據庫簡稱> <url>
// ex: git remote add origin <url>
```
遠端數據庫簡稱,預設為origin。
#### 推送到遠端數據庫
```
git push <數據庫簡稱> <分支名稱>
// ex: git push origin main
```
#### 複製遠端數據庫
```
git clone <複製的網址>
```
1. 在你的GitHub遠端數據庫,找到下圖的這個連結:

2. 終端機先cd到想要複製的資料夾位置,然後輸入`clone`。(**注意!不會產生新的資料夾**)
3. 會自動綁定遠端數據庫
#### 抓取遠端更新
```
git fetch
```
會發生在多人協作時,如果遠端的進度比本地端還要前面,這個指令就會將遠端資料抓下來,並更新再本地端,`git fetch`之後會長這樣:

你會發現本地的master還在原先的位置。
#### 本地同步更新
```
git pull
```
其實 **git pull = git fetch + git merge**
也就是說, `git pull` 就是在 `git fetch` 後,將 master 給 `merge` 到 origin/master 而已,結束後長這樣:

### Git 分支
#### HEAD
##### HEAD的相對位置
* 往前推一個版本:`HEAD^`,幾個`^`就往前推幾個版本。
* 往前推N個版本:`HEAD^N`
`^`也可以替換為`~`
##### HEAD脫離
```
git checkout <目前分支的commit SHA-1碼>
```

#### ORIG_HEAD
Git會幫你紀錄上一個HEAD的位置,因此在做一些危險操作時,可以使用`ORIG_HEAD`退回上一步。
#### 移動HEAD位置
```
git checkout <分支名稱/commit SHA-1碼>
```
HEAD就是目前指向的版本,透過此指令可以移動到想要的位置。

#### 建立分支
```
git branch <分支名稱>
```
如此一來,就會多出一個新的分支,初始的位置就是當前HEAD的位置。如果想在新分支上新增commit,記得用`git checkout` 切換到新的分支上喔!

##### 直接移動分支位置
```
git branch -f <分支名稱> <HEAD的相對位置/commit SHA-1碼>
```
* 相對位置:HEAD的相對位置
* 絕對位置:版本名稱
#### 合併分支
```
git merge <分支名稱>
```
此指令會將當前分支,合併到`git merge <>`輸入的分支中。
常見作法如下:
1. 先`git checkout`切換至較舊的分支
2. 輸入`git merge <較新的分支>`
3. 完成合併後,較舊的分支便有較新分支的commit
##### 合併-快轉模式

當master沒有作任何更動時合併到dev,此時不會產生新的commit,這種情況我們稱為==快轉模式==,當然反之就有非快轉模式。
##### 合併-非快轉模式

當master在分支之後有新的更動時,便會產生兩條相異的分支,**此時合併會產生新的commit**(如圖上C6),我們稱為==非快轉模式==。
##### `--no-ff`
```
git merge dev --no-ff
```
如果想要將master合併到dev上,卻又不想發生==快轉模式==,就要加入這個指令。

##### 另一種合併方式
```
git rebase <分支名稱>
```

`rebase`的概念是,因為上圖 cat 和 dog 兩條分支都源自於 master ,所以當我們使用`rebase`時,Git就會先複製一份 cat 的分支,然後重新給予commit編號並接在 dog 的後面。如此一來便合併便不會產生新的commit,而 cat 舊的commit則會在保留一段時間後,被Git回收。
要如何復原? 會用到之後講的 `reflog` 或者是 `reset ORIG_HEAD`。
但注意,使用rebase容易搞混,因此不推薦使用。
這裡提供一個方便的工具,學習如何活用Git分支:[Learn Git Branching](https://learngitbranching.js.org/?NODEMO=&locale=zh_TW)
#### 本地分支衝突
並不是每次合併都是順順利利的,**當兩條分支修改同一行Code時**,就會發生衝突。
可能會遇到這種情況...

master和dev兩條分支同時更改了h1的樣式,系統便會提示出現衝突:
```
❯ git merge dev
Auto-merging all.css
CONFLICT (content): Merge conflict in all.css
Automatic merge failed; fix conflicts and then commit the result.
```
**那麼,如何解決衝突?**
1. checkout 到 master 後,輸入 `git merge dev`
2. 發生衝突,all.css 變成 ==Unmerged== 狀態
3. 修改 all.css 後,重新加入到索引 `git add all.css`
4. 透過 `git status` 指令觀察,是否可以重新提交
5. 輸入 `git commit` 提交,並撰寫 commit 訊息,完成本次合併
#### 遠端協作分支衝突
這件事會常發生在多人協作的時候...
1. A和B協作,A先push上共同GitHub
2. B後來也要push上GitHub,於是她需要先把檔案給pull下來
3. 但B發現A的檔案和他的發生衝突了!!
**那麼,如何解決衝突?**
1. B必須先修正衝突,commit一個新的在本地端
2. 然後push上GitHub,在原先A檔案的後面

### Git 還原
#### 還原版本,且保留檔案
```
git reset <HEAD的相對位置/commit SHA-1碼>
```
#### 還原版本,且不想保留檔案
```
git reset <HEAD的相對位置/commit SHA-1碼> --hard
```
#### reset vs. checkout
* reset:帶著HEAD指向的分支移動至指定版本,HEAD脫離時無法使用。
* checkout:移動HEAD,不會帶動分支移動。
#### 還原你的還原
人總是會手賤犯錯,如果不小心還原了不想還原的版本時該怎麼辦?
```
git reflog
```
會顯示出先前的所有動作,複製復原的那次commit編號,再**reset**回去就好了,git都會幫你做紀錄,無須擔心檔案會不見啦。
#### 紀錄還原
```
git revert <commit SHA-1碼>
```
Git 會幫你還原這個commit更新的內容,但他不會幫你刪除,而是在新增一個還原後的commit(當然SHA-1碼也是新的)。
#### 還原時機統整
##### 新增檔案時,檔案還沒加入追蹤,想清空工作目錄
* 顯示要被清除的檔案:`git clean -n`
* 強制清除檔案:`git clean -f`
##### 檔案已加入追蹤,想還原工作目錄
* 單一檔案指令 :`git checkout -- <file>`
* 全部檔案指令:`git checkout .`
##### 檔案加入到索引,想退到工作目錄
* 指令:`git reset HEAD`
### Git 暫存
如果你A專案正寫到一半,突然老闆叫你修B專案的Bug,該怎麼辦? A專案還沒寫完又不能commit!
這時就該用到`git stash`了。
stach類似stack的概念,最後加入的記錄會在頂端({0})。
#### 暫存儲存當前目錄
```
git stash
```
#### 瀏覽 git stash 列表
```
git stash list
```
#### 還原暫存
```
git stash pop
```
#### 清除最新暫存
```
git stash drop
```
#### 清除全部暫存
```
git stash clear
```
#### 指定特定暫存紀錄
```
<stash指令> stash@{n} //n就是list上的編碼
```
[IT邦邦忙 - git stash 暫存檔案](https://ithelp.ithome.com.tw/articles/10220982)
## GitHub
### `README.md`(說明文件)
使用MarkDown語法,會直接顯示說明文件在專案底下。
#### 為圖片加上超連結
```
[![google]](https://google.com)
[google]:http://www.google.com/img/logo.jpg "GoogleLogo"
```
### 插入GitHub倉庫內的圖片
```
https://github.com/ 你的使用者名稱 / 你的專案名 / raw / 分支名 / 存放圖片的資料夾 / 該資料夾下的圖片
```
### GitHub Page(靜態網頁)
這個地方可以展示你的網頁!(但只有靜態網頁)
1. 建立一個本地數據庫,存著你的靜態網頁
2. 跟這之前的教學,在GitHub上建立一個遠端數據庫,並且將綁上本地端。
3. push檔案上GitHub
4. 成功後,到**GitHub數據庫>Settings>GitHub Pages**,**sourse選擇master**,如下圖

5. 完成後,再回到GitHub Pages,你會看到如圖的畫面,表示你成功了,網址要記好,它就是你靜態網頁的網址。

6. 若要更新內容,只要修改完後commit+push新的版本上來就好了。
我的範例
1. [個人小履歷](https://andychiangsh.github.io/myResume_v1/200904_%E6%88%91%E7%9A%84%E5%B1%A5%E6%AD%B7)
2. [甜點商家官網](https://andychiangsh.github.io/SweetShopWebsite/main02.htm)
### `.gitignore`(忽略檔案)
這個檔案要新增在你的專案裡面,功能是讓記錄在`.gitignore`內的檔案不會被版本控制,通常是用於插件,格式為:
```
index.html/
color.css
```
這樣一來,`index.html/color.css`這兩個檔案就不會有版本控制了。
`git status`可以查詢哪些檔案被忽略。
### PR(Pull requests)
當你在逛GitHub時,看到A的程式碼寫的不錯,但有一點小錯誤,你想幫他修改,該怎麼做?
1. 此專案的左上角會有一個==fork==按鈕,點選將此專案插入一份到自己的GitHub中。

2. 將自己GitHub上的專案`clone`到自己的電腦。
3. 修正程式碼
4. push上自己的GitHub
5. 然後選擇Pull requests>New pull request>Create pull request,輸入修正訊息後按確定。

6. 此時A那端就會收到你的pull request了,他看過如果覺得沒問題,他就會`merge`,並且加入一個merge過後的commit。
7. 你寫的程式碼就會出現在A的專案中了!
### GitHub Flow
GitHub Flow是一個由GitHub所規範的一個流程,許多團隊都會導入這套流程,讓開發順利進行。
#### 流程
1. clone 遠端資料。
2. 在 master 建立分支來新增功能。
3. 開始開發功能。
4. 發 PR (Pull Request) 申請合併。
5. 討論與檢視程式碼。
6. 部署(Deploy)。
7. 合併(Merge)。
#### 規範
1. 任何在 master 上的 commit 都是可以部署的。
2. 假使程式碼需要討論,請 PR 後在 comment 進行討論。
3. 如果想開發功能,務必新開分支。
[Understanding the GitHub flow](https://guides.github.com/introduction/flow/)
## 補充連結
* [在開源時代的興起下,如何透過License共享並保有權益](https://progressbar.tw/posts/61)
* [從自由軟體到開放原始碼,了解共享經濟的資源交換模式](https://progressbar.tw/posts/62)
## 進階補充
### GitHub desktop vs. SourseTree
#### GitHub desktop
* 優點:與GitHub連結良好(廢話,就是GitHub自己推出的),檔案可預設以VScode開啟。
* 缺點:沒有視覺化分支,沒有還原
#### SourseTree
* 優點:有視覺化分支,還原容易,有簡體中文,但還是覺得英文比較好XD
* 缺點:好像沒缺點(?
[GitHub desktop和SourseTree比較](https://iter01.com/567668.html)
### VScode Git 外掛
其實VScode上也有外掛可以使用git指令,基本的add、commit、push都可以,優點是如果你本身就是用VScode寫程式,不需要額外開其他程式,但缺點是沒有圖形化介面,可能會搞不清楚現在的branch長怎樣?
### SSH金鑰
除了HTTP外的另外一種連接GitHub方式,因為有一組公鑰和私鑰做雙重身分認證,比起HTTP更加安全。
設定SSH的步驟有點複雜,這裡就不贅述了,可以參考以下網址:
[SourceTree 連接 GitHub, Bitbucket (使用 SSH) 於 Windows](http://codeplanet.me/archives/2014/11/sourcetree-connect-github-bitbucket-use-ssh-on-windows/)