假設我有一個修改六個檔案的功能分支。該分支上的大多數提交都涉及file.py. 我最終意識到有一種可能更好的方法來實作這個功能,而無需任何接觸file.py。有沒有辦法調整我分支上的所有提交以不觸及該檔案?我覺得應該有一些互動式 rebase 的技巧可以很容易地做到這一點,但我不確定那會是什么技巧。
當然,我可以簡單地將更改還原為新的提交,但為后代混淆歷史似乎很遺憾。
uj5u.com熱心網友回復:
在分支的“開始”之前一直執行互動式變基(即,到該檔案仍然是您想要的方式的最近可訪問的提交)。讓我們稱之為提交abcde:
git rebase -i abcde
Todo 串列將出現,列出該“好”提交之后的所有提交。
pick 11111
pick 22222
pick 33333
...
在 Todo 串列中,標記每個提交edit。
edit 11111
edit 22222
edit 33333
...
保存并關閉編輯器。變基將開始。當變基中的每個階段都停止并等待您的指令時,從分支之前恢復相關檔案的狀態:
git restore --source abcde --worktree --staged -- <pathToFile>
git rebase --continue
您的編輯會詢問您是否要更改提交資訊;你沒有,所以只需關閉編輯器,然后我們繼續下一個提交。
當您到達該程序的最后時,您將一直攜帶一個未更改的檔案通過分支。
可以通過使用來自動化我剛才所說的一切git filter-repo,但是您要求使用互動式 rebase 的技巧,所以這就是我所描述的。
另外,請注意,如果您碰巧知道相關檔案在特定提交中未更改,則可以將該提交設定保留pick在待辦事項串列中,從而節省一些步驟。有辦法找出答案。但我的回答刻意簡單,忽略了這個問題。
我總是喜歡用插圖來證明我的答案。開場情況如下:
* f2fa796 (HEAD -> main) three
* dc90263 two
* 4861739 one
每個提交都有一個檔案a.txt,它會從上一次提交中更改該檔案,我現在將展示:
% git show HEAD:a.txt
aaa
% git show HEAD~1:a.txt
aa
% git show HEAD~2:a.txt
a
很好,讓我們開始我們的互動式變基:
% git rebase -i 4861739
在編輯器中,我將兩個提交都標記為edit,然后關閉編輯器。變基開始:
Stopped at dc90263... two
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
所以我說:
% git restore --source 4861739 --staged --worktree -- a.txt
% git rebase --continue
我們繼續進行下一次提交。此檔案中存在合并沖突,但這對我來說并不重要:
% git restore --source 4861739 --staged --worktree -- a.txt
% git rebase --continue
Git 說:
Successfully rebased and updated refs/heads/main.
那么現在讓我們再次檢查一下情況:
% git show HEAD:a.txt
a
% git show HEAD~1:a.txt
a
% git show HEAD~2:a.txt
a
這正是想要的結果。
uj5u.com熱心網友回復:
這可以通過在變基期間使用自動沖突解決來自動化。該演算法可以像這樣作業:
- 在提交處創建一個分支,其中包含相關檔案的最后更改。
- 在該分支上創建一個新的提交,將檔案的全部內容替換為獨特的東西(甚至可能什么都沒有)。這里的重點是確保您希望忽略的所有新更改肯定會與此更改發生沖突。
- 將您的主分支重新定位到 (
--onto) 新的臨時分支上,并使用“我們的”合并策略 (-Xours)。 - 現在使用互動式變基或通過另一個變基從您的分支中洗掉臨時提交,
--onto如下所示。
這是一個 bash 腳本,它演示了實際的演算法:
#!/bin/bash -v
git init
git branch -m main # name branch main in case that isn't your default
echo asdf > asdf; echo stuff > file.py; git add .; git commit -m "Create commit 1"
echo line2 >> asdf; echo line2 >> file.py; git add .; git commit -m "Create commit 2"
echo line3 >> asdf; echo line3 >> file.py; git add .; git commit -m "Create commit 3"
echo line4 >> asdf; echo line4 >> file.py; git add .; git commit -m "Create commit 4"
git branch main-rebase # make a copy of main for the rebase
# make a branch off of commit 1 (3 commits ago)
git switch -c temp-branch @~3
# make a temp commit that modifies file.py
echo testing > file.py; git add .; git commit -m "wip: change file.py"
# rebase using "ours" strategy to remove all changes to file.py after commit 1
git rebase main-rebase~3 main-rebase --onto temp-branch -Xours
# remove the temporary wip commit from the branch
git rebase main-rebase~3 main-rebase --onto main-rebase~4
# remove the temp branch
git branch -D temp-branch
# show the full log
echo "Oneline graph log of all branches:"
git log --all --graph --oneline
echo "Show history of file.py on main"
git log main --oneline -- file.py
echo "Show history of file.py on main-rebase"
git log main-rebase --oneline -- file.py
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/491541.html
標籤:混帐 git-rebase git-rewrite-历史
上一篇:抑制Bash腳本中的分離頭警告
