# Git【凡人修G傳之春水指法篇】<font color=#EE82EE size=3>收發自如,隨意化用</font> <!-- **[:arrow_right: 看此筆記的原始碼 :arrow_left:](/gC-As-9ESBS7WPPXyoI7Yg)** --> ## Git 基礎知識 [Git【凡人修G傳之築基篇】](https://hackmd.io/@UmEXPPDuRqO4GLkq657i-g/BJHkx00HF) --- ## 安裝環境 [<img src="https://gitforwindows.org/img/git_logo.png" width=40>](https://gitforwindows.org/) [Git Bash](https://gitforwindows.org/) - Git CLI (命令視窗) [<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/88/TortoiseGit_logo.svg/70px-TortoiseGit_logo.svg.png" width=40>](https://tortoisegit.org/download/) [TortoiseGit](https://tortoisegit.org/download/) - 主要用於顯示檔案狀態 --- ## Bash CMD <font color=#FF4040 size=3>(Basic)</font> ``` cd <dirctory> // change directory ls // list directory cd.. // go back last directory up arrow // last cmd tab // help to quick search name of directory or file clear // clear your terminal screen exit // exit your terminal history // list all of cmd history histroy <num> // list num of newest cmd ``` --- ## Git 一般版控 <font color=#FF4040 size=3>(SOP)</font> #### Git 一般SOP【方法一】 ###### 方法一. 先【Stash 隱藏目前工作】再【更新 + 合併】, 最後【Add + Commit】 + 【同步上傳】: ###### <font color=#FF0000>※ 功能製作一半時,但是遠端已有新項目更新需要搭配使用,則選擇【方法一】。</font> ``` // 檢查各檔案的狀態 (於根目錄下) // . 指當前目錄下所有檔案 > git status . // 隱藏全部檔案 // 可以在執行一次 git status . 確認一下工作區是否清空了 // 注意!!! 如果有 Untracked Files 將不列入隱藏區 // 如果需要則可以帶入參數 --include-untracked 或 -u (git stash -u) > git stash // 確定工作區清空後,就可以拉取遠端更新地端 (自行決定是否使用 rebase) > git pull origin master --rebase // 從遠端更新完成後,取出隱藏區的檔案 (可能會有衝突) // 如有衝突則...手動修復合併... > git stash pop NOTE: 至於後續從隱藏區取出的上次未完成的檔案,是否要直接提交則自行判斷, 如果沒有,則做到這邊就好,相反的如果有要直接更新至遠端,就接續以下。 ``` ``` // 將更改過的檔案全部加入暫存區 (可以自行挑選要 add 的檔案) // . 指當前目錄下所有檔案 > git add . // 註解說明提交的檔案 (完成 devBranch 的 commit 後,如沒挑檔則會清空工作區) > git commit -m "Your Commit" // 以下指令為更新至遠端 (自行判斷是否需要更新至遠端) > git push origin master ``` #### Git 一般SOP【方法二】 ###### 方法二. 先【Add + Commit】再【更新 + 合併】, 最後【Add + Commit】 + 【同步上傳】: ###### <font color=#FF0000>※ 無例外的話,確定功能都完成了,單純進行合併並且更新至遠端,則選擇【方法二】。</font> ``` // 檢查各檔案的狀態 (於根目錄下) // . 指當前目錄下所有檔案 > git status . // 將更改過的檔案全部加入暫存區 (可以自行挑選要 add 的檔案) // . 指當前目錄下所有檔案 > git add . // 註解說明提交的檔案 (完成 commit 後,如沒挑檔則會清空工作區) > git commit "Your Commit" // 完成 commit 後,拉取遠端更新地端 (自行決定是否使用 rebase) // 如有衝突則...手動修復合併... > git pull origin master --rebase // 將已完成修復後的衝突檔案進行 add // . 指當前目錄下所有檔案 > git add . // add 後,繼續合併 (以此類推直至完成) > git rebase --continue // 完成合併後,會自動開啟 vim,如需要則可以在原有的 commit 添加說明...無的話直接 => :q! // 以下指令為更新至遠端 (自行判斷是否需要更新至遠端) > git push origin master ``` ## Git 分支版控 <font color=#FF4040 size=3>(SOP)</font> #### Git 分支合併SOP ###### 將完成功能的 `devBranch` 分支合併至 `master` 中,以下: [:book:] 如要取消 rebase => `git rebase --abort`。 ``` // 先確保工作區為清空狀態,如果沒問題就切換分支開始製作 // 開始新增功能修改等等...完成後開始準備提交 > git checkout devBranch // 將更改過的檔案或新增的檔案全部加入暫存區 (可以自行挑選要 add 的檔案) // . 指當前目錄下所有檔案 > git add . // 註解說明提交的檔案 (完成 devBranch 的 commit 後,如沒挑檔則會清空工作區) > git commit "Your Commit" // 切換至 master 分支,先將地端的 master 更新至最新 (自行決定是否使用 rebase) > git pull origin master --rebase // 完成 master 更新後,在切換至 devBranch > git checkout devBranch // 將最新的 master 合併至 devBranch (主要是為了避免直接在 master 進行衝突合併,可以自行判斷) // 如有衝突則...修復手動合併... > git rebase master // 將已完成修復後的衝突檔案進行 add // . 指當前目錄下所有檔案 > git add . // add 後,繼續合併 (以此類推直至完成) > git rebase --continue // 完成合併後,會自動開啟 vim,如需要則可以在原有的 commit 添加說明...無的話直接 => :q! // 此時 devBranch 已經完成更新與合併,最後切換至 master 分支 > git checkout master // 此時在 master 分支,開始將 devBranch 合併至 master (剛剛已經有將 master 合併至 devBranch 了,所以基本上是無痛合併) > git merge devBranch // 將 devBranch 合併至 master 完成後... // 以下指令為更新至遠端 (自行判斷是否需要更新至遠端) > git push origin master ``` ##### 以上如果<font color=#FF006F>分支整併完畢後</font>,依照需求如要刪除已合併分支則刪除,如果<font color=#FF006F>沒有要刪除 `devBranch` 分支,但是要更新的話</font>,以下。 ``` > git push origin devBranch // 進行該分支的遠端更新 ``` --- ## Git 克隆/複製品 <font color=#FF4040 size=3>(Clone)</font> #### Git Clone 預設 [:book:] `git clone <remote_repo>`。 ``` git clone https://github.com/michaelo/happy_git.git ``` #### Git 指定特定分支進行 Clone [:book:] `git clone -b <branch> <remote_repo>`。 ``` git clone -b dev https://github.com/michaelo/happy_git.git ``` ## Git 狀態 <font color=#FF4040 size=3>(Status)</font> [:book:] `git status <path>`。 ``` git status . ``` ## Git 隱藏區 <font color=#FF4040 size=3>(Stash)</font> #### Git 存入隱藏區 ``` git stash ``` #### Git 從隱藏區的柱列取出最近隱藏的內容 ``` git stash pop ``` #### Git 隱藏區列表 ``` git stash list ``` ## Git 暫存區 <font color=#FF4040 size=3>(Add Stage)</font> #### Git 存入暫存區 [:book:] `git add .` 表示此目錄包含子目錄全部存入,無指定。 ``` git add ./myFolder/my_file.txt git add . ``` #### Git 從暫存區撤銷 [:book:] `git reset` 表示全部撤銷,無指定。 ``` git reset my_file.txt git reset ``` ## Git 提交 <font color=#FF4040 size=3>(Commit)</font> #### Git 提交 [:book:] `-m` 允許多個註解。 ``` git commit -m "Your Commit" git commit -m "1. First Commit" -m "2. Second Commit" ``` #### Git 後悔提交 <font color=#FF006F size=2>(想要拆掉重做)</font> ``` > git log --oneline // 顯示如下 ``` ``` e12d8ef (HEAD -> master) this is my commit 2 661080b this is my commit 1 ``` ``` > git reset HEAD^ // 從以上可以看到剛剛提交的 HEAD 在 e12d8ef, 但是我要退回去一個 661080b 表示拆掉或撤銷 (e12d8ef) ``` #### Git 修改提交註解 [:book:] `--amend` 表示修正、修改。 ``` git commit --amend -m "Your Commit" // 如果當下已經提交至遠端, 先確認你目前 commit 為最新的並且 amend 後, 可執行以下強制更新遠端 // ※備註: -f 不要亂用, 必要確認你當前 commit 為最新 (否則遠端會被覆蓋) git push origin master -f ``` ## Git 重置 <font color=#FF4040 size=3>(Reset => For Stage: Unstage/Cancel, For Commit: GoTo/GoBack)</font> #### Git 查看 Commit 狀況 ``` git log --oneline ``` #### Git 退回 Commit <font color=#FF006F size=2>(需先執行 log 查看, 才能知道退回至哪一個)</font> [:book:] `^` 表示「前一次」的意思,所以每一個 `^` 符號就會退回1次。 [:book:] `~` 表示「輸入要退回次數 <font color=#0000FF size=3>(預設無輸入則1)</font>」的意思,所以`~3`就是退回3次。 ``` git reset HEAD^ git reset HEAD~1 ``` [:book:] `git reset <commit id>`預設為`--mixed`。 - `--mixed (default)` => 保留修改檔案(指的是工作區中的檔案),並且撤銷全部暫存區檔案。<font color=#FF006F size=2>(未追蹤的新檔案不納入)</font> - `--soft` => 保留修改檔案(指的是工作區中的檔案),並且不會撤銷暫存區檔案。<font color=#FF006F size=2>(未追蹤的新檔案不納入)</font> - `--hard` => 不保留修改檔案(指的是工作區中的檔案),強制還原至指定 commit 有的檔案。<font color=#FF006F size=2>(未追蹤的新檔案不納入)</font> ``` git reset HEAD^^ --soft git reset HEAD~2 --hard ``` ## Git 遠端 <font color=#FF4040 size=3>(Remote => origin)</font> #### Git 新增遠端 [:book:] `git remote add <name> <url>`。 ``` git remote add origin https://github.com/michaelo/happy_git.git ``` #### Git 更名遠端 [:book:] `git remane <old name>:<new name>`。 ``` git remote rename origin gitnet ``` #### Git 移除遠端 <font color=#FF006F size=2>(如果有遷移伺服器, 或是本地端不在追蹤此遠端)</font> ``` git remote rm origin ``` #### Git 查看遠端 [:book:] `-v`表示 verbose <font color=#0000FF size=3>(冗長意思, 白話一點就是"詳細資訊")</font>。 ``` git remote -v git remote ``` #### Git 綁定預設遠端分支 <font color=#FF006F size=2>(於第一次的 commit 設置)</font> [:book:] `git push -u <remote> <branch>`。 [:book:] `-u` 表示 upstream。 ``` git push -u origin master ``` ## Git 引用日誌 <font color=#FF4040 size=3>(Reference log)</font> #### Git 查看引用日誌 <font color=#FF006F size=2>(主要可用於 reset )</font> [:book:] `reflog` 表示reference log。 [:book:] 多筆情況下可以使用`Enter`顯示下一筆,反之輸入`q`則離開。 ``` git reflog // 顯示如下 ``` ``` a68395d HEAD@{0}: reset: moving to HEAD^^ 69b4a9f HEAD@{1}: reset: moving to HEAD^ : ``` #### Git 使用 reflog 退回或還原 [:book:] 可搭配`--mixed`, `--soft`, `--hard`。 ``` git reset HEAD@{1} ``` ## Git 分支 <font color=#FF4040 size=3>(Branch)</font> #### Git 查看分支 [:book:] `-a`表示全部清單。 ``` git branch -a git branch ``` #### Git 建立分支 [:book:] `git branch <branch name>`, 指令如下。 ``` git branch devBranch ``` #### Git 遠端直接建立分支 [:book:] `master:devBranch` 意思就是在遠端上建立有`master`內容的`devBranch`新分支。 ``` git push origin master:devBranch ``` #### Git 獲取遠端分支 ``` // git fetch <遠端主機名> <遠端分支名稱>:<本地分支名稱> git fetch origin master:myBranch ``` #### Git 切換分支 ``` git checkout devBranch ``` #### Git 刪除分支 [:book:] `-d` 一般刪除,`-D` 強制刪除。 ``` git branch -d devBranch git branch -D devBranch ``` #### Git 刪除遠端分支 [:book:] `:` 表示將空的內容推上至分支,即為刪除。 ``` git push origin :devBranch ``` ## Git 反追蹤 <font color=#FF4040 size=3>(Untrack)</font> #### Git 移除追蹤特定【檔案】 [:book:] `git rm --cached <file name>。`, 指令如下 [:book:] `--cached` 表示快取 <font color=#0000FF size=3>(白話一點就是 Git 追蹤區)</font>。 ``` git rm --cached ./my_file.txt ``` #### Git 移除追蹤特定【資料夾】 [:book:] `git rm --cached <directory>。`, 指令如下 [:book:] `-r` 表示recursive files <font color=#0000FF size=3>(白話一點就是遞迴該資料夾中的所有檔案)</font>。 ``` git rm --cached -r myFolder/ ``` #### Git 反追蹤後, 並且更新遠端 ``` // git add <your file> > git add ./my_file.txt > git commit -m "Untrack File" // git push <remote> <branch> > git push origin master ``` --- ## Git 切割樹 <font color=#FF4040 size=3>(Subtree)</font> #### Git Subtree SOP <font color=#FF0F8F size= 3>(subtree 是基於分支作為切割管理)</font> ``` // 同步更新 > git add . > git commit -m "" > git push origin master > git subtree split --prefix=Path --branch upm_Test > git push origin upm_Test // 新Tag > git add . > git commit -m "" > git push origin master > git subtree split --prefix=Path --branch upm_Test > git tag v1.0.0_Test upm_Test > git push origin upm_Test v1.0.0_Test // tag only ``` --- ## Git 子模組 <font color=#FF4040 size=3>(Submodule)</font> #### Git Submodule SOP <font color=#FF0F8F size= 3>(submodule 是基於 Repository 作為子管理)</font> ``` // [Add] 原主專案已有 git, 但是要新增已存在的文件作為 submodule // 步驟1. (先將 submodule 清空並且 git 到另一個 repository) > cd submodule_dir > git rm --cached . -r > git init (in submodule_dir) > git add remote add origin <submodule.git> > git add . > git commit -m "your submodule commit" > git push -u origin <branch> (submodule) // 步驟 2. > cd main_dir > rm submodule_dir -rf (請先確保 submodule 已經 push 到 git 的 repository) > git submodule add <remote.submodule.git> <local.submodule.clone.path> > git add . > git commit -m "add new submodule" > git push origin master (main) // [Add] 原專案 + submodule 都尚未 git // 步驟1. 建立 submodule git > cd submodule_dir > git init (in submodule_dir) > git add remote add origin <submodule.git> > git add . > git commit -m "your submodule commit" > git push -u origin <branch> (submodule) // 步驟 2. > cd main_dir > rm submodule_dir -rf (請先確保 submodule 已經 push 到 git 的 repository) > git init (in main_dir) > git add remote add origin <main.git> > git add . > git commit -m "your main commit" > git push -u origin <branch> (submodule) > git submodule add <remote.submodule.git> <local.submodule.clone.path> > git add . > git commit -m "add new submodule" > git push origin master (main) // [Push] 主專案 + Submodules (會 push 兩次: 一次 = submodule, 另一次 = main) > cd submodule_dir > git add . (in submodule_dir) > git commit -m "modified submodule" > git push origin master (submodule) > cd main_dir > git add . > git commit -m "update submodule into main" > git push origin master (main) --- // [Update] 主專案 + Submodules > git pull --recurse-submodules origin <branch> --rebase or > git pull --recurse-submodules origin <branch> --merge --- // [Update] 主專案 > git pull origin <branch> --rebase or > git pull origin <branch> --merge // [Update] Submodules > git submodule update --remote --rebase or > git submodule update --remote --merge --- // [Clone] 主專案 + Submodules > git clone <main.git> > git submodule update --init --recursive or > git clone --recursive <main.git> ```