# Git 學習筆記 ###### tags: `Git` - [Git 學習筆記](#git-學習筆記) - [tags: `Git`](#tags-git) - [常用指令](#常用指令) - [Git 常用指令](#git-常用指令) - [Git Bash 常用指令](#git-bash-常用指令) - [Git Flow 開發流程觀念](#git-flow-開發流程觀念) - [分支介紹](#分支介紹) - [長期分支](#長期分支) - [任務分支(Topic)](#任務分支topic) - [Git Commit 規範](#git-commit-規範) - [Commit Message 格式](#commit-message-格式) - [Header Type](#header-type) - [Body](#body) - [Footer](#footer) - [commit 模板](#commit-模板) - [Git 操作情境](#git-操作情境) - [取消 commit:git reset](#取消-commitgit-reset) - [確認 git 紀錄](#確認-git-紀錄) - [利用相對位置取消 commit](#利用相對位置取消-commit) - [利用絕對位置取消 commit](#利用絕對位置取消-commit) - [git commit 打錯字](#git-commit-打錯字) - [轉移資料庫:git mirror](#轉移資料庫git-mirror) - [將未完成的工作暫存:git stash](#將未完成的工作暫存git-stash) - [將現階段工作暫存](#將現階段工作暫存) - [取出暫存](#取出暫存) - [解決合併衝突](#解決合併衝突) - [更改 git remote 位置](#更改-git-remote-位置) - [取消 mrege (清除合併紀錄)](#取消-mrege-清除合併紀錄) - [新增遠端儲存庫](#新增遠端儲存庫) - [Git 管理](#git-管理) - [使用 VSCode 管理 Git](#使用-vscode-管理-git) - [GitHub 操作](#github-操作) - [將本地專案上傳到 github](#將本地專案上傳到-github) - [Https 設定 Token](#https-設定-token) - [設定 personal access token](#設定-personal-access-token) - [設定 SSH](#設定-ssh) - [輸入指令產生 SHH](#輸入指令產生-shh) - [產生 SSH 連線所需的公鑰內容](#產生-ssh-連線所需的公鑰內容) - [上傳公鑰](#上傳公鑰) ## 常用指令 ### Git 常用指令 - `git init` 將目前的目錄初始化為 Git 目錄, 建立本地儲存庫 - `git config` 設定或檢視 Git 設定檔資訊 - `git add` 將檔案加入 Git 暫存區 - `git rm` 將檔案移出 Git 暫存區 - `git status` 顯示 Git 狀態 - `git commit` 將暫存區的檔案提交至儲存庫納入版本控制 - `git log` 顯示過去歷次的版本異動 - `git reflog` 顯示完整的版本異動歷史紀錄 - `git show` 顯示指定版本的異動內容 - `git branch` 建立一個新分支 (branch) - `git checkout` 取出分支內容還原為工作目錄 - `git merge` 合併分支 - `git reset` 重設某一版本 - `git clone` 從遠端儲存庫 (GitHub 或 Bitbucket) 複製副本至本地儲存庫 - `git push` 將本地儲存庫內容推送到遠端儲存庫 - `git pull` 將遠端儲存庫拉回合併更新到本地儲存庫 ### Git Bash 常用指令 | Linux | Windows | 說明 | | ---------------- | --------------------- | -------------------------------- | | `pwd` | `cd` | 顯示幕前目錄 | | `ls -al` | `dir` | 顯示目前目錄下的檔案與子目錄列表 | | `mkdir tmp` | `md tmp` | 建立子目錄 tmp | | `rm -r tmp` | `rd tmp` | 刪除子目錄 tmp | | `cd tmp` | `cd tmp` | 切換至子目錄 tmp | | `cd ..` | `cd ..` | 切換至上一層目錄 | | `touch test.txt` | `copy nul > test.txt` | 建立空白文字檔案 | | `cat file/more` | `type file` | 顯示檔案內容 | | `rm file` | `del file` | 刪除檔案 file | | `mv file1 file2` | `ren file1 file2` | 將檔案 file1 更名為 file2 | | `cp file1 file2` | `copy file1 file2` | 複製檔案 file1 為 file2 | | `date` | `date` | 顯示日期 (Linux 含時間) | | `clear` | `cls` | 清除螢幕 | ## Git Flow 開發流程觀念 > [參考資料:Git Flow 是什麼?為什麼需要這種東西?](https://gitbook.tw/chapters/gitflow/why-need-git-flow) > > [參考資料:Git flow 分支策略](https://git-tutorial.readthedocs.io/zh/latest/branchingmodel.html) ### 分支介紹 #### 長期分支 - **main**(原為 master, 於 2020/10 變更) 主要為穩定,上線的版本。不該允許開發者直接 commit 到此分支。 一般在專案初期,環境建置好就會拉 develop 分支出去,以維持 main 獨立性。 - **develop** 所有開發分支的基礎,當新增/修改功能時,會從此分支切出去,完成後再合併回來。 #### 任務分支(Topic) - **hotfix** 上線版本需緊急修復時,由 main 直接切出的 hotfix 分支,修復完成也會合併至 main 分支。 由於 develop 在開發中,若從 develop 切 hotfix 分支,再合併至 main 分支時可能會出現更嚴重的問題。 - **feature** 開發新功能時,會從 develop 切出 feature 分支,其命名方式採`feature/功能名稱`。 - **release** 由 develop 切出來,正式上線前的最終測試分支,通過後會將 release 合併到 main 以及 develop 確保在 release 時修正的一些問題能同步到 main 與 develop。 ### Git Commit 規範 > [Git Commit Message 這樣寫會更好,替專案引入規範與範例](https://ithelp.ithome.com.tw/articles/10228738) #### Commit Message 格式 ```bash # 標題: <type>(<scope>): <subject> # - type: feat, fix, docs, style, refactor, test, chore # - scope: 如果修改範圍為全局修改或難以分配給單個組件,可略 # - subject: 以動詞開頭的簡短描述 # # 正文: 內文需包含: # * 程式碼更訂的原因(問題、原因、需求) # * 調整項目 # * 與先前行為的對比 # # 結尾: # - 任務編號(如果有) # - 重大變化(紀錄不兼容的更動), # 以 BREAKING CHANGE: 開頭,後面是對變動的描述、以及變動原因和遷移方法。 # ``` #### Header Type - **feat** - 新增/修改功能 (Feature) - **fix** - 修正 Bug (bug fix) - **docs** - 修改/新增文件 (documentation) - **style** - 修改程式碼格式或風格,不影響原有運作,例如 ESLint (formatting, missing semi colons, …) - **refactor** - 重構 or 優化,不屬於 bug 也不屬於新增功能等 - **test** - 增加測試功能 (when adding missing tests) - **chore** - 增加或修改第三方套件(輔助工具)等 (maintain) - **perf** - 改善效能 (A code change that improves performance) - **revert** - 撤銷回覆先前的 commit 例如:revert: type(scope): subject (回覆版本:xxxx)。 #### Body 可選的。與 subject 一樣,使用命令式現在時態, 如 change,而不是 changed 或 changes。 body 應包括改變的動機,並將其與以前的行為進行對比。也就是說,描述為什麼修改,做了什麼樣的修改,以及開發的思路等,是 commit 的詳細描述。 #### Footer Breaking Changes 應以單詞 BREAKING CHANGE 開頭:用空格或兩個換行符。後面是對變動的描述和變動的理由。 ```bash BREAKING CHANGE: isolate scope bindings definition has changed. To migrate the code follow the example below: Before: scope: { myAttr: 'attribute', } After: scope: { myAttr: '@', } "The removed `inject` wasn't generaly useful for directives so there should be no code using it." ``` 如果當前 commit 還原了先前的 commit,則應以 revert:開頭,後跟還原的 commit 的 header。在 body 中必須寫成:This reverts commit \<hash>。其中 hash 是要還原的 commit 的 SHA 標識。 ```bash revert: feat(pencil): add 'delete' option This reverts commit 667ecc1654a317a13331b17617d973392f415f02. ``` #### commit 模板 在~/.gitconfig 新增 ``` [commit] template = ~/.gitmessage ``` 新建 ~/.gitmessage ``` # 標題: <type>(<scope>): <subject> # - type: feat, fix, docs, style, refactor, test, chore # - scope: 如果修改範圍為全局修改或難以分配給單個組件,可略 # - subject: 以動詞開頭的簡短描述 # # 正文: 內文需包含: # * 程式碼更訂的原因(問題、原因、需求) # * 調整項目 # * 與先前行為的對比 # # 結尾: # - 任務編號(如果有) # - 重大變化(紀錄不兼容的更動), # 以 BREAKING CHANGE: 開頭,後面是對變動的描述、以及變動原因和遷移方法。 # ``` ## Git 操作情境 ### 取消 commit:git reset Git 的 `reset`指令,比較像是「前往」或是「變成」,並不會真的重新設定。 `reset`後的東西都還可以撿的回來。 #### 確認 git 紀錄 ```terminal git log --oneline af75a42 (HEAD -> develop) 0327 1baa403 (origin/develop) no message 13fd2dc 0223 a640c49 0222新增 e09ecae init commit ``` #### 利用相對位置取消 commit ```terminal git reset af75a42^ ``` `^`符號表示「前一次」的意思,`af75a42^`是指`af75a42`這個 commit 的「前一次」,`af75a42^^`則是往前兩次,以此類推。 如果要倒退五次可以寫成`af75a42~5`。 另外`HEAD`和`develop`也都指向`af75a42`這個 commit,所以也可以寫成 ```terminal git reset develop^ & git reset HEAD^ ``` #### 利用絕對位置取消 commit ```terminal git reset 1baa403 ``` 他會切會到`1baa403`這個 commit,剛好是`af75a42`的前一個 commit,和取消最後一次 commit 的效果一樣。 ### git commit 打錯字 先把前一次 add 的內容,保留在 changes to be committed 區域 ```terminal git reset --soft HEAD^ ``` 接著再重新進行一次 git commit 即可 ### 轉移資料庫:git mirror 可以轉移整個 repository 的資訊,包括 beanch, tags 將 repo clone --mirror 到本地 ```bash git clone --mirror gitolite@git.lab317.org:dinos80152/Authentication ``` 接著在 github 建立新的 repository 進到專案資料夾,設定新的遠端 git repo 位置 ```bash cd your_project.git/ git remote set-url --push origin https://github.com/your_name/your_project.git ``` local 更新 remote branch ,最後將整包 push 上去 ```bash git push --mirror ``` 或者一個指令直接指向遠端 repo ```bash git push --mirror https://github.com/your_name/your_project.git ``` ### 將未完成的工作暫存:git stash 有時候會有工作做到一半,需要切換到別的分支進行其他任務。 這時有兩種辦法: 1. 先 `commit` 目前進度,之後再 `reset`,將做一半的東西拆回來繼續做。 2. 使用 `stash`。 先看一下目前的狀態: ```bash git status On branch feature/admin_controller Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: app/Http/Controllers/RegionController.php modified: app/Models/Room.php modified: app/Models/User.php no changes added to commit (use "git add" and/or "git commit -a") ``` #### 將現階段工作暫存 目前正在修改 `app/Http/Controllers/RegionController.php` `app/Models/Room.php` `app/Models/User.php`,使用 `git stash` 把他們存起來。 ```bash git stash Saved working directory and index state WIP on feature/admin_controller: c745ccb style(MemberController): 修改response的資料與取消註解 ``` > **注意** > > Untracked 狀態的檔案無法被 stash,需要額外使用 `-u` 參數 看一下目前的狀態 ```bash git status On branch cat nothing to commit, working tree clean ``` `git stash list` 可以查看暫存檔案 ```bash git stash list stash@{0}: WIP on cat: b174a5a add cat 2 ``` #### 取出暫存 當任務完成,要把剛剛暫存的東西拿回來 ```bash git stash pop stash@{0} On branch feature/add_new_api_route Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: app/Http/Controllers/RegionController.php modified: app/Models/Room.php modified: app/Models/User.php no changes added to commit (use "git add" and/or "git commit -a") Dropped stash@{0} (8810ecbe89e1c1412c0c47d7fb7ded9f3e29aa53) ``` 使用 `pop` 指令,可以將某個 `stash` 拿出來並套到目前的分支上。套用成功之後,套用過的 `stash` 就會被刪除。 如果沒有指定 `pop` 哪一個 `stash`,將會從編號小的也就是 `stash@{0}` 開始使用,也就是最後存進來的。 要刪除 `stash` 可以用 `drop` 指令 ```bash git stash drop stash@{0} Dropped stash@{0} (87390c02bbfc8cf7a38fb42f6f3a357e51ce6cd1) ``` 如果要把 `stash` 撿回來,但不想刪除,可以使用 `apply` ```bash git stash apply stash@{0} ``` ### 解決合併衝突 當在不同分支中,修改同一檔案的不同行,此時合併不會發生問題。 倘若修改的是同一行,就會發生合併衝突。 ```bash git merge feature/create_device_model Auto-merging app-src/app/Http/Controllers/UserController.php CONFLICT (content): Merge conflict in app-src/app/Http/Controllers/UserController.php Auto-merging app-src/app/Models/Room.php Auto-merging app-src/app/Models/User.php CONFLICT (content): Merge conflict in app-src/app/Models/User.php Automatic merge failed; fix conflicts and then commit the result. ``` 有出現 CONFLICT (content)提示的檔案,為發生合併衝突的檔案。 此時在檔案中,Git 會將衝突位置標示出來。 ```php <<<<<<< HEAD 當前內容。 ======= 要合併的目標分支上歧異的內容。 >>>>>>> featuer/i_am_old_branch ``` 修正衝突點後,將修改的檔案暫存,最後進行提交。 ```bash git add --all git commit ``` ### 更改 git remote 位置 當修改 git repo 的名稱或是路徑時,若要在本機進行 push 或是 pull 的指令時,會出現:remote: This repository moved. Please use the new location [new location] - 解決辦法:重新設定 remote url ```bash git remote set-url origin https://XXX.git ``` 檢查 remote url 是否修改成功 ```bash git remote -v ``` ### 取消 mrege (清除合併紀錄) > [Git 實戰技巧 - 取消合併](https://blog.darkthread.net/blog/git-undo-merge/) 當 feature 與 develop 分支的合併位置有誤,想要拆掉重做 ```bash db7915e (HEAD -> dev, feature/mqtt_test) feat: 測試mqtt連線 b65d2d2 (tag: release_v2.0.0, origin/dev) no message 0a198be refactor(firmware index page): 優化firmware前端頁面 539942f (origin/master, origin/HEAD, master) Merge branch 'feature/fix_firmware_download' into dev 1a4515a fix(firmwareController): 修復firmware下載問題 d1e204f docs(README): 修改上線環境設定 ``` `git rebase -i` :重整目標 commit 之後的 commit:重整清單中不會有下指令的 commit 而是顯示其後所有的 commit。 ```bash git rebase -i 0a198be ``` 輸入指令之後會進入編輯器 ```vim pick db7915e feat: 測試mqtt連線 pick b65d2d2 no message # Rebase 539942f..879c462 onto 539942f (3 commands) # # Commands: # p, pick <commit> = use commit # r, reword <commit> = use commit, but edit the commit message # e, edit <commit> = use commit, but stop for amending # s, squash <commit> = use commit, but meld into previous commit # f, fixup [-C | -c] <commit> = like "squash" but keep only the previous # commit's log message, unless -C is used, in which case # keep only this commit's message; -c is same as -C but # opens the editor ``` 並將要取消的 commit 改為 drop ```vim drip db7915e feat: 測試mqtt連線 pick b65d2d2 no message ``` ```bash b65d2d2 (HEAD -> dev, tag: release_v2.0.0, origin/dev) no message 0a198be refactor(firmware index page): 優化firmware前端頁面 539942f (origin/master, origin/HEAD, master) Merge branch 'feature/fix_firmware_download' into dev 1a4515a fix(firmwareController): 修復firmware下載問題 ``` ### 新增遠端儲存庫 ```bash git init git add . git commit -m "First commit" ``` 添加遠端儲存庫的路徑 ```bash ## git remote add origin "remote repository URL" git remote add origin //fishbone/研發部/軟體區/GitServer/V5/*.git ``` 將遠端儲存庫初始化 ```bash ## git init --bare "remote repository URL" git init --bare //fishbone/研發部/韌體區/GitServer/V5/*.git ``` 將本地儲存庫內容推送到遠端 ```bash git push --set-upstream origin main ``` ### Git 別名 修改 `~/.gitconfig` ```vim [alias] st = status ptlg = log --color --graph --pretty=format:'%C(yellow)%h%Creset %C(bold brightred)%d%Creset %C()%s%Creset \n %C(blue italic dim)-- %an%Creset %C(green italic dim)(%cr)%Creset' adal = add --all ``` ## Git 管理 ### 使用 VSCode 管理 Git > [Visual Studio Code 無需輸入 Git 指令,透過界面按鈕就可輕鬆管理 Github 中的專案檔案](https://www.minwt.com/webdesign-dev/22926.html) ## GitHub 操作 ### 將本地專案上傳到 github 1. git init 2. git add . 3. git commit -m "init commit" 4. git remote add origin `https://github.com/<username>/<repo>.git` 5. git push -u origin master ### Https 設定 Token 當使用推送,輸入 github 密碼會出現錯誤。 ```terminal changgenglu@masenyuandeMacBook-Air ~ % git push -u origin master remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead. remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information. fatal: unable to access 'https://github.com/changgenglu/your_project.git/': The requested URL returned error: 403 ``` 大致意思是,密碼驗證於 2021 年 8 月 13 日不再支援,也就是今天不能再用密碼方式去提交程式碼。請用使用 **personal access token** 替代。 #### 設定 personal access token - 開啟 GitHub.com -> Setting -> Developer settings -> Personal access tokens - 按下`Generate new token` - Note 欄位填入 token 的備註 - Expiration 設定 token 的時效 - Select scopes 設定權限(基本全部開啟) - 按下`Generate token` - 複製 token 代碼 再次使用終端機推送 ```terminal git push -u origin master ``` 輸入 github 密碼的地方,貼上 token 代碼 ### 設定 SSH #### 輸入指令產生 SHH ```terminal ssh-keygen ``` 產生 ```terminal $ Enter file in which to save the key (/Users/changgenglu/.ssh/id_rsa): # 這行只是確定存在哪 $ Overwrite (y/n)? # 如果原本就有金鑰會跳出此問題,覆蓋嗎? (是) $ Enter passphrase (empty for no passphrase): $ Enter same passphrase again: # 輸入密碼,再次確認輸入密碼 ``` 此處的輸入密碼為使用至個金鑰的密碼,可以選擇不輸入。 #### 產生 SSH 連線所需的公鑰內容 ```terminal cat ~/.ssh/id_rsa.pub ``` 輸出實例 ```terminal ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDFp+A3qe4qm1Dkw66LN/vNGlufX5iC9VERfuUiXHNM5L3hQuz6wO8WuzFv+zDIHRPGUl616oLXTHTqommuO0GZavDo+lbUIRkSBM9j/9tr+hlF4LPTT4ggjOgzLCHTrSyzcmcdykgBfnDgX3aYfZbhCEcWdERUxWFNnDf+YYlNd8L6LMKSIce61nhqiSLNbugDCrE0IH+/1hoS3LNoag9V05Qwo5yZ6srLNJT8uISoqvJv5BwSpBL9ImnePx+LzDiVXlJMisKf1GSXdVuWmVWlKrZOsadk4ZkSNH2cL1wgkNvAUbydWKG9Ag4TfI/khKwUXyhT+7V4jWsJusDXZxafylZma4qeOsaLAN4ScSStnOoSm1CxeNqmPsQpAGbtvx49yB2+c4HFsa68VzcwV1oejhh2E67iqqKK53IFN/qQmYYfhUukY6rgLLHlLkmjLqdVpVcULCP0mMzn4xacFWLwDgOtZK1i97vWaLPyG6hYQQ108zK9i/Cg13p0Z+CUTCs= changgenglu@masenyuandeMacBook-Air.local ``` #### 上傳公鑰 到 Github > Settings > SSH and GPG keys 的設定頁面,選擇 New SSH Key。