# 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) ---