---
# System prepended metadata

title: Github 教學
tags: [github]

---

#  Github 教學 
[toc]

## 基本指令
- 安裝, 實際步驟
    1. ``sudo apt install git``
    2. `git init`
    3. ``ssh-keygen -t rsa -C "youremail@mail.com"`` # 填 email
    4. 回到github，進入Account Setting，左邊選擇SSH Keys，Add SSH，title隨便填，貼上 `.ssh/id_rsa.pub` 的 key.
    5. 驗證是否成功<br/>``ssh -T git@github.com``
    6. 加入專案<br/>``git remote add origin https://github.com/USER/REPO.git
``

- 更新 user
    - `git config --global user.name ""`
    - `git config --global user.email ""`

- 更新
-> cd 到要更新的目錄
-> git add .
-> git commit -m '做了哪些更動' (取好一點名字, 方便之後找)
-> git push origin master(branch name)

- 找 branch name
-> git branch -m

- 修改要放的 repository 位置g刪除目前位置
-> git remote rm origin
加入要去的位置
-> git remote add origin url(repository 的 ssh 網址)

- 回溯 commit
    - 找出 commit 的代號
    -> git log --online --graph
    找出那次 commit 更改哪些, 會把更改的檔案的東西全印出
    -> git show x(代號)
    - 查前 n 次 : `git diff HEAD~n`
        - ex. `git diff HEAD~1`

- 查詢現在使用的 git user
-> git config --global user.name
設定名字
-> git config --global user.name "tommy"

- error : Please make sure you have the correct access rights
改成 https
git remote set-url origin https://github.com/

- 推不上去
    1. git pull origin master --rebase # 使用 Rebase 方式合併 
    2. git push origin master -f (不建議，會把之前的 commit 都洗掉)
- error : remote: Support for password authentication was removed
settings -> developer settings -> personal access token -> fine-grained token -> 給權限後得到 ssh token -> password 輸入 ssh token

- 查看分支 : `git branch` 、 `git branch -r`

- 建分支 : `git branch new_branch_name`

- 換分支 : `git checkout new_branch_name`
    - 檔案內容會被直接換成該分支

- 複製特定分支 : `git clone -b develop --single-branch https://github.com/tommygood/imCredit.git`

- commit 前查看這次新增修改了甚麼
    - `git add .`
    - `git status`

- 查看 remote 分支和本地的不同
    - 先看一下本地有沒有 remote branch
        - 若無就 `git fetch`
    - ex. 查看和 main 分支的不同 : `git diff main`

- readme
    - 通常會有的欄位
        - background
        - intertroduce
        - installation
        - usage : 使用方法

    - script tag 無法顯示的問題 
        - 要隔一行
        - ex. ![](https://hackmd.io/_uploads/SkeA-Vqrh.png)

- 刪除檔案的快取
    - `git rm --cached file_name`

- 一開始為 master branch
    - 一開始 pull 下來預設都是 master branch，所以如果 repo 是 main branch 或其他 branch 就要注意
    - 可以把本地的 master branch 對到 remote 的 main
        - `git push origin master:topic`

- 合併發生衝突 : `Automatic merge failed; fix conflicts and then commit the result.`
    - 原因
        - 因為相同檔案的相同行被不同人更改，且他們相同 branch 的 commit history 不同
    - 範例
        ```html=
        <div class="container">
        <<<<<<< HEAD
              <div>我是 Cat</div>
        =======
              <div>我是 Dog</div>
        >>>>>>> dog
        ```
    - 解法
        - 把要保留的留下來，不要的刪掉，再推一次
            ```html=
            <div class="container">
              <div>我是 Cat</div>
            ```
            
            - `git add .`
            - `git commit -m "fixed merge~"`
            - `git push xxx`

    - ref
        - https://gitbook.tw/chapters/branch/fix-conflict

- 抓遠端分支 : `git fetch`
    - 再 `git switch xxx` 就可 switch 到 xxx branch
- 刷新新增內容 : `git reset`

- `錯誤: 您對下列檔案的本機修改將被合併動作覆蓋` 
    - 可以用 `git restore` 把改過的檔案還原

- `警告：您正丟下 1 個提交，未和任何分支關聯`
    - 幫這個 commit 建一個 new branch : `git branch {commit_id} {new_branch_name}`
    - 切換回原本的 branch : `git checkout master`
    - merge 兩個 branch : `git merge {new_branch_name}`

- 更改 commit user
    - `git commit --amend --author="krixi <annnn6633@gmail.com>"`

## learning

- git add `file_name`
    - 將 file 加入 stage (暫存區
    - if `file_name` == `.` : 把當前目錄所有檔案加入暫存區 
    - `-p` : 逐個區塊問你要不要 add 到 stage
- git commit -m "msg"
    - 將 stage 的 file 修改的內容提交到當前 branch
    - 可在本地 commit 多次再 push 上去 remote

- `git push origin branch_name`
    - `origin` : repo 來源的名稱（也可改別的）
        - ![](https://hackmd.io/_uploads/HyvUwMnA2.png)
    - 設定 `upstream` 後可直接 `git push`
        - `git push -u origin {branch_name}`
            - 或是 `git push origin {branch_name}`
        - 之後就 push 就可以直接 `git push`
    - `git push origin master` 在幹嘛 ?
        - ![](https://hackmd.io/_uploads/SyUZbQh-p.png)


- `git status`
    - check 和上次 commit 不同部分
    - ![](https://hackmd.io/_uploads/SJz6mz2Ah.png)
    - ![image](https://hackmd.io/_uploads/Sy6_xT6e1x.png)


- `git log`
    - 看本地端目前有的 commit 紀錄
        - ![](https://hackmd.io/_uploads/BJR1hOnCh.png)
    - 看 remote 的 commit
        - 先抓 remote commit : `git fetch`
            - 將 remote 的新 commit 抓到 local
        - 再看 remote commit : `git log origin/master`
            - ![](https://hackmd.io/_uploads/SyXV3dn0h.png)
    - `git log --oneline`
        - 比較好閱讀

- `git fetch`
    - 抓 remote 的 branch 的新 commit（還沒 merge）
        - `git fetch`
            - 假設 remote 的 branch 上多了一個 commit
            - ![](https://hackmd.io/_uploads/Hyxc_G3bT.png)
    - 看遠端和本地的 branch 的 commit 的差別 : `git log {remote}/{branch}`
        - ![](https://hackmd.io/_uploads/r1nH_f3-a.png)
    - 看本地和遠端的新的 commit 的內容的差別 : `git diff {remote}/{branch}`
        - ![](https://hackmd.io/_uploads/SkIKtMh-6.png)
    - 將本地的 branch 合併遠端的
        - `git merge {remote}/{branch}`
            - ![](https://hackmd.io/_uploads/ry0-tGhWT.png)
        - 本地和遠端的 commit 就會相同
            - ![](https://hackmd.io/_uploads/SkDNKM2WT.png)

- `git pull origin branch_name`
    - = `git fetch` + `git merge`

- `HEAD`
    - 指向目前在哪一個 commit
        - ![](https://hackmd.io/_uploads/SJAA2kU16.png)
    - `detached HEAD` : `HEAD` 指向沒有 branch 的 commit
        - ![](https://hackmd.io/_uploads/ByFfTkIk6.png)
        - 當 checkout 到沒有 branch 指向的 commit 就會發生
            - 因為一個 branch 會指向一個 commit
            - ![](https://hackmd.io/_uploads/BJ5IGqZbT.png)
                - 所以這邊可以切到沒有 branch 指向的 commit : `git checkout 07a79ee` 
                - ![](https://hackmd.io/_uploads/Byi2z9b-p.png)
        - ex.
            - 一開始， head 指向 master branch
                - ![](https://hackmd.io/_uploads/rkh6Q5ZZa.png)
            - 切到之前的 commit : `git checkout cd82f29`
                - ![](https://hackmd.io/_uploads/SybM4cW-p.png)
            - 也可以在這個 detached head 狀態下 commit（但此時新的 commit : `b6d204e` 沒有 branch 指向他）
                - ![](https://hackmd.io/_uploads/rJ72v5-W6.png) 
            - 但若 checkout 回別的 branch，此 commit 就不易找到，所以會幫這個 commit 建一個新的 branch 就可以了
                - `git checkout -b {new_branch}`
                - ![](https://hackmd.io/_uploads/ByjC6JLkp.png)
    - 所以如果想要回到某一個 commit 再建新功能
        - 一開始在 main branch 有幾個 commit（目前 HEAD 在 main 的 branch 指向的 commit，也就是 c4）
            - ![](https://hackmd.io/_uploads/HJYrt5bWa.png)
        - 回到 c1 這個 id 的 commit 
            - `git checkout c1`
                - 或是回到 HEAD 的 3 個 commit 前 
                    - `git checkout HEAD~3`
                    - `git checkout HEAD^^^`
                - 或是回到 main 的 3 個 commit 前 
                    - `git checkout main~3`
                    - `git checkout main^^^`
            - ![](https://hackmd.io/_uploads/Hk4nt5b-6.png)
        - 在 c1 的狀況下開新功能
            - 幫這個 commit 建一個 branch : `git checkout -b new_func`
                - ![](https://hackmd.io/_uploads/H1275qbWa.png)
        - 在 `new_func` 的 branch 新增功能，加 2 個 commit(c5, c6)
            - `git commit -m "new1"`
            - `git commit -m "new2"`
            - ![](https://hackmd.io/_uploads/SkGiccZWa.png)
        - 新增完功能了，把新的 commit 整合回 main branch
            - `git rebase main`
            - ![](https://hackmd.io/_uploads/SyBko9bZp.png)
        - 把 main branch merge new_func merge
            - 先切回去 main : `git switch main`
            - 再 merge : `git merge new_func`
            - ![](https://hackmd.io/_uploads/BkwCT9-W6.png)
        - 若有 conflict 就須先解決才能 merge

    - ref
        - https://gitbook.tw/chapters/faq/detached-head.html



- `git branch`
    - 查看目前分支
    - `-r` : remote branch
    - branch 會指向一個 commit 的 reference
        - ![](https://hackmd.io/_uploads/rkLnPCSJT.png)
        - 建立 branch 過程
            - ![](https://hackmd.io/_uploads/SkyuFZW-a.png)
            - ![](https://hackmd.io/_uploads/SkEKtb--a.png)
    - 改變 branch 指向的 commit
        - 原本
            - ![](https://hackmd.io/_uploads/SkuwqQzb6.png)
        - 改到 c6 這個 commit : `git branch -f c6`
            - ![](https://hackmd.io/_uploads/r1Mc5Qfba.png)

- `git checkout`
    - 可以 restore 檔案、切換 commit（<b>切換 HEAD，不切換 branch</b>。所以 branch 還在相同 commit）、切換分支，但若是檔案和分支同個名字會優先做切換分支，但若是想要 restore 檔案就會有問題，所以後來拆分開來 :
        - `git restore` : 還原
        - `git switch` : 切 branch
    - 通常會用 `git checkout` 然後再對該 commit 開一個 branch，改完後再 merge，而不是 `git reset`
    - `-b` : 建分支並切換，只能建不存在的 branch
        - ![](https://hackmd.io/_uploads/B19wfeeZ6.png)

- `git reset`
    - 重新回到哪一個 commit (**可以往前 or 後**)，<b>切換 HEAD + branch</b> 指向的 commit
    - `git reset HEAD~1`
        - 回到上 1 個 commit
        - 可以改成 x 個
    - para
        1. `--mixed` 
            - 預設的
            - 會把舊的 commit 的資料從 stage 移除，但不會真正從檔案移除（不會修改新 commit 的內容）
            - 通常會用此來撤銷 commit
            - 如果想要還原檔案狀態成該舊 commit
                - `git restore {file}`
        2. `--soft`
            - 不會把 commit 的資料從 stage 移除，也不會真正從檔案移除
        3. `--hard`
            - 把 commit 的資料從 stage 移除，也從檔案移除
    - 撤回 reset
        - 不管是 mixed, soft, hard 都可以撤回 reset（也就是回到較新的 commit
        - `git reflog` : 查看 HEAD 的移動紀錄，只要有換 branch, commit, reset 都會有紀錄，所以可藉由此看到較新的 commit 的 id，再 reset 回去就好
    - ex.
        - 原本
            - ![](https://hackmd.io/_uploads/rkF7J4z-T.png)
        - 想回到 test5 的 commit : `git reset HEAD~2` 後
            - ![](https://hackmd.io/_uploads/SkhNyNG-p.png)
        - 若想要撤回 commit，可以看 head reference log : `git reflog`
            - ![](https://hackmd.io/_uploads/Hkqq1VzW6.png)
            - 可以看到原本 m2 commit 的 commid id
            - 再 reset 回去就可以了 : `git reset ee417d3`
                - ![](https://hackmd.io/_uploads/HJ5kx4GW6.png)
    - 特色
        - 適用於還沒 push 到 remote 的時候
        - 因為會更改 remote 的 commit 歷史

    - ref
        - https://gitbook.tw/chapters/using-git/reset-commit

- `git revert`
    - 建一個新 commit 來取代取消該 commit
    - ex.
        - 原本
            - ![](https://hackmd.io/_uploads/Hya-ZEMb6.png)
        - 想要回到 c1 commit 的狀態
            - `git revert c1`
            - ![](https://hackmd.io/_uploads/H18W74GWa.png)
                - 和 `git reset` 不同，會建立一個新的 commit 以回到舊 commit 的狀態
        - 特色
            - 可以保留舊的 commit 紀錄
            - 適用於已經 push 到 remote 的時候

- `git merge`
    - 可合併切出去的 branch 回主要 branch
    - 流程
        - 初始 repo，僅有一個 main branch
            - ![](https://hackmd.io/_uploads/r1yeTWbb6.png)
        - 要新增新功能或改 bug，切 new branch : `git checkout -b bugFix`
            - ![](https://hackmd.io/_uploads/Byg6hbbZ6.png)

        - 新增完功能，bugFix 分支 commit : `git commit -m "bugFix commit"`
            - ![](https://hackmd.io/_uploads/HyCf6WZWT.png)
            - 所以現在 bugFix branch 領先 main branch 一個 commit
        - main 需要馬上做一些調整，也做 commit : `git switch main` + `git commit -m "main commit"`
            - ![](https://hackmd.io/_uploads/ByzPh-Zbp.png)
        - 最後需要把新增的功能合併回 main branch : `git merge bugFix`
            - ![](https://hackmd.io/_uploads/B1wapbZW6.png)
            - c4 是因為 merge 而自動產生的 commit
                - ![](https://hackmd.io/_uploads/S1DgR-bWp.png)
        - 補充
            - 回到上一個 commit : `git reset HEAD~1`
                - ![](https://hackmd.io/_uploads/B1F00W-Za.png)
            - Fast-Forward
                - 若新的 commit 和原本的 branch 在同一條 commit history，就會直接更改新的 commit 改的地方（因不會有 conflict 的問題），<b>且不建立一個 merge commit</b>
                - 好處：少一個 merge commit
                - 壞處：無法保留合併的紀錄，不易觀察不同 branch
                - ![image](https://hackmd.io/_uploads/SJtFoaTekl.png)
                	- ![image](https://hackmd.io/_uploads/B1w5jT6g1e.png)
                		- fast forward : 預設
					- ![image](https://hackmd.io/_uploads/ry4oopaeyx.png)
						- `--no-ff` : no fast forward
            - 從 A branch merge B 和 從 B branch merge A 差在哪
                - 一個是在 A branch 上多新增一筆 commit（合併 B branch 的），另個相反
                - 但本地檔案內容會相同

- `git rebase`
    - 可以調整 commit 的歷史紀錄
        - 如果 commit 紀錄太醜，可以用此在送 pull request 前整理 commit，讓別人比較好 code review
    - 可以用來把新功能的 branch 的 commit 接回原本 branch 的最新進度的 commit（和 `git merge` 有合併分支的功能）
        - ex. 假設主要的 branch = main, 新功能切出去的 branch = x
            - 切 branch : `git checkout -b x`
            - 然後 main 和 x branch 都有新 commit（在切 x branch 之後的）
                - ![](https://hackmd.io/_uploads/SkcMZrWWT.png)
            - 現在在 x branch 做和 main rebase : `git rebase main`
                - ![](https://hackmd.io/_uploads/SJ9xWS-Z6.png)
            
            - 所以現在 x branch 的 commit 紀錄會是
                - x 的 commit
                - main 的 commit
            - 不過這樣會讓 x branch 的 commit 紀錄有問題
                - 因為後面（比較舊的）的 commit 歷史紀錄被改了，且一般只能新增新的 commit 在前面（比較新的），所以會 push 不上去
                - 所以如果硬要 push 就得用 `git push -f`
                - 所以最好不要在 main branch 上做 rebase
                    - ex. 
                        - bugFix 在 c1 commit 從 main 切出去
                            - ![](https://hackmd.io/_uploads/BJIMOU-Wp.png)
                        - `git rebase bugFix`
                            - ![](https://hackmd.io/_uploads/r18E_L--p.png)
                                - main branch 的歷史 commit 被改了
        - 和 `git merge` 不同，不會有合併分支而產生的新的 commit
            - ![](https://hackmd.io/_uploads/rk0g9H-b6.png)
        - 好處
            - 可以線性觀察 commit 歷史，且不會像 `git merge` 有多的 merge commit
        - 用法
            - 所以通常會先開一個新的 branch，當做完功能且 commit 好之後，在新的 branch 做 `git rebase main`
                - bugFix 在 c1 commit 從 main 切出去，且兩個分支在切出去後都有新的 commit
                    - ![](https://hackmd.io/_uploads/HJkD6Ub-p.png)
                - 在非主要分支做：`git rebase main`
                    - ![](https://hackmd.io/_uploads/BylM5IbbT.png)
                        - 注意：`C2`, `C2'` 和 `C3`, `C3'` 是不同 commit，原本的 commit 還會留存但變成沒有 branch 指向（不會馬上被刪掉，但過一陣子沒用會被 git 回收機制處理掉）
                    - 此時 bugFix 的 commit
            - 最後再切回 main branch 做 merge
                - `git switch main`
                - `git merge bugFix`
                    - 也可用 `git rebase bugFix`，在這邊是一樣意思
                    - 或是 create pull request
                - ![](https://hackmd.io/_uploads/ByuQcLZbT.png)

- conflict
    - A 和 B 都修改到同個檔案的同個地方，無法讓 git 自動去 merge
    - 流程
        - 有 A 和 B，且有一專案有一檔案
            ```=
            1234
            4567
            789
            ```
        - A 先修改 `a.txt`
            ```=
            1234
            4567
            78
            ```
            - `git add .`
            - `git commit -m "update by A"`
            - `git push`
                - 成功推上遠端
        - B 再修改 `a.txt`
            ```=
            1234
            4567
            7
            ```
            - `git add .`
            - `git commit -m "update by B"`
            - `git push`
                - 發生錯誤，因 commit history 和 origin 不同（遠端多了 A 的 commit，但本地沒有此 commit）
                    - ![](https://hackmd.io/_uploads/ryBMO2kWT.png)
                - 所以要先 pull : `git pull`
                    - ![](https://hackmd.io/_uploads/ryp8_2Jb6.png)
                        - 發生 conflict，且因為改到同一行，所以 git 無法 auto merge，所以會產生 conflict，就要手動解決
                            - ![](https://hackmd.io/_uploads/Hyk3_n1WT.png)
                            - 看是要保留 local commit 的內容還是遠端的（兩個衝突的 commit 紀錄都會留存，只是要決定保留哪個 commit 的內容）
                            - 把 `<<<<` 那些刪掉，留要用的內容就好（`HEAD` 到 `=====` 中間是自己 local 的紀錄）
                            - 然後一樣新增修改紀錄
                                - `git add .`
                                - `git commit -m "merge A"`
                                - `git push`
                            - 最後 git log（上面是最新的）
                                - commit : merge A
                                - commit : update by B
                                - commit : update by A
                    - 如果 A 和 B 改到不同行，git 可以自動 merge commit
                        - `pull` 之前，本地少了 `Update README.md` 這個 commit，但沒有 `pull` 就新增了一條 `git commit -m "test3"` 然後直接 `push`，所以會 error
                            - ![](https://hackmd.io/_uploads/BkinK3yb6.png)
                        - `pull` 之後
                            - ![](https://hackmd.io/_uploads/HyaaK3kbT.png)
                                - git 自動 merge，所以自動多了一條 merge 的 commit
                        - 最後再 `push` 到 remote 就 ok

- PR(pull request)
    - 開發新功能時先切出去不同 branch，完成後送出 request 讓主 branch 去 merge 新 branch，也可以讓別人來做 code review
    - 兩種情況
        1. 開源專案
            - 自己先 fork repo
            - 修改好，新增 commit 並 push
            - create pull request 讓原作者知道，他就會去審核是否可以 merge
        3. 內部專案
            - 開別的新功能的 branch
            - 修改好，新增 commit 並 push
                - ![](https://hackmd.io/_uploads/rk-dANzW6.png)
                    - 這邊新開了 test5 branch，並多新增兩個 commit
            - create pull request 讓其他 code reviewer 知道，他們就會去審核是否可以 merge
                - 切到新的 branch, 案 contibute 的 open pull request
                    - ![](https://hackmd.io/_uploads/B1WZxHzWp.png)
                - 選擇要 merge 的 branch
                    - ![](https://hackmd.io/_uploads/HJCmkBMWa.png)
                    - 但也有可能因為 conflict 不能 merge
                        - ![](https://hackmd.io/_uploads/rkNKTVMZp.png)
                        - 就要先切到新的 branch 再做 `git rebase {舊的要切回去的 branch}`
                - 解決完 conflict 就可以 create pull request
                    - ![](https://hackmd.io/_uploads/BJfGTVG-p.png)
            - 沒問題就案同意 merge
                - ![](https://hackmd.io/_uploads/rkHHT4fW6.png)
            - 新增的 commit 就會自動被 merge
                - ![](https://hackmd.io/_uploads/Sy6UA4fbT.png)

- `git diff`
    - 看不同 commit 間差異
    - ex.
        - 指定兩個 commit
            - ![](https://hackmd.io/_uploads/ryQ6uEMWT.png)
        - 看現在和上一個 commit 差異
            - ![](https://hackmd.io/_uploads/ByDxtNzWp.png)
        - 在 commit 前看看新增了哪些
            - ![](https://hackmd.io/_uploads/Hy7rVXn-T.png)
            - 在 `git add .` 前後都可以看

- `git cherry-pick`
    - 只想要 merge 指定的 commit
        - 可能一個 branch 中有幾個 commit 正常，有幾個有問題
    - ex.
        - 有兩個 branch
            - ![](https://hackmd.io/_uploads/ryp6V32Za.png)
        - main 只要 c2, c3 : `git cherry-pick c2 c3`
            - ![](https://hackmd.io/_uploads/rkb9433W6.png)

- `git remote`
	- `-v` : 列出遠端數據庫列表(包含 rul)
		- ![image](https://hackmd.io/_uploads/rk42XxTlyg.png)
	- `add <遠端數據庫簡稱> <url>` : 加入遠端的來源，可以有多個

- 教學
    - https://learngitbranching.js.org/?locale=zh_TW
        - git tree
## Git Hook
### 介紹
- 在 git repo 中，在特定的 event (hook) 發生時，自動觸發特定的 script
### 架構
- ![image](https://hackmd.io/_uploads/HyJp6Trlyg.png)
- 讓 `.git/hooks` soft link 到一個共用的 hook directory
- 在 hook directory 放對應種類的 shell script
### hook 種類
- pre-commit
    - 可以在使用者在 commit 時做一些檢查。
    - 這些檢查包括自動格式化程式碼、自動測試等等。
    - ![image](https://hackmd.io/_uploads/r16NC8zb1e.png)
- commit-msg
    - 公司或是團隊對於每個人提交的 commit 訊息有特定的要求，比如說字數、大小寫開頭等等
    - ![image](https://hackmd.io/_uploads/r1AZJCBx1l.png)
    - ![image](https://hackmd.io/_uploads/SkbmyRBeye.png)
- pre-push
    - 在 push 前進行某些測試，如果測試通過就推送到遠端私服器，否則就取消執行。
    - ![image](https://hackmd.io/_uploads/BJVlJCrg1e.png)
### ref
- https://yhtechnote.com/git-hooks/

## Github Actions
- https://hackmd.io/vQR0RZlDTjOxcU9p8F6AYQ

## merge commits from 2 repos
1. 在專案 A 中添加專案 B 為遠端倉庫：
    - `cd /path/to/project-a`
    - `git remote add project-b /path/to/project-b`
2. 抓取專案 B 的所有資料：
    - `git fetch project-b`
3. 將專案 B 合併到專案 A 中： 這裡假設你要合併專案 B 的 main 分支到專案 A 的 main 分支。
    - `git merge project-b/main --allow-unrelated-histories`
        - `--allow-unrelated-histories 允許合併兩個不同歷史的專案。`
## Good Commit Msg
### 組成
- ![image](https://hackmd.io/_uploads/rkWXnXagye.png)
- type
	- ![image](https://hackmd.io/_uploads/r1c82mTgyx.png)
### 範例
- ![image](https://hackmd.io/_uploads/SyWbhmaekg.png)
- ![image](https://hackmd.io/_uploads/Hks_3XTgyl.png)
- ![image](https://hackmd.io/_uploads/BJhKnm6lye.png)
- ![image](https://hackmd.io/_uploads/S12chmTlkg.png)
- ![image](https://hackmd.io/_uploads/H1Ps2QTlyl.png)
- ![image](https://hackmd.io/_uploads/rkw6hmTx1x.png)
- ![image](https://hackmd.io/_uploads/HyWR2max1g.png)
- ![image](https://hackmd.io/_uploads/rysJ6XTg1g.png)
- ![image](https://hackmd.io/_uploads/SkHgpmTlyl.png)
- https://wadehuanglearning.blogspot.com/2019/05/commit-commit-commit-why-what-commit.html

## Conventional Release Bot
- Conventional Release Bot 是 Hahow 開源的 GitHub 機器人，它能夠根據 Conventional Commits 的規範，自動幫你產生 GitHub Releases 和 Git tags。
- 總結一下 Conventional Release Bot 的優點：
	1. 自動化產生 CHANGELOG（GitHub Releases）
	2. 自動化更新 SemVer 版本號（以 Conventional Commits 的 types 為基礎）
	3. 通過友善的結構化 commit 規範，讓開發者更容易瀏覽更新歷史，提高對專案做出貢獻的意願
	4. 更好地向人類（同事、使用者或其他利益相關者）傳達軟體更新的本質
- https://tech.hahow.in/%E5%A6%82%E4%BD%95%E8%87%AA%E5%8B%95%E5%8C%96-github-releases-%E6%B5%81%E7%A8%8B-6e7e33b61169

## 如何跟上 fork 的 repo 的 commit
- `git remote add upstream (upstream url)` : 添加 upstream 的 remote origin
	- ![image](https://hackmd.io/_uploads/H1ZADhTg1g.png)
- `git fetch upstream`
- `git merge upstream/master` :  將 upstream merge 到我們當前分支。

## merge pr
1. Create a Merge Commit 
	- main 分支會增加一個 merge commit，且保留完整的分支 commit 歷史。
	- ![image](https://hackmd.io/_uploads/BkVPtaaxJg.png)
2. Squash and Merge 
	- 不會在 main 中生成合併提交，只會有一個合併後的單一 commit
	- ![image](https://hackmd.io/_uploads/ByQut6px1l.png)
3. Rebase and Merge
	- 不會生成 merge commit，但會保留原分支中的每個 commit

## git submodule
- Git 可以做到 repository 中嵌入其他 git repositories, 相當於版本控制原本的 repository 之外，也把內嵌的 repositories 也納入版本控制的範圍，這功能被稱為 submodule 。
- Submodule 適合應用在多個 repository 共享 library, utility functions 的情境，可以節省各個 repository 重複開發的成本，例如下圖， repo A 與 repo B 共用同一個 submodule, 所以 repo A 與 repo B 都只要共同維護一份 submodule 即可
- ![image](https://hackmd.io/_uploads/BytAj8Mbyx.png)
## 清理重要敏感資料
- bfg
	- ![image](https://hackmd.io/_uploads/SkUnn8z-Je.png)
	- 速度更快
- filter-branch
	- ![image](https://hackmd.io/_uploads/Hy2Jp8zbkl.png)
## work tree
- ![image](https://hackmd.io/_uploads/Bk4xyPM-Jx.png)
## Github Pages
- 免費建立個人、其他專案的靜態網站
- 免費 domain : username.github.io
### 教學
- ref : https://medium.com/%E9%80%B2%E6%93%8A%E7%9A%84-git-git-git/%E5%BE%9E%E9%9B%B6%E9%96%8B%E5%A7%8B-%E7%94%A8github-pages-%E4%B8%8A%E5%82%B3%E9%9D%9C%E6%85%8B%E7%B6%B2%E7%AB%99-fa2ae83e6276

---


## 簡易版
### 使用終端機
1.  cd 到要更新的目錄
2. `git add .` : 把要更新的東西放到紀錄檔
	> (將更改的檔案列入準備更新區 (staged))
4. `git commit -m '做了哪些更動'` : 提交紀錄(這次做的更動)
	> 將更改的內容寫入本機資料

5.  `git push origin master(branch name)` : 從本地push資料到雲端
	> (將更改的內容寫入遠端資料)

![](https://hackmd.io/_uploads/BJSYwf9Za.png)

---

### 使用 vscode

[教學網站](https://pythonviz.com/git/use-git-in-vs-code-basic-operations/)
#### 1. 加入新的修改（commit）

> 把需要加入修改（commit）的檔案加入「Staged Changes」

* 把檔案加入 **暫存變更 (Staged Changes)**


![](https://hackmd.io/_uploads/ByePUX7hh.png)

1. 首先在 Changes 的框框下，選擇 main.py 並點擊暫存變更(Stage Changes)的按鈕。
1. 完成後，會出現 Changes 框框了，多出了「Staged Changes」一欄，而 main.py 將會在 Staged Changes 以下。
1.  而且在File History 的框框裡，我們從本來的「Uncommitted changes」變成了「Staged Changes」。這是代表已經把 main.py 裡的更改提升至「Staged」的環節。
1.  留意我們的檔案現在是綠色的「Added」狀態，代表我們已經將新的檔案加入了 Staged Changes 和 Git 的視線範圍。
 
#### 2.把 Staged Changes 加入修改（commit）和分支（branch）
> 輸入修改信息（commit message）並在分支（branch）加入修改（commit）
* 在右上方「Message (Command Enter to commit on ‘master’)」的框框裡，我們應該**輸入一些有關這個修改（commit）有用的訊息**。例如我們加入了「main.py」，可以填寫「Create new file」。
* ![](https://hackmd.io/_uploads/BJDU7XX22.png)

點擊「Command + Enter」，便成功把我們的 Staged Changes 變成一個修改（commit），加入了 master 這個分支（branch）了

完成後，我們可以見到以下的改變：
* Changes/Staged Changes 的框框現在是空白的，代表我們已經把所有檔案加入分支（branch）的修改（commit）
* Commits 的框框從「No commits could be found」多出了「Create new file」的修改（commit）。這是顯示我們現在使用的分支（即 master 分支）有了 1 個新的修改，就是我們剛剛加入的修改。如果我們放大（expand）這個項目，會見到 main.py 的更改
* File History 的框框多出了「Create new file」一行。由於 File History 會顯示我們現在開啟的檔案（即 main.py）以往有什麼修改，所以它告訴我們，在「Create new file」的修改了我們曾經更改了 main.py
* Branches 的框框裡，master 分支多出了一個「Create new file」的修改（commit），跟 Commits 框框的資訊一樣。

#### 3.更改已有的檔案及提交修改
修改現有的檔案（existing file），會發現檔案現在是處於黃色「Modified」的狀態。這是由於我們在 Git 的上一個修改（commit）已經加入了 main.py，所以 Git 知道這個檔案的存在。
> 紅色的部分代表我們上一個版本的內容
> 黃色的部分有新增的內容，這代表我們現在對檔案做出了什麼更改


#### 4.為現有檔案的更改加入修改（Commit）

整個流程其實跟上面新加入 main.py 的時候的流程一樣：

1. 儲存已更改的檔案
1. 把需要加入修改（commit）的檔案加入「Staged Changes」
1. 輸入修改信息（commit message）並在分支（branch）加入修改（commit）

### 5.還原（rollback）檔案及移除修改（commit）

> 有時候為檔案加入了新的修改（commit），但發現還是原來的版本比較好

以下是還原檔案的步驟。這些步驟就像是*加入修改的反轉*：

1. 在分支（branch）選擇修改（commit），並進行修改還原（Undo Commit）
1. 把變更從「Staged Changes」駁回至「Changes」
1. 在「Changes」裡放棄變更（Discard changes）

* 在 Commits 的框框裡用滑鼠浮動在檔案的修改（commit）上， 會有個像是還原的 捨棄變更「Undo Commit」 按鈕。
![](https://hackmd.io/_uploads/Bk3I6QQ2h.png)

點擊 修改還原（Undo Commit）後，變更回到了「Staged Changes」的階段。這時候，其實就跟我們先前「Stage Changes」後，還未輸入修改訊息（Commit message）一樣。

現在我們的 main.py 已經變回黃色的「Modified」狀態。
我們把滑鼠浮動在「Staged Changes」的 main.py 上，會發現一個像減號的按鈕，讓我們可以「Unstage Changes」，即把這些變更駁回到「Unstaged Changes」的列表裡。
![](https://hackmd.io/_uploads/HyHP0XX23.png)

點擊「Unstage changes」以後，main.py 的變更來到了「Changes」的列表。
如果我們把滑鼠浮動在「Changes」的 main.py 上， 會發現又一個還原的標誌。這次，它的描述是「Discard Changes」，即是把這些變更放棄的意思。
![](https://hackmd.io/_uploads/BJX1yN723.png)
點擊「Discard Changes」以後，您會見到一個彈出視窗，詢問您是否真的要放棄這些變更。這是 VS Code 給我們的最後警告，過了這次以後，我們便再不能恢復這些變更。

點擊藍色的「Discard Changes」按鈕繼續：![](https://hackmd.io/_uploads/B1dxJNQ2n.png)
回到起初第 1 次修訂的版本

---

##### 專案啟用 Git
1. VS Code 的左邊，點擊「Git」的圖案，並點擊「Initialize Repository」![](https://hackmd.io/_uploads/H1xU1Q7hn.png)
2. 完成後，原始檔控制(Source Control)版面應該有以下的選項：
 ![](https://hackmd.io/_uploads/r1cWlmQ2n.png)

3. 新檔案變成了綠色，而在 Explorer 的右側出現了「U」的字眼。「U」的全寫是 Untracked，意思就是這一個檔案未在任何的 Git 分支（branch）裡有紀錄。
4. 跳到「Source Control」的版面。留意在「Changes」框框裡多了「main.py」。這個「Changes」的框框十分重要，因為它會告訴我們有哪些檔案被修改了，包括新建的檔案。也能看到「File History」框框裡多出了一行「Uncommitted changes」。這個框框有助我們了解這個檔案的改變過程。由於我們的「main.py」檔案是新建的，所以在 File History 裡只有未在任何 Git 分支的紀錄。

![](https://hackmd.io/_uploads/SJVx-77h3.png)


---


