--- title: 'Git 學習紀錄' disqus: hackmd --- ## Git基礎 ### Git指令練習網站 * `https://github.com/pcottle/learnGitBranching` ### VsCode 安裝Git相關套件 * Git Graph * Git History * GitLens — Git supercharged ### 常用git log * git log --oneline --graph * --oneline : 是 --pretty=oneline --abbrev-commit 的簡短用法 * --graph : 以 ASCII 在 log 輸出旁邊畫出分支的分歧及合併。 * git log 參數參考網址:http://jamestw.logdown.com/posts/238719-advanced-git-log --- ### 修改最近一次的commit紀錄 * git commit \-\-amend -m "XXXXX" * git commit \-\-amend * 沒有加上 **-m** 會跳出一個視窗來編輯訊息(vim編輯器) --- ### 追加調整內容( test.txt ) 到最近一次的commit 1. git add test.txt 2. git commit --amend --no-edit * --no-edit:不編輯commit訊息 3. git push -f :強制覆蓋遠端分支 --- ### git 復原檔案 * git checkout xxx 復原該份檔案 * git checkout . 復原所有檔案 * 概念:拿暫存區的檔案,覆蓋至工作區 * git checkout HEAD~2 xxx * 拿兩個版本前的檔案,覆蓋至當前工作目錄,同時修改暫存區的內容 --- ### 將commit 拆掉重做 * git **reset e12d8ef^** * 表示指向**e12d8ef**這個commit的前一次 * git reset **HEAD^** \ git reset **master^** * **指定退回哪個commit** : git reset f55f49d * --mixed` 模式(`預設`),將目前commit至f55f49d中所修改的東西,全部退至**工作目錄** --- ### Commit拆出來的檔案 * `--mixed` 模式(`預設`): * 丟回**工作目錄** * 暫存區的檔案丟掉,但不會動工作目錄的檔案,Commit拆出來的檔案會留在工作目錄,但不會留在暫存區 * `--soft` 模式: * 工作目錄、暫存區都不會被丟掉,只有HEAD移動 * Commit拆出來的檔案會直接放在**暫存區** * `--hard` 模式: * 將工作目錄、暫存區都丟棄,直接退回該commit時的狀態 --- ### 救回 hard模式下,reset的某個commit 1. git log -g \ git reflog : 查看紀錄 2. 找到該commit的SHA-1碼 3. git reset --hard 0929fe2 #指向0929fe2,同時會復原該0929fe2 之前所做的commit紀錄 --- ### HARD 是什麼東西? * 是一個指標,指向某一個分支 * 通常把HEAD當作「目前所在分支」 --- ### 只Commit 一個檔案的部分內容 * git add -p : * 選y:將整個檔案已修改的部分全部加入到暫存區 * 選e:只想送出部分修改到暫存區,會開啟一個編輯器 * 將不想加入的部分,刪除後,離開即可 --- ### 當前用戶信息 * 基礎指令:git **config** * 顯示當前使用的**所有 Git 配置**:git config --list * **當前倉庫**配置:git config **--local** --list * **全局**配置:git config **--global** --list * **系統範圍**配置:git config **--system** --list * 查看**當前用戶信息**: * git config `user.name` * git config `user\.email` * **全局配置用戶信息**: * git config **--global** `user.name` * git config **--global** `user.email` * **全局**更改 * git config --global `user.name` "Your Name" * git config --global `user.email` "your.email@example.com" * **當前倉庫**更改 * git config `user.name` "Your Name" * git config `user.email` "your.email@example.com" ## 分支基礎 ### 常用指令 * git branch 查看分支 * git branch cat 新增分支 >> 名為cat * git branch -m cat tiger 分支改名 >> 將分支名cat,修改成tiger * git branch -M main 分支改名 * git branch -d tiger 刪除分支 >> 將分支 tiger刪除 * 如果tiger 分支還沒有被完全合併,會出現提示訊息:The bracnh "tiger" is not fully merged * git branch -D tiger 強制刪除分支 * git checkout tiger 切換成tiger分支 * git checkout -b sister 創建並切換成 sister 分支 ___ ### 概念: 1. 分支只是指向某一個Commit的指標(重要) 2. 分支只是指向某ㄧ個commit,刪除指標並不會讓commit消失 3. 分支合併:合併「分支指向的那個Commit」=> 合併Commit(合併分支) --- ### git 切換分支 * 做了兩件事 1. 更新暫存區、工作目錄:該分支會指向那個Commit的內容來更新,但在分支切換前所做的修改則還是會停留在工作目錄 2. 變更HEAD位置 --- ### git 如果調整到一半切換分支會發生什麼事情 * 狀況:目前在tiger分支,修改index.html(modified)、增加cat3.html(Untracked) * git checkout master * 確認 index.html的修改、cat3.html的增加是否還存在 * 結果:index.html的修改、cat3.html的增加 都停留在工作目錄,不受分支切換的影響 * 使用 git stash 來暫存狀態,在切換分支 ___ ### git stash 暫存目前狀態 * 使用git stash * git stash -u 可以包括 untracked的狀態 * 還原暫存 git stash pop * 瀏覽 git stash 列表 git stash list * 最後存的檔案會在最上面喲!也就是 stash{0} * 清除最新暫存 git stash drop * 指定刪除的暫存 git stah drop stash{0} 刪除第一個暫存 * 清除全部暫存 git stash clear --- ### git merge xxx 合併分支 * 狀況:master 合併tiger分支,目前tiger新增cat1.html\cat2.html * 步驟: 1. 切換成master分支:git checkout master 2. 合併tiger分支:git merge tiger * 合併後結果:master 分支增加cat1.html\cat2.html * tiger 分支來自master,合併分支時 >> git會自動選用「快轉模式」合併 * 不使用快轉模式:git merge `--no-ff` xxx ___ ### git merge XX 合併分支-2 * 將tiger分支與dog分支合併 * tiger分支與dog分支 來自master分支 * git會產生一個額外的commit來處理 * 這個commit會指向二個commit,標記來自哪兩個分支 * 步驟 1. git checkout tiger 2. git merge dog > Merge made by the 'recursive' stragtegy. dog1.html | 0 dog2.html | 0 2 files changed, 0 insertions(+),0 deletion(-) create mode 100644 dog1.html create mode 100644 dog2.html 3. 編輯合併的訊息(自動出現Vim編輯器) 4. 離開編輯,完成合併 * tiger 會指向新的commit,內容來自dog分支 * Commit會指向cat跟dog分支 * HEAD會隨著tiger分支前進,dog分支的HEAD停留在原地 --- ### 取消分支合併 * 指令: git merge --abort --- ### 救回沒有合併的分支 * 目標:刪除tiger分支後,救回tiger分支 * 步驟: 1. git branch -D tiger #刪除tiger分支,並提示是否要刪除未合併的分支(-d) * 紀錄刪除訊息:Deleted branch tiger (was 705804d). 2. git branch new_tiger 705804d #建一個新分支,指向705804d這個commit * 分支概念:分支只是指向某ㄧ個commit,刪除指標並不會讓commit消失 ### 另一種合併方式(rebase) * git rebase:重新定義分支的參考基準 * base:你這分支從哪裡生出來的 * rebase:等於修改歷史基礎 * 組合 new_tiger 與 dog 分支 <font color="#9400d3"> #兩個分支的base都是master分支</font> * 步驟: 1. git checkout new_tiger 2. git rebase dog <font color="#9400d3">#將dog分支當做我新的參考基準</font> >First, rewinding head to replay your work on top of it... Applying: cat1 test1 Applying: cat2 test2 3. 將new_tiger分支會接到dog分支上面 * rebase與merge的明顯差異:rebase合併分支的話,Git不會特別做出一個專門用來合併的Commit ### Rebase 概念 * 流程圖 ```mermaid graph TD A[b174a5]-->B[c68537] C[28a76d HEAD\tiger]-->D[32bc96]-->E[053fb2 dog]-->F[b69eb6] B-->G[e12d8e Master]-->H[XXXX] F-->G style A fill:#f6e6a1,color:#008000 style B fill:#f6e6a1,color:#008000 style G color:#f00 style E color:#f00 style C color:#f00 style D color:#007fff ``` * 說明 1. 「先拿c68537這個commit接到053fb2這個commit上」,因為c68537的原本的上一層Commit是<font color="#f00">e12d8e</font>,現在要接到<font color="#f00">053fb2</font>上,需要<font color="#f00">重新計算</font>這個Commit(<font color="#008000">c68537</font>)的SHA-1值,重新計算的值是<font color="#007fff">32bc96</font> 2. <font color="#008000">b174a5</font>要接到32bc96上面,重新計算的SHA-1值是<font color="#007fff">28a76d</font> 3. 原本tiger是指向b17a5這個Commit,<font color="#9400d3">現在要改指向</font>最後做出來那一個新的Commit(<font color="#007fff">28a76d</font>) * 舊的commit(b174a5\c68537) * 未立即被刪除,有一天會被git的回收機制處理掉 * 誰Rebase誰有差? * 會使commit的歷史順序不同 ### 取消Rebase 1. 使用Reflog:查看紀錄 1. git reflog :::info bd578fa (HEAD -> new_tiger) HEAD@{0}: rebase finished: returning to refs/heads/new_tiger bd578fa (HEAD -> new_tiger) HEAD@{1}: rebase: cat2 test2 54eb930 HEAD@{2}: rebase: cat1 test1 92efbb7 (dog) HEAD@{3}: rebase: checkout dog 705804d HEAD@{4}: checkout: moving from master to new_tiger ::: 2. 發現到:705804d HEAD@{4}: checkout: moving from master to new_tiger <font color="#008000">=></font> <font color="#9400d3">做rebase前的最後動作</font> 3. git reset 705804d --hard 回到rebase前的狀態 2. 使用ORID_HEAD * ORID_HEAD:特別紀錄點,會紀錄危險操作 * 分支合併、Rebase都是「危險操作」 * 步驟: 1. git checkout dog 2. git rebase new_tiger 3. git reset <font color="#f00">ORIG_HEAD</font> --hard <font color="#9400d3">#回到rebase前的狀態</font> 3. rebase發生衝突時,取消rebase * git rebase --abort ### 合併發生衝突的處理 * 需求:將new_tiger分支與dog分支合併 * 針對各分支的index.html相同位置,做出調整,以便觸發合併衝突 * 步驟: 1. git checkout new_tiger 2. git merge dog <font color="#f00">#發生衝突</font> :::danger uto-merging index.html CONFLICT (content): Merge conflict in index.html Automatic merge failed; fix conflicts and then commit the result. ::: 3. git status :::info <font color="#000000">On branch new_tiger You have unmerged paths. (fix conflicts and run \"git commit\") (use \"git merge --abort\" to abort the merge) Changes to be committed:</font> <font color="#008000">new file: dog1.html new file: dog2.html</font> <font color="#000000">Unmerged paths:</font> <font color="#000000">(use "git add &lt;file&gt; ..." to mark resolution)</font> <font color="#f00">both modified: index.html</font> ::: 4. 分支衝突處理:<font color="#f00">對 index.html做內容選擇</font>,是要用new_tiger分支或dog分支的內容 5. 處理完後:git <font color="#f00">add</font> index.html 6. 完成分支合併:git commit `-m` "xxxx" * git reset ORIG_HEAD `--hard` <font color="#9400d3">#解除merge合併</font> ### Rebase 衝突處理 * 需求:new_tiger 重新定義基準 git rebase dog * 發生衝途:rebase過程會中斷 * 步驟: 1. git checkout new_tiger 2. git rebase dog <font color="#f00">#發生衝突</font> :::danger <font color="#000000"> First, rewinding head to replay your work on top of it... Applying: cat1 test1 Applying: cat2 test2 Applying: TEST merge ERROR car Using index info to reconstruct a base tree... <font color="#f00">M index.html</font> Falling back to patching base and 3-way merge... Auto-merging index.html CONFLICT (content): Merge conflict in index.html <font color="#f00"> error: Failed to merge in the changes.</font> Patch failed at 0003 TEST merge ERROR car <font color="#e6b800"> hint: Use 'git am --show-current-patch' to see the failed patch</font> Resolve all conflicts manually, mark them as resolved with <font color="#008000">"git add/rm &lt;conflicted_files&gt;"</font>, <font color="#007fff">then run "git rebase --continue".</font> You can instead skip this commit: run "git rebase --skip".</font> To abort and get back to the state before "git rebase", run "git rebase --abort".</font> ::: 3. 處理發生衝突的檔案:處理index.html 4. 處理完後:git add index.html 5. 繼續Rebase(完成):git rebase --continue ### 非文字檔發生衝突(Merge) * 狀況:new_tiger分支合併dog分支,各分支都有一個同名的圖片檔 * 步驟: 1. git checkout new_tiger <font color="#f00">#發生衝突</font> :::danger <font color="#000000">warning: <font color="#f00">Cannot merge binary files: testimage.png </font>(HEAD vs. dog) CONFLICT (add/add): Merge conflict in testimage.png Auto-merging testimage.png Automatic merge failed; fix conflicts and then commit the result. </font> ::: 2. git status :::info On branch new_tiger You have unmerged paths. (fix conflicts and run "git commit") (use "git merge --abort" to abort the merge) Changes to be committed: <font color="#008000">new file: dog1.html new file: dog2.html</font> Unmerged paths: (use "git add &lt;file&gt;..." to mark resolution) <font color="#f00">both added: testimage.png</font> ::: 3. 選擇要使用new_tiger或dog分支的圖片 * 使用new_tiger分支:git checkout <font color="#f00">--ours</font> testimage.png * 使用dog分支:git checkout <font color="#f00">--theirs</font> testimage.png 4. 處理完後:git <font color="#f00">add</font> testimage.png 5. 完成分支合併:git commit -m "xxxx" ### 從過去的某一個commit開啟分支 * 流程圖 ```mermaid graph TD A[921fbb7f]-->B[4cbf089b] C[705804d4]-->D[ab7eca6c] B-->G[4f559e1b]-->H[bea0a519]-->I[0929fe29] D-->G style H fill:#f00 ``` * 狀況:從bea0a519,開啟一個新分支 * 法一: 1. 切換至bea0a519 Commit:git checkout bea0a519 2. 建立切換至bird分支:git checkout -d bird * 法二:git branch bird bea0a519 ## 修改歷史紀錄 ### 修改歷史訊息 * 需求:dbfccb4的訊息(cat) 調整 * 訊息調整,<font color="#f00"> git會產生新的Commit來代替舊的</font> * 步驟: 1. git rebase -i a2e0040 <font color="#9400d3">#選擇rebase的切入點</font> :::info <font color="#000000"> pick dbfccb4 cat pick d07dee5 dog1 pick 0aaeb33 dog2 </font> ::: 2. 編輯跳出的視窗,使用reword指令 :::info <font color="#000000"> <font color="#f00"> reword</font> dbfccb4 cat pick d07dee5 dog1 pick 0aaeb33 dog2 </font> ::: 3. 離開Vim編輯後,開始進行Rebase,會跳出Vim來編輯新Commit訊息 * 將cat 調整成 new cat 4. 存檔離開後,完成Commit的訊息調整 ### 將多個Commit合併為一個Commit * 流程圖 ```mermaid graph TD A[639b2f8]-->B[ef30bbd]-->C[a91ced5]-->D[8b5ee11]-->E[b226606]-->F[a2e0040] style A fill:#008000 style B fill:#008000 style C fill:#007fff style D fill:#007fff style E fill:#007fff ``` * 將639b2f8和ef30bbd合併為一個commit、8b5ee11和b226606合併一個一個commit * 注意:互動模式的紀錄由上而下是從舊到新 * 步驟 1. git rebase -i a2e0040 <font color="#9400d3">#選擇rebase的切入點</font> :::info <font color="#000000">pick b226606 cat1 pick 8b5ee11 cat2 pick a91ced5 edd1 pick ef30bbd dog1 pick 639b2f8 dog2 </font> ::: 2. 編輯跳出的視窗,使用squash指令 :::info <font color="#000000">pick b226606 cat1 <font color="#f00">squash</font> 8b5ee11 cat2 <font color="#f00">squash</font> a91ced5 edd1 pick ef30bbd dog1 <font color="#f00">squash</font> 639b2f8 dog2 </font> ::: 3. 上面的修改會發生下列的事 1. 639b2f8會跟前一個Commit ef30bbd 合併 2. a91ced5跟 8b5ee11合併,再跟前一個Commit b226606 合併 4. 離開Vim編輯後,開始進行Rebase,會跳出Vim來編輯新Commit訊息 * 有兩個合併Commit的操作,需要做兩次的Commit的訊息編輯(編輯第一個Commit訊息) * 編輯Commit的訊息 >> Git會產生新的Commit來代替舊Commit * 新流程圖 ```mermaid graph TD A[4984b96]-->B[dbfccb4]-->C[a2e0040] style A fill:#008000 style B fill:#007fff ``` * b226606\8b5ee11\a91ced5 合併後的commit => dbfccb4 * 639b2f8\639b2f8 合併後的commit => 4984b96 ### 將一個Commit拆解成多個Commit * 需求:拆解4984b96這個Commit * 步驟: 1. git rebase -i a2e0040 <font color="#9400d3">#選擇rebase的切入點</font> :::info <font color="#000000"> pick dbfccb4 cat pick 4984b96 dog</font> ::: 2. 編輯跳出的視窗,使用edit指令 :::info <font color="#000000"> pick dbfccb4 cat <font color="#f00">edit</font> 4984b96 dog</font> ::: 3. 拆Commit:git reset HEAD^ <font color="#9400d3">#當前HEAD的位置是在4984b96這個Commit</font> 4. 發現 dog1.txt\dog2.txt,拆出<font color="#008000">是放在工作區</font>且<font color="#007fff">Untracked狀態</font> 5. 將上面這兩個檔案分別Commit 6. 繼續rebase(完成):git rebase --countinue ### 在某些Commit之間再加新的Commit * 需求:在b6a5fbd(dog1)跟dbfccb4(cat)這兩個Commit之間,在多增加兩個Commit * 步驟: 1. git reabse -i a2e0040 <font color="#9400d3">#選擇rebase的切入點</font> :::info <font color="#000000"> <font color="#9400d3">pick dbfccb4 cat</font> <font color="#9400d3">pick b6a5fbd dog1</font> pick fd3cf39 dog2</font> ::: 2. 編輯跳出的視窗,使用edit指令 <font color="#9400d3">#需停在dbfccb4 cat</font> :::info <font color="#000000"> <font color="#f00">edit</font> dbfccb4 cat pick b6a5fbd dog1 pick fd3cf39 dog2</font> ::: 3. 添加兩份檔案,並分別Commit 4. 繼續rebase(完成):git rebase --countinue ### 調整Commit順序 * 將1b632fb(dog1)\6060645(dog2)移動至dbfccb4(cat)後面 * 步驟: 1. git rebase -i a2e0040 <font color="#9400d3">#選擇rebase的切入點</font> :::info <font color="#000000"> pick dbfccb4 cat pick 8ca3152 edd1 pick 6060645 dog1 pick 1b632fb dog2</font> ::: 2. 編輯跳出的視窗,調整Commit順序 <font color="#000000"> pick dbfccb4 cat <font color="#f00"> pick 6060645 dog1 pick 1b632fb dog2</font> pick 8ca3152 edd1</font> 3. 存檔離開後,完成Commit順序調整 ### 刪除某些Commit * 需求:刪除有dog訊息的Commit * 步驟: 1. git reabse -i a2e0040 <font color="#9400d3">#選擇rebase的切入點</font> :::info <font color="#000000"> pick dbfccb4 cat pick bd27a88 dog1 pick 3206c6d dog2 pick 82ab1ab edd1</font> ::: 2. 編輯跳出的視窗,使用<font color="#008000">drop指令</font>或<font color="#007fff">刪除該行</font> :::info <font color="#000000"> pick dbfccb4 cat</fon> <font color="#000000"> drop 3206c6d dog2</font> pick 82ab1ab edd1</font> ::: 3. 存檔離開後,完成Commit的刪除 ### Revert 指令 * 概念:做一個新Commit,來取消不要的Commit * 需求:取消最後這次的Commit(dog3) * git revert HEAD --no-edit * 取消Revert * 法ㄧ:再開一次revert >> git revert HEAD --no-edit * 法二:使用reset >> git reset HEAD^ --hard ### 什麼時候使用Revert * 使用時機:多人協作專案 * 團隊的開發政策,可能無法使用reset * 使用Revert指令做出一個「取消」的操作,對其他人來說不算是「修改歷史」,而是新增一個Commit * Reset\Rebase\Revert 差別 | 指令 | 改變歷史紀錄 | 說明 | | -------- | -------- | -------- | | Reset | 是 | 把⽬前的狀態設定成某個指定的 Commit 的狀態,通常適⽤於尚未推出去的 Commit。 | | Rebase | 是 | 不管是新增、修改、刪除 Commit 都相當⽅便,⽤來整理、編輯還沒有推出去的 Commit 相當⽅便,但通常也只適⽤於尚未推出去的 Commit。 | | Revert | 否 | 新增⼀個 Commit 來反轉(或說取消)另⼀個 Commit 的內容,原本的Commit 依舊還是會保留在歷史紀錄中。雖然會因此⽽增加 Commit 數,但通常比較適⽤於已經推出去的 Commit,或是不允許使⽤ Reset 或 Rebase 之修改歷史紀錄的指令的場合。 | ## 標籤 Tag ### 使用標籤 * 標籤:「標籤(tag)」是⼀個指向某⼀個 Commit 的指標。 * 當做貼紙⼀樣看待,它就是貼在某個 Commit上的東⻄。 * 什麼時候使用標籤?:通常在開發軟體有完成特定的⾥程碑,例如軟體版號 1.0.0 或是 beta-release 之類的,這時候就很適合使⽤標籤做標記。 * 有兩種標籤: * 輕量標籤(lightweight tag)=> 個⼈使⽤或是暫時標記⽤途 * 有附註標籤(annotated tag)=> 是軟體版號之類的⽤途 * 差異:訊息量的不同 * 展現標籤訊息指令: git show <font color="#996b1f"> 標籤名</font> <font color="#9400d3">=></font> git <font color="#f00">show</font> <font color="#996b1f"> big_cats</font> #### 輕量標籤(lightweight tag) * 概念:為輕量標籤僅是⼀個指向某個 Commit 的指標, <font color="#f00"> 沒有含有其 它的資訊</font>, <font color="#9400d3">所以 Git 比較推薦使⽤有附註的標籤(annotated tag)</font>。 * 需求:在44e9fe1(add lion and tiger)上,打上一個big_cats的標籤 * 指令:git <font color="#f00"> tag</font> <font color="#996b1f"> big_cats</font> <font color="#008000">44e9fe1 </font> * git歷史紀錄 git log --oneline :::info <font color="#000000"> 9c5fd18 (HEAD -> master) add fish 1 e62a0b0 add pig1 <font color="#f00">44e9fe1 <font color="#996b1f"> (tag: big_cats)</font> add lion and tiger</font> 1683520 dog3</font> ::: #### 有附註標籤(annotated tag) * 需求:在e62a0b0(add pig1)上,打上一個big_pigs的標籤,並加上訊息 "Big Pigs are comming" * 指令:git <font color="#f00">tag</font> <font color="#996b1f"> big_pigs </font><font color="#008000"> e62a0b0</font> <font color="#f00">-a -m "Big Pigs are comming" </font> * -a:請 Git 幫你建立有附註的標籤 * -m:在做⼀般的 Commit ⼀樣輸入的訊息 #### 刪除標籤 * 需求:刪除<font color="#996b1f"> big_cats</font>這個標籤 * 指令:git tag <font color="#f00">-d</font> <font color="#996b1f"> big_cats </font> * <font color="#f00">-d</font>:刪除標籤 * 刪除後訊息: Deleted tag 'big_cats' (was 44e9fe1) ## 其它常見狀況題與冷知識 ### ⼿邊的⼯作做到⼀半,臨時要切換到別的任務 * 法一:Commit目前進度 * 步驟: 1. git add --all 2. git commit -m "not finish yet" 3. git reset HEAD^ * 法二:使用Stash * 目前new_tiger分支狀態 git status :::info <font color="#000000"> On branch new_tiger Changes not staged for commit: (use "git add &lt;file&gt;..." to update what will be committed) (use "git restore &lt;file&gt;..." to discard changes in working directory) &emsp; <font color="#f00">modified: cat1.hml &emsp; modified: cat2.hml &emsp; modified: index.html</font> no changes added to commit (use "git add" and/or "git commit -a")</font> ::: * 步驟: 1. git <font color="#f00">stash</font> <font color="#9400d3">#將上述檔案的修改先「存」起來</font> * <font color="#008000">Untracked 狀態</font>的檔案 <font color="#9400d3">沒有辦法被Stash</font>, <font color="#f00">需要額外使用 -u 參數</font> 2. git status 確認狀態 :::info <font color="#000000"> On branch new_tiger nothing to commit, working tree clean</font> ::: * 檔案存到那邊去? * git stash --list * stash@{0}: WIP on new_tiger: fe4d60e TEST * stash@{0} 是這個Stash的代名詞 * 撿Stash回來用 1. 確認目前Stash 列表 git stash --list 2. git stash <font color="#f00">pop </font>stash@{0} * pop:將stash@{0}拿出來,套用在目前的分支上,並將剛套用過的Stash刪除 * 如果沒有指定套用的Stash,會從編號最小的開始 * 刪除Stash:git stash <font color="#f00">drop</font> stash@{0} * 套用Stash:git stash <font color="#f00">apply</font> stash@{0} * 套用+刪除Stash:git stash <font color="#f00">pop </font>stash@{0} ### 去除某一個檔案 filter-branch * 需求:去除config/daatbase.yml * 使用filter-branch:git filter-branch --tree-filter "rm -f config/daatbase.yml" ### 回復剛剛filter-branch 造成的結果 * 指令:git reset refs/original/refs/heads/master --hard ### 撿別的分支的Commit過來合併 cherry-pick * 概念:不是將原有的Commit剪過來貼上,而是比較像是複製Commit的內容過來 * 接到new_tiger的內容需要重新計算,產生新的Commit * 原本fish分支的Commit不會有變化,還是在原來的地方 * 需求:挑選fish分支的<font color="#008000">62451b2(add whale)</font>和<font color="#007fff">38d07c7(dd dolphin)</font>,合併至new_tiger分支 * 指令:git <font color="#f00">cherry-pick</font> 38d07c7 62451b2 ### 撿過來但不先合併 * 需求:撿過來的Commit,<font color="#f00">先放置在暫存區</font> * 添加 <font color="#f00">--no-commit</font>參數:git cherry-pick <font color="#f00">--no-commit</font> <font color="#008000">38d07c7 </font> ### 怎麼樣把檔案真正的從 Git 裡移掉 * 需求:完全刪除config/database.yml * 步驟: 1. 添加 <font color="#f00">-f參數</font>: git filter-branch <font color="#f00">-f </font>--tree-filter <font color="#9400d3">"rm -f config/database.yml"</font> 2. 處理資源回收的事項 1. rm .git/refs/original/refs/heads/master * 清除備份點 2. git reflog expire --all --expire=now * 要求 Reflog 現在立刻過期(不然預設要等 30 天) 3. git fsck --unreachable * 看到很多 Unreachable 的物件 4. git gc --prune=now * 啟動 Git 的資源回收機制 3. 檢查⼀下是否都清除 git fsck :::info <font color="#000000">Checking object directories: 100% (256/256), done. Checking objects: 100% (16/16), done.</font> ::: ### 本地分支更名 to 遠端分支 * 前提:**確認該分支本地是否有或為最新** * 步驟: 1. 本地分支更名:`git branch -m oldName newName` 2. 刪除遠端分支:`git push --delete origin oldName ` * 或至gitlab\gitHub 將遠端分支刪除 3. 將更名後的分支推上:`git push -u origin newName` ### 遠端分支更名 to 本地分支更名 * 步驟: 1. 本地分支更名:git branch -m oldName newName 2. 設定目前分支的上游分支:git branch **-u** origin/dev ### 遠端倉庫-操作 * 查看當前遠程倉庫:git remote **-v** * 添加倉庫:git remote **add** \<remote_name> \<remote_url> * 刪除倉庫:git remote **remove** \<remote_name> * **調整**: * **倉庫名**(`remote_name`):git remote **rename** \<old_name> \<new_name> * **倉庫**(`remote_url`):git remote **set-url** \<remote_name> \<new_remote_url> ### 修改舊有的commit * 使用 **rebase** * **情境**:當前有3個commit 需要 **對第一個commit** 增加\修改內容 * **commit**: * **abc123** - 第一個提交 * def456 - 第二個提交 * ghi789 - 第三個提交(`最新的提交`) * 向第一個提交 `abc123` 增加内容 * 步驟: 1. git rebase -i HEAD~**3** * HEAD~3 表示對最近 3 個提交進行變基。 3. 編輯**目標**提交 * 編輯器會開啟一個**commit列表**,顯示以下內容: ``` pick abc123 First commit pick def456 Second commit pick ghi789 Third commit ``` * 將第一個commit **pick** 改為 **edit** ``` edit abc123 First commit pick def456 Second commit pick ghi789 Third commit ``` * **pick** abc123 First commit => **edit** abc123 First commit * **儲存並退出編輯器** 3. **修改第一個提交**: * rebase將**暫停在第一個提交**,終端將提示類似以下信息 ``` Stopped at abc123... First commit You can now amend the commit. ``` * **添加修改**:git add \<file1\> \<file2\> * **提交修改**:使用 **--amend** 選項 * `git commit --amend` 4. **繼續rebase**:git rebase `--continue` 5. **循環 3/4的操作**,直到rebase完成 6. 强制推送(如果已推送到遠端):git pusg -f * **中斷**:`git rebase --abort` ### origin/dev被rabase或force push後的處理 * 情境: * `dev分支`被rabase或force push * 當前有`f1` \ `f2`分支從`dev分支`中**切出** * 說明: * 本地的 `dev` 已經跟遠端歷史**不再共享共同祖先** (diverged) * 因此你必須重設它,並且讓下游分支(`f1`, `f2`)**重新 rebase 到新的 dev 上** * 作法:`f1` \ `f2`分支要手動 rebase 到新的基準(base)上 * 每個對應的分支都要個別手動調整 * 步驟: 1. **更新遠端資訊** * git fetch origin 2. rebase `f1分支` * git **checkout** `f1` * git **rebase** `origin/dev` 3. rebase `f2分支` * git **checkout** `f2` * git **rebase** `origin/dev` 4. 處理dev * git **checkout** dev * git **reset** --hard `origin/dev` * 把本地的`dev分支`**強制重設**成跟`遠端的dev`**一模一樣** 5. 處理`f1` \ `f2`分支的遠端 => rebase強制推送 * git push -f origin f1 * git push -f origin f2 * 若擔心風險,可以在 rebase 前備份: * git branch backup/f1 f1 * git branch backup/f2 f2 ### 本地分支與遠端分支有衝突 * **遠端分支為主**,二種方式: * git pull `--rebase` * **底層邏輯**: 1. 將本地commit暫時移除 2. 拉取遠端最新的commit 3. 把本地commit套用在最新的遠端上 * 需要處理衝突 * git pull origin \<brancj-name * 將遠端分支的commit merge到本地,**產生一個新的merge commit** * **本地分支為主**,強推 * git push -f ## git config 常見設定 * 使用者資訊 * git config --global user.name "你的名字" * git config --global user.email "你的信箱" * 編碼設定 * git config --global i18n.commitEncoding utf-8 * git config --global i18n.logOutputEncoding utf-8 * 編輯器設定 * 使用 git commit 時會開啟 VSCode 編輯 commit 訊息 * git config --global core.editor "code --wait" * 憑證儲存方式 * git config --global credential.helper store # 儲存純文字憑證 * git config --global credential.helper cache # 暫存憑證(預設 15 分鐘) * git config --global credential.helper manager # Windows 使用者 * git config --global credential.helper osxkeychain # macOS 使用者 * 顯示log美化 * git config --global alias.lg "log --oneline --graph --decorate --all" * 設定換行符號(跨平台) * git config --global core.autocrlf input # macOS / Linux * git config --global core.autocrlf true # Windows ###### tags: `Git`