flyc
La vie est drôle
(二)
也大概用不到、也不會想用到
rerere and reflog
FlyC
請將對應的場景會使用到的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說剛剛的單還是上好了
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 個檔案,把內容分別 改成A1 和 B2
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
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的身後
欸客戶說還是上好了
.............................
.................................................................
不要緊張不要害怕
為了測試,我們把我們剛剛的測試分支
# 切回真正的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. 非常亂
差不多是這樣
參考資料
By flyc