---
# System prepended metadata

title: Super Git
tags: [Git]

---

---
tags: Git
---
# Super Git
 tags: `Git`
 editor: `Vito`
 lang: `hant`
[ToC]

## :memo:行前須知



> 工作需要
> 我會將教學筆記製作成不只有Git bash而且也會有sourcetree的版本. (所以我可能要開始惡補我的sourcetree因為我真的不會用)



***
## :round_pushpin: 一、下載安裝Git

>進入官網並下載符合系統的版本  ➜  [南港展覽館站長觀光官方網站](https://git-scm.com/downloads)
>只需要點"下一步"直到你看見下面這個畫面

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

>他的意思是讓你可以在CMD或其他類似環境執行Git指令，但是這個設定將會覆寫某些功能，如果要使用的話請確保你熟知所有相關的議題。



***
## :round_pushpin: 二、設定個人資料

>當你安裝完成後請開啟Git bash
>想得到更多相關的指令：

:arrow_down_small: 輸入指令
```
git --help
```
:arrow_down_small: 輸出結果
![](https://i.imgur.com/JZSrri3.png)
</br>
>現在你可以開始設定你的相關資料

:arrow_down_small: 輸入指令
```
git config --global user.name "Vito Yang"
git config --global user.email "yutcpp41100202@gmail.com"
```

:arrow_down_small: 輸出結果
>如果你覺得甚麼都沒發生，那是正常的。

![](https://i.imgur.com/78HDpgg.png)
</br>
>當你設定完成後，你也可以執行下面的指令來看看還有哪些狗東西。

:arrow_down_small: 輸入指令
```
git config --list
```

:arrow_down_small: 輸出結果
![](https://i.imgur.com/WRInnUw.png)


</br>
</br>

:::info
**config的設定中還有很多東西但是就不講太多，主要放在協同合作以及版控操作還有實戰問題上**
:::




***
## :round_pushpin: 三、Repositroy 及 Remote Origin
 demo: `Github` `Git Bash`

>首先在Git平台上建立Repository如下圖 :arrow_down:
></br> 
![](https://i.imgur.com/4vQ8YF3.png)
</br>
>成功建立過後會進入到一個頁面用於提醒你將Local與Remote連結以及你要使用ssh或是https的方式，如下圖 :arrow_down: 
></br>
![](https://i.imgur.com/5S0bUu3.png)
</br>
>接著你會需要準備一個資料夾用來做Local的Git init(你也可以在建立Repository之前就先準備好，Up to you)
>
</br>

**:sparkles: 接下來我的作法會是直接打開==git bash(包含git init準備)==**
 :::info
**步驟**
1. 執行git bash
2. cd 到欲建立git init資料夾的path
3. mkdir 建立資料夾
4. cd 進入該資料夾
5. 在root path 執行 git init
6. 將專案置入資料夾(這邊使用文字檔示範專案資料夾init)
7. 加入追蹤檔案變更
8. 查看目前檔案狀態
9. 送出commit
10. 將remote https綁至local path
11. 第一次因應git init 的 push
12. 正常時候的push
13. 完成
 :::
> 執行git bash 
![](https://i.imgur.com/uCWFuEW.png)


> cd 到欲建立git init 資料夾的path

:arrow_down_small: 輸入指令
```
cd c:
cd Users/User/Desktop/
```
:arrow_down_small: 輸出結果

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

</br>

> mkdir 建立資料夾

:arrow_down_small: 輸入指令
```
mkdir <Directory name>
```
![](https://i.imgur.com/4SM7cSH.png)

</br>

> cd 進入該資料夾

:arrow_down_small: 輸入指令
```
cd <Directory name>
```
![](https://i.imgur.com/kYcK01Z.png)

</br>

> 在root path 執行git init

:arrow_down_small: 輸入指令
```
git init
```
![](https://i.imgur.com/J4kRQIs.png)

</br>

> 將專案置入資料夾(這邊使用文字檔示範專案資料夾init)

:arrow_down_small: 輸入指令
```
  NULL
```
:arrow_down_small: 輸出結果

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

</br>

> 加入追蹤檔案("." 意即 "全部")

:arrow_down_small: 輸入指令
```
git add .
git add <File Name>
```
:arrow_down_small: 輸出結果

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

</br>

> 查看目前檔案狀態

:arrow_down_small: 輸入指令
```
git status
git status -s
```
:arrow_down_small: 輸出結果

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

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


</br>

> 送出commit

:arrow_down_small: 輸入指令
```
git commit -m "<commit log content>"
```
:arrow_down_small: 輸出結果

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


</br>

> 將remote https綁至local path

:arrow_down_small: 輸入指令
```
git remote add origin https://github.com/VitoYangZL/GitTutorial.git
```
:arrow_down_small: 輸出結果

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

</br>

> 第一次因應git init 的 push

:arrow_down_small: 輸入指令
```
git push -u orgin master
```
:arrow_down_small: 輸出結果

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

</br>

> 正常時候的push

:arrow_down_small: 輸入指令
```
git push
```
:arrow_down_small: 輸出結果

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

</br>



***
## :round_pushpin: 四、本地/遠端

>如果是單人作業，通常會在本地端追蹤檔案(add)後需要提交commit到本地儲存庫，單人作業的話也許就不會有branch的衍生，除非==有特別需求或是有額外對於版本的測試項目==，但是保持master分支由其他分支來merge是個好習慣。


:::info 
本地端master : add :arrow_right: commit :arrow_right: push到遠端master
:::
>因為沒有別人與你有衝突，所以任何修改你只需要對自己原本的專案做完修改後再做另一次的 add , commit , push 就好了。
>但是如果今天需要與別人一起合作項目，你就得開始要考慮到分支、pull、merge還有==衝突==的問題。
:::info
本地端branch : add :arrow_right: commit :arrow_right: push 到遠端的branch
:arrow_right: merge Request :arrow_right:由遠端master merge 遠端branch
:::
#### :pushpin: **模擬情境**
>假設今天有兩個人要共同合作一個專案，我們有了A跟B兩位高手，而他們的master分支叫做masterA or masterB，他們的子分支叫做branchA or branchB，這時候會有多少分支呢？
>我們會有
><遠端>的 master 和 masterA 還有 masterB
><遠端>的 branchA 和 branchB 還有
><本地端>A的 masterA 和 branchA
><本地端>B的 masterB 和 branchB
**那這時候就必須要遵循git的共同協作的規則避免發生衝突。
最常見的衝突如下：
A和B都Fork了ver1，而A做完了他的部分後新增了一段Code並直接push，而隨後B也做完了，也是新增一段Code，這時候會發現無法Push，因為此時的遠端會不知道兩者原本相差的部分他要保留哪一段？**
```
解決方法可能會有兩種
1.直接打開IDE修改或刪除保留的Code
2.使用rebase command
```


## :round_pushpin: 五、commit出包了怎麼回頭

 demo: `Git Bash` `Sourcetree`
 :collision: **issue**:rebase,revert,reset



**:pushpin: 先說revert,reset**
>這兩個操作非常相似，
>都是將你code目前的狀況，返回到"某一次的commit結果"，
>差別就在於 :arrow_down: 
>++revert 會保留 commit log++
>而
>++reset 會連同 commit log 一起清除++

:::info
**revert** :  會新增一個commit log 來蓋掉前一個你不想要的commit，意思就是說，假設你在 ID5 想回到 ID3 那這時候你的 code就會先回到 ID3 的狀況，然後再commit一次產生了code跟ID3一樣的ID6。

</br>

**reset** :  而reset如果從ID5回到ID3之後就是回到ID3了，ID4及ID5將被清除。
:::

#### :pushpin: **模擬情境**
>今天我一個人獨立作業時，我發現我commit送錯了，我想要回到我前一個commit的狀態，並且我要重新開始我的作業(不保留git commit log)，那麼我要做的動作會是？

##### Gitbash

>先看目前的Git Log狀況，得知目前我們的HEAD在v3

:arrow_down_small: 輸入指令

```
git log
```

:arrow_down_small: 輸出結果

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

>我們想要從v3退到v2且不保留git log
>這邊會有兩個重點需要說明
>1.HEAD^1 是啥
>2. --hard ??

:arrow_down_small: 輸入指令
    
```
git reset --hard HEAD^1
```

>這邊可以看到你前面的v3已經不見了，硬生生地回去v2

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

>那為什麼會需要打 --hard ?
>其實在reset這個功能中有分為(soft,mixed,hard)三種模式如下

``` =
git reset --soft HEAD^
git reset HEAD^ 
git reset --hard HEAD^
```
>假設還原點為 HEAD^
>
>[WorkingTree] => [Index/Cached] =>[Repository]
>
>[本地資料夾檔案] =>[檔案變更索引]  =>  [檔案庫]
>
>Soft : 僅有 檔案庫 還原至前一個版本
>
>Mixed : 檔案庫及檔案變更索引 還原至前一個版本
>
>Hard : 所有階段都 
>
>執行過程可使用下方指令查看程式碼差異
```
git diff
git diff --cached HEAD
```

>那 HEAD^1 是啥?
>
>HEAD你可以當作目前所在位置，像是一個指標的概念，指著哪然後做甚麼事。
>那我們在reset的時候有可能會看到
>
>"HEAD^" 指定至前一個版本
>
>"HEAD^^^" 指定至前三個版本
>
>"HEAD^5" 指定至前五個版本
>
>"HEAD^dej4f8cvcs8" 指定至某一個git commit id的版本

</br>

***

</br>

##### Sourcetree


>在想要回到地版本commit上點擊滑鼠右鍵，並選擇
>" reset current branch to this commit "
>
![](https://i.imgur.com/b5Cntp2.png)

>接著選擇你要使用的模式(soft,mixed,hard)
>
![](https://i.imgur.com/uhcpL7a.png)

>如果使用了"hard"模式，會有彈跳視窗提醒你，使用Hard模式將會導致local的workingtree及index都會變更
>
![](https://i.imgur.com/Y0obMgb.png)

>執行後你會發現雖然你的commit回到前面的版本了，但是更新的commit還會在，因為這裡的可視化flow是抓reflog，所以你只需要再推一版上去就可以了。
>
![](https://i.imgur.com/MKwcPjy.png)

>其他還有更多複雜或比較繁瑣的情境留到後面熟一點再來說

***

## :round_pushpin: 六、用對gitflow還要培養好習慣

 demo: `Chart`
 origin: `https://reurl.cc/eXWxrm`
 source: `nvie.com`

>Git flow 
![](https://miro.medium.com/v2/resize:fit:1100/format:webp/1*8-zDz1s5Atux_yNW_mXmfg@2x.png)

>假設，你們已經有了**master**與**develop兩大常在型branch**，其他branch都已經不見了。
>為什麼要有==這麼強的假設==？後面會再詳述，而事實上，本文結束後你會發現，這樣的假設一點也不強，以一個健康的git工作流程來說，這樣的情況根本就是習以為常。

>早上你泡好咖啡，從你的座位上坐下，決定好今天要為這個專案加上一個發email的新功能，於是這個時候你要做的事是『加新功能』。既然如此，你就應該創一個新的feature branch，並將其命名為『add_email_feature』。

![](https://miro.medium.com/v2/resize:fit:352/format:webp/1*npmgYVH6g9m7GGhVFMDQgA.png)

>上圖中的粉紅圈圈所在的支線就是你新創的branch。依照gitflow的規定，他會被加上一個feature的目錄名：feature/add_email_feature，不為什麼，**只是為了方便辨識與管理而已**。
>接下來你就開始開發，開發與自動測試過程花了三天，結束後你就要結束這個branch，於是你merge回develop，一切又如一開始那樣祥和，世上只剩develop和master兩大branch。
當然，世事不可能永遠如我們想的那麼美好，專案越來越大，就會有越來越多功能在同步開發，這時候怎麼辦？就==同步==啊不然怎麼辦。

![](https://miro.medium.com/v2/resize:fit:720/format:webp/1*W01NDnDIeLvq3ROBoSJBAg.png)

>上圖這樣同時有好幾個feature同時在開發的場景多了去了。但是gitflow告訴我們不用擔心，儘管開發自己的功能便是，但是你一定要注意一點，**開發測試完自己的功能就一定要趕快merge回主線，並且結束這個branch**。

:::warning
***記住，無論何時，都要讓流浪在外的feature branch越少越好，branch生命週期越短越好。這件事情對幫助develop的穩定度有莫大的好處*。**
:::

>首先，我們要問自己一件事，在功能做完馬上就發現問題，進測後才發現問題，還是上線前一天發現問題比較好？
身為一個好的RD，你應該要有能力設下安全的toggle，並且同時能附上足夠完整的auto test讓你有信心這個toggle不容易被誤啟，更有信心QA有能力驗證這個toggle的開關，同時QA驗過的版本就肯定是拿去上線的版本才對。
如果上面的這些保證與信心都無法在你的工作上建立，那你們的專案危險程度是很顯見的。而且我可以確定你們工作上的困難絕對不是主要來自於git。你應該先想辦法解決那些造成危險的因素才對。

>好不容易熬到要上線了，這個時候我們要做的第一件事，就是要來決定版號。怎麼決定版號並不在本文要討論的範圍內，總之他就是被決定了。**接著就是要來開release branch了

![](https://miro.medium.com/v2/resize:fit:1100/format:webp/1*FNRRcBdikBkMDOvAjNIBpQ.png)

上圖中綠色的點，描述的正是release branch極短的一生。開啟release branch之後，**就開始進入生產前的總驗測。**

也有人只把release branch當成是進入master前的緩衝，讓總驗測針對master進行，一旦發生問題就視同正式環境問題處理，也就是開==hotfix==。

這兩種方法都有人用，各有優缺點。我覺得都不錯，但是有一件事一定要嚴格遵守：

:::warning
***一旦release開出來，就只能修bug，不能開新功能。
一旦release開出來，就只能修bug，不能開新功能。
一旦release開出來，就只能修bug，不能開新功能。***
:::
>因為太重要了所以要講三次。在實務上，總是有人喜歡偷渡一些新功能在bugfix裡面，但是要知道，此時大多已經沒有執行從頭驗測到尾的回歸測試了。這說穿了是對公司即將上市的產品，偷偷埋入未經詳實驗證的新功能，雖然很帥，但卻是非常危險的行為。

>俗話說得好，吃燒餅哪有不掉芝麻，寫程式哪有不出bug？一旦產品在正式環境出了問題，唯一一條路就是解掉他了。這個時候hotfix就該出場了。

![](https://miro.medium.com/v2/resize:fit:1100/format:webp/1*CH4QZJQM11CQiuPPno6NMg.png)

>上圖的紅點就是hotfix，它從master開出來，結束時回到master，順便修復develop，跟release branch很像，對不？

事實上不只流程像，大部分情形下，我們對待hotfix就要像release一樣：

* 開了branch的同時就要決定版號
* 只准修bug，不准偷渡新功能

>理由很簡單，因為hotfix和release與進入正式環境運轉的距離是一樣遠的，當然對待他們的謹慎度要一樣高才對。而hotfix因為通常事出緊急，又更難進行全盤的手動回歸驗證了（如果有自動回歸驗證就一定也要通過，但是這也不能完全保證無誤）。因此，為了避免更大損失，一定要謹慎再三才行。

>驗測完畢，hotfix結束，產品上線，順便修復develop，世界又回到最原始的單純狀態，只剩下develop和master了。

:::success

重點整理
* feature branch不要太長壽，最多最多，不要超過一個sprint
* 萬一內容太多，就回頭跟PO商量，這個需求太大了，必須修剪
* 品質好的自動測試可以消除你對頻繁回主線的疑慮
* 進版前一定要開release，預產線的測試版本一定要是上線的同一個版本
* 承上，改一行註解也不行！
* hotfix跟release開啟的同時就要決定版號，這能幫你確認版本
* 經常存在的branch只有兩個：develop與master
:::
