你可能不知道

git 指令

(二)

也大概用不到也不會想用到

rerere and reflog

FlyC

Outline

  • 場景連連看
  • git rerere
  • git reflog

場景連連看

請將對應的場景會使用到的git 指令做連連看

寫code寫到一半、還沒到適合commit 的時機點就要去hotfix;

hotfix 到一半、還沒到適合commit 的時機點又有超級hotfix,我不想切來切去

我的程式碼炸裂、關聯檔又太多,

不確定問題在哪隻檔案,我想知道是哪個commit 害他壞去

開發途中,我不確定我的feature 和master 有沒有衝突,

但我不想留下一直把master merge 回自已身上的commit

我不小心把遠端的分支和自己本地的分支全砍光後,

PM說剛剛的單還是上好了

候選名單:

rerere

bisect

reflog

worktree

git worktree

寫code寫到一半、還沒到適合commit 的時機點就要去hotfix;

hotfix 到一半、還沒到適合commit 的時機點又有超級hotfix,我不想切來切去

git bisect

我的程式碼炸裂、關聯檔又太多,

不確定問題在哪隻檔案,我想知道是哪個commit 害他壞去

git rerere

開發途中,我不確定我的feature 和master 有沒有衝突,

但我不想留下一直把master merge 回自已身上的commit

git reflog

我不小心把遠端的分支和自己本地的分支全砍光後,

PM說剛剛的單還是上好了

git rerere

reuse recorded resolution

重新使用 - 被記錄過的 - 解法

開發途中,我不確定我的feature 和master 有沒有衝突,

但我不想留下一直把master merge 回自已身上的commit

在開始之前,先來個預防針

 

git rerere 不太好理解

 

在操作上也比其他部分繁瑣、

很容易造成分支上的打結

 

所以

 

建議在操作上還是以複製為主、

減少操作上的失誤

警告

那麼,我們開始吧!

首先,git rerere 是git 的一個

 

設定項目

 

就像是user.name, core.ignorecase 等同款

設定過一次、就會永久生效

當然也會依照.gitconfig 的層級做疊加的影響範圍

 

那麽,就先把它打開吧

# default 是false
git config --global rerere.enabled true 

可以透過檢查 ~/.gitconfig 檔案來檢查是否設定成功

設定完成後,我們來介紹一下等等的流程:

 

我們要故意製造衝突

 

1. 創建 1 個假裝的主分支

2. 創 3 個新檔案,裡面都留空

3. 創建 2 個分支,A 跟 B

4. 分別在A 和B 修改步驟 1. 創的第 1 個檔案,把內容分別          改成A1B2

5. 切換回主分支

6. 把A merge 回主分支

7. 切到B,把主分支往自己身上 merge

8. 解決衝突

# 創建一個假的測試用master 分支
git checkout -b test-rerere/master

# 創建 3 個檔案 + commit
touch file-1.txt
touch file-2.txt
touch file-3.txt
git add file-1.txt file-2.txt file-3.txt
git commit -m "[add] touch 3 files"

# 創建 2 個branch
git checkout -b test-rerere/A-分支
git checkout -b test-rerere/B-分支

# 分別修改第 1 個檔案 + commit
## A branch
git checkout test-rerere/A-分支
echo "A-1" > file-1.txt
git add file-1.txt
git commit -m "[modified] modified file-1.txt to A"
## B branch
git checkout test-rerere/B-分支
echo "B-1" > file-1.txt
git add file-1.txt
git commit -m "[modified] modified file-1.txt to B"

# 切換回主分支 + 把A merge 回主分支
git checkout test-rerere/master
git merge test-rerere/A-分支 --no-ff -m "Merge A into master"

# 切到B,把主分支往自己身上 merge
git checkout test-rerere/B-分支
git merge --no-ff test-rerere/master

那麼,開始吧

我們來解衝突吧!

# 解決衝突
echo "file-1 我就是要B" > file-1.txt
git diff # 查看差異
# bash
git add file-1.txt
# 打好 Merge message
git commit -m "Merge master into B"


# 結束!
cat file-1.txt # file-1 我就是要B

我們來看看線吧

唔... 合來合去的有點醜,我決定了,

我要把 剛剛解決衝突的方式記下來

等到最後要合進master 的時候再解就好了

那,就來倒退

# 退回上一個commit
git reset --hard head^
git log --graph --oneline --all

那我們繼續開發囉,一樣

 

 

 

切回 A ,開發file-2

切回 B ,開發file-2

先把 A 合進master

再把master 合到 B 上

解決衝突

 

# 首先是A 分支
git checkout test-rerere/A-分支
echo A-2 > file-2.txt
git add file-2.txt
git commit -m "[add] add A-2 to file-2"

# 換B 分支
git checkout test-rerere/B-分支
echo B-2 > file-2.txt
git add file-2.txt
git commit -m "[add] B-2 to file-2"

# 切回主分支
git checkout test-rerere/master
# 合併A 分支
git merge test-rerere/A-分支 --no-ff -m "Merge A into master"

# 切回B 分支,接著把master merge 回B 分支
git checkout test-rerere/B-分支
git merge --no-ff test-rerere/master

#衝突!

那麼,來看看 git diff 吧!

這次應該會有之前file-1.txt 的衝突、跟新的file-2.txt 的衝突

git diff

嗯,好像跟之前ㄧㄧㄤ...

這個衝突我好像在哪裡看過嘛!

而且這個解法跟我之前解的好像很像嘛!

沒錯,這個衝突就是之前發生在 file-1.txt 的衝突

咱 git rerere 的功能就在這裡啦!

 

總之,先把新出現的衝突解一解吧

echo "file-2 我就是要B" > file-2.txt
git diff # 可以稍微看一下
git add file-2.txt
git add file-1.txt # 別忘了file-1.txt 也要加進來喔
git commit -m "Merge master into B"

# 直接來看一下log 吧
git log --graph --oneline 

直接來看一下log 吧

不要緊張不要害怕,為了讓線乾淨些

讓我們回到上一個還沒有把master 往自己身上merge 的時候吧!

# 回到上一步
git reset --hard head^
# 查看 log
git log --graph --oneline --all

那麼,我們要繼續囉

直接對 file-3.txt 做一樣的事情,這部分就不做解釋了

直接寫code

# 首先是A 分支
git checkout test-rerere/A-分支
echo A-3 > file-3.txt
git add file-3.txt
git commit -m "[add] add A-3 to file-3"

# 換B 分支
git checkout test-rerere/B-分支
echo B-3 > file-3.txt
git add file-3.txt
git commit -m "[add] B-3 to file-3"

# 切回主分支
git checkout test-rerere/master
# 合併A 分支
git merge test-rerere/A-分支 --no-ff -m "Merge A into master"

# 切回B 分支,接著把master merge 回B 分支
git checkout test-rerere/B-分支
git merge --no-ff test-rerere/master

#衝突! 直接看 git diff
git diff

想必大家也猜到了

咱偉大的git rerere 

 

已經全部記起來啦!!!

 

所以我們依舊只要解決最新的那file-3.txt 造成的衝突即可

 

直接上code

# 解決衝突
echo "file-3 我還是要B" > file-3.txt
git add file-3.txt
git add file-1.txt # 別忘記把file-1 加進來囉
git add file-2.txt # 當然也別忘記file-2
git commit -m "Merge master into B"

# 來看看log 
git log --graph --oneline

不過..

如果我們不在意線的樣子、

就這樣直接把B 分支合回主線會發生什麼事?

這張圖並沒有符合其梗圖格式

# 切回主分支
git checkout test-rerere/master
# 把B 合併進master
git merge test-rerere/B-分支 --no-ff --no-commit
git commit -m "Merge B into master"

# 查看log
git log --graph --oneline

那條該死的線交叉了

我不要,讓我們回到上一步.. 不

上兩步好了!

讓master 回到合併B 之前、

B 則是回到合併 master 之前

直接上code!

# 讓master 往回走
git reset --hard head^
# 讓B 往回走
git checkout test-rerere/B-分支
git reset --hard head^

# 看看 log
git log --graph --oneline --all

master 是master

B 是B

那麼,我們來merge 吧!

git checkout test-rerere/master
git merge test-rerere/B-分支 --no-ff

# 衝突! 看看git diff
git diff

全部,都記起來啦

所以我們只要全部加一加就好囉

# 解衝突
git add file-1.txt
git add file-2.txt
git add file-3.txt
git commit -m "Merge B into master"

# 查看log
git log --graph --oneline

完美

這就是git rerere 的功能

git rerere 是一個設定項目,預設是false

他會記錄起發生在此分支上的所有衝突

並且在再次遇到的時候顯示出來供選擇

 

由此,可以在同步master 功能的同時

達到不讓線有交叉、

讓後續log 觀看較為適宜的好處

 

但也因此,如果開發時間太長的分支

在合併的時候很可能忘記之前是怎麼解的

造成錯過二次檢驗衝突的機會

 

在使用上需多斟酌

別急,還沒結束

 

讓我們回到上一步、

看看如果沒有設定git rerere 為true 的話

在最後一步會是什麼情況

# 回到還沒merge 的上一步
git reset --hard head^
# 檢查一下log 
git log --graph --oneline --all

master 和B 已經確實分開了

讓我們把git rerere 設定回false

# 修改回false
git config --global rerere.enabled false

一樣檢查一下 ~/.gitconfig

就來把B 合併到master 吧!

# 把B 合併進master
git merge test-rerere/B-分支 --no-ff

# 衝突! 看看差異
git diff

可以看到之前解過的衝突再次出現了..

除非有工人智慧去記住

不然全部都得重解一次

 

好啦,這下真的結束git rerere

如果忘記了

直接下 git rerere --help

就可以看到圖示範例囉

 

 

限制:

git version ^2.5

git reflog

Reference + logs = reflog

我不小心把遠端的分支和自己本地的分支全砍光後,

PM說剛剛的單還是上好了

https://img.moegirl.org/common/thumb/4/43/Monster_Reborn.jpg/250px-Monster_Reborn.jpg

PM 說我們要開發一個新功能,這個功能有點複雜、牽涉的功能很多,改動的檔案多到讓人想提早下班。你接到案子之後立刻把手邊其他的案子全都停掉,開始全心全意投入這個案子。在開發的過程一共新增了150張圖庫的圖片、還有異動到連同html 檔案也算進來的共76支檔案、230個以上的commit 節點,途中還有開發小需求線、做自己本地端的合併與統整。就在你以為一切都可以上線的時候,PM 說他要修改需求,像是把150 張圖片的邏輯全部抽走之類的。由於不想用rebase 或是reset 等比較暴力的方式修改先前提交的commit ,你決定用往後繼續開發的方式開發修改的功能,洋洋灑灑又多了437個commit,還有很多很多的新舊檔案刪除與異動,最後的最後,PM 走到你的身後,他開口...

 

客戶決定不上了

RD 就憤而把分支全砍了

本地的、遠端的

全砍了

然而PM 兩個小時後,又來到了RD的身後

欸客戶說還是上好了

.............................

.................................................................

不要緊張不要害怕

git reflog 來拯救

為了測試,我們把我們剛剛的測試分支

全都砍掉!

# 切回真正的master
git checkout maseter
# 確認現在有什麼分支可以砍
git branch --all

# 全部砍掉
git branch -D test-bisect/master
git branch -D test-rerere/A-分支
git branch -D test-rerere/B-分支
git branch -D test-rerere/master
git branch -D test-worktree/master
git branch -D 真紅眼黑龍
git branch -D 黑魔導

# 然後再確認一次分支
git branch --all

接著,就直接輸入 git reflog 看看結果吧!

# 直接輸入git reflog
git reflog

天殺的這什麼鬼

不過仔細看就可以發現, 這是我們之前對commit 的所有操作記錄!

只要挑選到正確的hash key...

# 切換到hash key
git checkout b6dc7ed # 後面的這串hash 每個人都會不一樣

# 查看 log
git log --graph --oneline --all

長回來啦

透過這種方式

就可以讓已經不存在本地或遠端的commit 成功復原

 

git reflog 看起來雖然是相當齊全的記錄檔

但其實有相當多的限制,像是

 

1. 他只會紀錄這台機器的操作記錄

2. 延續1, 所以其他機器無法復原被刪除的commit

3. 有點亂

4. 不會紀錄沒有commit 的操作記錄

5. 超級亂

6. 非常亂

 

差不多是這樣

QA

THX

參考資料

Made with Slides.com