<style> .reveal table, .reveal table th, .reveal table td { border: 1px solid; font-size: 33px; text-align: center; vertical-align: middle; } </style> # 如何使用 Git? (本地篇) --- ## 目錄 - Git基本介紹 - 如何開始使用 - 環境設置 - 檔案管理 - Commit管理 - 分支管理 - 暫存工作區 --- ## Git基本介紹 ---- ### 那些年一起玩單機遊戲 <img src="https://i.imgur.com/p7FZbA3.gif" height="400"> ---- ### Git 行為描述 | Git | Game Boy | | -------- | -------- | | Commit | 存檔 | | Checkout | 讀檔 | | Branch | 分歧點 | | 遠端倉庫(Repository) | 遊戲平台 | --- ## 如何開始使用 ---- ### 安裝 Git <li style="text-align:start;padding-left:50px;font-weight:600;font-size:35px;margin-bottom:10px;">可以到<a href="https://git-scm.com/downloads">官網</a>選擇目前作業系統環境進行安裝</li> <li style="text-align:start;padding-left:50px;font-weight:600;font-size:35px">Mac OS 作業系統可以使用 HomeBrew 安裝</li> ```= /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" brew install git ``` ---- ### 初始化資料夾 ```= # 建立空資料夾 mkdir git-demo # git 初始化 git init git-demo ``` --- ## 環境設置 ---- ### 查看 Git 設定 <li style="text-align:start;padding-left:50px;font-weight:600;font-size:35px">查看 global 的 config (全域)</li> ```= git config --list (預設是global) git config --global --list vim ~/.gitconfig ``` <li style="text-align:start;padding-left:50px;font-weight:600;font-size:35px">查看 local 的 config (專案)</li> ```= cd 專案路徑 git config --local --list vim 專案路徑/.git/config ``` ---- ### 常用的設定 <li style="text-align:start;padding-left:50px;font-weight:600;font-size:35px">設定用戶名及信箱</li> ```= git config --global user.name <username> git config --global user.email <email> ``` <li style="text-align:start;padding-left:50px;font-weight:600;font-size:35px">命令的別名</li> ```= git config --global alias.<aliasname> <commandname> # example git config --global alias.st status ``` <p style="text-align:start;color:red;font-size:25px;padding-left:50px;margin-top:30px;">* 如果 --global 改成 --local 就是當前專案的設定 </p> --- ## 檔案管理 ---- <img src="https://i.imgur.com/bp5hKoX.png" width="580" height="500"> ---- ### 未被追蹤的檔案 <img src="https://i.imgur.com/y3mEGSZ.png" width="800" height="160"/> ---- ### 被更動但尚未要提交的檔案 <img src="https://i.imgur.com/VE6UUqP.png" width="800" height="160"/> ---- ### 將要提交的檔案 <img src="https://i.imgur.com/GescWTb.png" width="800" height="160"/> <p style="text-align:start;font-size:25px;padding-left:75px;font-weight:600;">* 在此狀態用 git reset HEAD 會還原原始狀態(未追蹤檔案、更動尚未被提交)</p> ---- ### 查看檔案 ```= # 查看目前 unstaged 檔案 git status # 查看已提交的檔案內容 git show <hash id> # 查看尚未提交的檔案內容 git diff <file> # 查看歷史紀錄 git log ``` ---- ### 新增/提交檔案 ```= # 將全部的檔案設為staged git add . 或者 git add -A # 將單一的檔案設為staged git add <file> # 提交訊息 git commit -m <message> ``` ---- ### 刪除檔案 <li style="text-align:start;padding-left:60px;font-weight:600;font-size:35px">前置條件為已經進入 git 版控的歷史紀錄</li> ```= # 遠端與本地端都刪除檔案 git rm <file> # 遠端刪除檔案,但本地端依然保留著 git rm --cached <file> ``` ---- ### 檔案還原 <li style="text-align:start;padding-left:60px;font-weight:600;font-size:35px">前置條件為尚未提交的狀態</li> ```= # 還原未修改前的檔案內容(單一檔案) git checkout -- <file> # 將所有未提交的檔案還原未修改的內容 git reset --hard HEAD ``` ---- ### 忽略檔案 <div style="display:flex;justify-content:space-between"> <p style="text-align:start">新增 .gitignore 檔案,把該目錄想忽略的檔案路徑寫在該檔案裡面。</p> <img src="https://i.imgur.com/L7oesL4.png" width="400" height="430" style="margin-left:30px;"> </div> --- ## Commit 管理 ---- ### 什麼是 Commit? <ul style="margin-left:25px;"> <li style="text-align:start;font-weight:600;font-size:33px">為提交後的檔案“標記訊息”</li> <li style="text-align:start;font-weight:600;font-size:33px">每一個 commit 內容只記錄"修改"的程式內容,並"不是完整"的code</li> </ul> <img src="https://i.imgur.com/cM1ayI1.png"> ---- ### 關鍵字 HEAD <li style="text-align:start;padding-left:45px;font-weight:600;font-size:35px">HEAD 為 git 的關鍵字,代表目前 commit 所在的位置 </li> <li style="text-align:start;padding-left:45px;font-weight:600;font-size:35px">HEAD^ 或 HEAD~1 代表上一個 commit 位置 </li> <li style="text-align:start;padding-left:45px;font-weight:600;font-size:35px">HEAD^^ 或 HEAD~2 代表上兩個 commit 位置 </li> <p style="text-align:start;font-size:35px;margin-left:90px;margin-top:0px;">以此類推...</p> <img src="https://i.imgur.com/Eci51RV.png" width="850" height="150"> ---- ### Reset 還原 <li style="text-align:start;padding-left:45px;font-weight:600;font-size:30px">透過 reset 可以隨心所欲還原最初的內容</li> <li style="text-align:start;padding-left:45px;font-weight:600;font-size:30px">可以搭配 HEAD 讓我們不用死記 commit id</li> ```= # 回到上一次 commit,還原為 modified、untracked 檔案 git reset HEAD^ # 回到上一次 commit,還原為 changes to be commited 檔案 git reset --soft HEAD^ # 回到上一次(或是指定) commit,並強制清除第一個已提交內容 git reset --hard HEAD^/<commit id> ``` <p style="color:red;font-size:30px;text-align:start;margin-left:40px;">*建議在操作前先將工作區的程式暫存 (stash) 起來</p> ---- ### 找回消失的 commit <ul style="margin-left:10px;"> <li style="text-align:start;font-weight:600;font-size:30px">使用 git reflog 來查看 HEAD 的歷史紀錄,至少可以保留一個月</li> <li style="text-align:start;font-weight:600;font-size:30px">透過 git reset --hard <commit id> 還原</li> </ul> <img src="https://i.imgur.com/eqcehou.png" height="400"> ---- ### 修改已提交的內容 <p style="text-align:start;margin-left:30px;font-size:33px;">有時候 commit 訊息打錯,想要重新修改訊息,以下有三種方式</p> <li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">第一種</li> ```= git reset --soft HEAD^ git commit -m '訊息內容' ``` <li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">第二種</li> ```= git commit --amend -m ``` <li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">第三種</li> ```= git rebase -i <commit id> ``` ---- ### 何謂 cherry-pick <li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">cherry-pick 為少量操作(複製貼上) commit</li> <li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">每一個 commit 為紀錄修改的片段程式碼代表一個物件</li> ```= git cherry-pick <commit id> ``` ---- ### 何謂 rebase <li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">重新定義基準點</li> <li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;">在操作特定 commit 時,需要加上 -i (interactive) 參數進行互動模式</li> ```= git rebase -i <commit id> ``` <img src="https://i.imgur.com/YpCMzVZ.png" height="350"> ---- ### 何謂 revert <li style="text-align:start;font-weight:600;font-size:27px;padding-left:30px;">實作概念很像是數學運算的 100 + (-100) = 0</li> <li style="text-align:start;font-weight:600;font-size:27px;padding-left:30px;">當發現某個 commit 有問題想要拿掉,而且是前面已經很多 commit 的情況</li> ```= git revert <commit id> ``` <img src="https://i.imgur.com/INTGt4P.png" style="margin-left:20px;"> ---- ### 互動模式(ㄧ) <p style="text-align:start;font-size:33px;">當使用 cherry-pick/rebase/revert 操作 commit 順序時,有時會遇到衝突要解決,操作方式都一樣</p> <li style="text-align:start;font-weight:600;font-size:27px;padding-left:25px;list-style:none;">1. 遇到衝突顯示畫面</li> <img src="https://i.imgur.com/rebawA6.png"> ---- ### 互動模式(ㄧ) <li style="text-align:start;font-weight:600;font-size:27px;padding-left:25px;list-style:none;">2. 透過 git status 查看檔案狀態</li> <img src="https://i.imgur.com/bf7YvgU.png"> ---- ### 互動模式(ㄧ) <li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;list-style:none;">3. 解完衝突</li> ```= git add -u git cherry-pick/rebase/revert --continue ``` <li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;list-style:none;">4. 放棄操作</li> ```= git cherry-pick/rebase/revert --abort ``` ---- ### 互動模式(二) <li style="text-align:start;font-weight:600;font-size:27px;padding-left:25px;list-style:none;">1. 有時操作時會出現未修改變更的提示</li> ![](https://i.imgur.com/LtVYT2c.png) ---- ### 互動模式(二) <li style="text-align:start;font-weight:600;font-size:27px;padding-left:25px;list-style:none;">2. 使用 git status 查看狀態</li> ![](https://i.imgur.com/dsa4GWT.png) ---- ### 互動模式(二) <li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;list-style:none;">3. 允許未變更的 commit</li> ```= git commit --allow-empty ``` <li style="text-align:start;font-weight:600;font-size:27px;padding-left:50px;list-style:none;">4. 放棄操作</li> ```= git cherry-pick/rebase --abort ``` <p style="font-size:30px;text-align:start;color:red;margin-top:40px;margin-left:20px;">*不能使用 --continue 原因是未解決衝突,如果要繼續只能用 --allow-empty</p> --- ## 分支管理 ---- ### Commit tree <ul> <li style="text-align:start;font-weight:600;font-size:35px;">在 Git 裡面,每一個 commit 都是一個節點,當把這些節點一個一個串起來最後組成就是 commit tree</li> <li style="text-align:start;font-weight:600;font-size:35px;">每個節點(commit id)都是獨一無二的,都可以另外長出自己的分支</li> </ul> ```= o---o---o feature 2 / o---o---o---o---o---o---o---o---o---o---o develop \ / o---o---o---o feature 1 ``` ---- ### 何謂分支 <ul> <li style="text-align:start;font-weight:600;font-size:35px;">建立分支其實就是把 commit 貼上標籤,相反的刪除分支就是把標籤撕掉一樣</li> <li style="text-align:start;font-weight:600;font-size:35px;">就算刪除分支該 commit 也沒有因此刪除也可以透過 reflog 查看歷史紀錄救回來</li> </ul> <div style="display:flex;justify-content:center;"> <img src="https://i.imgur.com/bw3RBAW.png" height="300"> </div> ---- ### 查看/切換分支 ```= # 顯示本地端所有分支 git branch # 顯示本地端與遠端所有分支 git branch -a # 切換分支 git checkout <branch name> ``` ---- ### 建立分支 ```= # 建立分支 git branch <new branch name> # 建立分支並切換 git checkout -b <new branch name> # 指定 commit id 建立分支並切換 git checkout -b <new branch name> <commit id> # 指定遠端的分支建立並切換 git checkout -b <new branch name> origin/<branch name> ``` ---- ### 刪除分支 ```= # 刪除分支 git branch -d <branch name> # 強制刪除分支 git branch -D <branch name> # 刪除後的分支復原 git branch <new branch> <commit id> ``` <img src="https://i.imgur.com/6qwIpAY.png" height="177"> ---- ### rebase 合併分支 <li style="text-align:start;font-weight:600;font-size:35px;padding-left:50px;">重新定義分支基準點</li> ```= git checkout feature/xxx git rebase develop ``` ---- ### rebase (情況一) <li style="text-align:start;font-weight:600;font-size:35px;padding-left:20px;list-style:none;">1. 首先往上找兩個分支的交會點</li> <img src="https://i.imgur.com/PTK4JuN.png"> <li style="text-align:start;font-weight:600;font-size:35px;list-style:none;">2. 再把 develop 有 feature 沒有的 commit 接到後面</li> <div style="display:flex;justify-content:center;"> <div> <span style="font-size:30px;">一開始</span> <img src="https://i.imgur.com/VXJvilB.png" width="500" height="100"> </div> <div> <span style="font-size:30px;">結果</span> <img src="https://i.imgur.com/PSgNAEO.png" width="500" height="100"> </div> </div> ---- ### rebase (情況二) <li style="text-align:start;font-weight:600;font-size:35px;padding-left:20px;list-style:none;">1. 首先往上找兩個分支的交會點</li> ![](https://i.imgur.com/XoJdB5F.png) <ul> <li style="text-align:start;font-weight:600;font-size:35px;list-style:none;">2. 把 develop 有 feature 沒有的 commit 接到後面,<span style="color:red;">再把本身分支的 commit 接到最後面</span></li> </ul> <div style="display:flex;justify-content:center;"> <div> <span style="font-size:30px;">一開始</span> <img src="https://i.imgur.com/tt5sB2H.png" width="500" height="100"> </div> <div> <span style="font-size:30px;">結果</span> <img src="https://i.imgur.com/8Hef719.png" width="500" height="100"> </div> </div> ---- ### merge 合併分支 <li style="text-align:start;font-weight:600;font-size:35px;padding-left:50px;">根據 commit 的 timestamp 不同來進行合併動作</li> ```= # FastForward 合併不會多一個 merge commit git merge --ff <branch name> # No FastForward 合併會多一個 merge commit git merge --no-ff <branch name> ``` ---- ### merge 合併分支 <li style="text-align:start;font-weight:600;font-size:30px;padding-left:170px;">No FastForward 合併結果</li> <img src="https://i.imgur.com/SbPtxhD.png" height="450"> ---- ### 復原合併刪除後的分支 <ul style="margin-left:70px;"> <li style="text-align:start;font-weight:600;font-size:30px;">由於合併會紀錄來源分支的 commit id,還記得前幾章提到用 commit id 建立branch</li> </ul> ```= git checkout -b feature/add_disassemble_frontend 5413927 ``` ---- ### rebase 與 merge 比較 <table> <thead> <tr> <th>Merge</th> <th>Rebase</th> </tr> </thead> <tbody> <tr> <td>容易理解</td> <td>不容易理解</td> </tr> <tr> <td>當發生衝突的時候,全部在 Merge Commit 一次解完</td> <td>Rebase 的過程中,每次 commit 發生衝突都要解一次衝突</td> </tr> <tr> <td>commit tree 會有兩個 parents,不容易 trace code</td> <td>commit 路徑單純,容易 trace code,或回到指定版本</td> </tr> <tr> <td>過多的 Merge Commit 會看起來很亂</td> <td>分支乾淨一致</td> </tr> </tbody> </table> --- ## 暫存工作區 ---- ### 常用指令 ```= # 列出暫存區所有清單 git stash list # 將目前 modify 的檔案暫存,如果是 untracked 檔案需先 add git stash # 將在暫存區最新的 pop 出來 git stash pop # 指定的編號 pop 出來 git stash pop stash@{num} # 將所有暫存區清空 git stash clear # 指定的編號刪除 git stash drop stash@{num} ``` --- ## 冷知識 ---- ### HEAD 也有縮寫 <li style="text-align:start;font-weight:600;font-size:30px;padding-left:50px;">可以用 @ 來代替 HEAD</li> ```= git reset HEAD^ git reset @^ ``` ---- ### ORIG_HEAD 是什麼 <p style="font-size:35px;text-align:start;margin-left:50px;">當你在做一些比較「危險」的操作(例如像 merge、rebase 或 reset 之類的),Git 就會把 HEAD 的狀態儲存,讓你隨時可以跳回危險動作之前的狀態。</p> <li style="text-align:start;font-weight:600;font-size:30px;padding-left:50px;">當你誤刪掉某一隻commit</li> ```= git reset --hard <commit id> ``` <li style="text-align:start;font-weight:600;font-size:30px;padding-left:50px;">ORIG_HEAD 可以馬上復原</li> ```= git reset --hard ORIG_HEAD ``` ---- ### .git 資料夾是什麼 ![](https://i.imgur.com/nLffxm0.png) --- ## 情境題 ---- ### (情境一) 重新整理 commit 順序 ```= # 原來順序 P1 -> P2 -> P3 -> P4 -> P5 # 調整後的順序 P1 -> P2 -> P4 -> P5 -> P3 ``` ---- ### 解法 ```= # 先回到未改變順序的節點 git reset --hard P2 # 在用 cherry-pick 調整順序 git cherry-pick P4 P5 P3 ``` ---- ### (情境二) 將多個 commit 合併為一個 ```= # 原來的 commit tree P1 -> P2 -> P3 -> P4 # 整理後的 commit tree P1 -> P2 -> P5 ``` ---- ### 解法 ```= # 開啟 rebase 互動模式 git rebase -i P2 ``` <img src="https://i.imgur.com/tyBilhs.png" height="420"> ---- ### (情境三) 切換版本 <li style="text-align:start;font-weight:600;font-size:33px;padding-left:50px;list-style:none;">1. 切換 commit 並不會清除內容</li> ```= git checkout <commit id> ``` <img src="https://i.imgur.com/U5KwXE7.png" height="350"> ---- ### (情境三) 切換版本 <li style="text-align:start;font-weight:600;font-size:33px;padding-left:50px;list-style:none;">2. 切換 commit 並清除內容</li> ```= git reset --hard <commit id> ``` <img src="https://i.imgur.com/07slICU.png" height="80"> ---- ### (情境四) 已被 git 追蹤的檔案如何移除 ```= 1. 本地端跟遠端一起刪掉檔案 git rm <file> 2. 本地端保留,遠端刪掉檔案 git rm --cached <file> ``` ---- ### (情境五) 更新分支並合併分支 <li style="text-align:start;font-weight:600;font-size:33px;padding-left:50px;list-style:none;">1. 使用 rebase 重新定義 feature 起始節點</li> ```= git checkout feature/xxx git rebase develop ``` <li style="text-align:start;font-weight:600;font-size:33px;padding-left:50px;list-style:none;">2. 開發完要合併回 develop</li> ```= git checkout develop git rebase feature/xxx ``` --- ### 相關連結 - [連猴子都能懂的git](https://backlog.com/git-tutorial/tw/reference/config.html) - [Git book](https://zlargon.gitbooks.io/git-tutorial/content/) - [30 天精通 Git 版本控管](https://github.com/doggy8088/Learn-Git-in-30-days/blob/master/zh-tw/README.md) - [為你自己學Git](https://gitbook.tw/chapters/config/user-config.html) - [Learning Branching](https://learngitbranching.js.org/?NODEMO)
{"metaMigratedAt":"2023-06-14T19:12:37.412Z","metaMigratedFrom":"Content","title":"如何使用 Git? (本地篇)","breaks":true,"contributors":"[{\"id\":\"5344d19f-c240-4fe4-bd63-c70af739e6c4\",\"add\":32857,\"del\":17655}]"}
    1385 views