簡體   English   中英

重寫歷史記錄以撤消對 git 中當前分支上文件的所有更改

[英]rewrite history to undo all changes to a file on current branch in git

假設我有一個修改六個文件的功能分支。 該分支上的大多數提交都涉及file.py 我最終意識到有一種更好的方法可以實現此功能,而無需觸及file.py 有沒有辦法調整我分支上的所有提交以不觸及該文件? 我覺得應該有一些交互式 rebase 的技巧可以很容易地做到這一點,但我不確定那會是什么技巧。

當然,我可以簡單地將更改還原為新的提交,但為后代混淆歷史似乎很遺憾。

在分支的“開始”之前一直執行交互式變基(即,到該文件仍然是您想要的方式的最近可訪問的提交)。 我們稱之為提交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

這正是想要的結果。

這可以通過在變基期間使用自動沖突解決來自動化。 該算法可以像這樣工作:

  1. 在提交處創建一個分支,其中包含相關文件的最后更改。
  2. 在該分支上創建一個新的提交,將文件的全部內容替換為獨特的東西(甚至可能什么都沒有)。 這里的重點是確保您希望忽略的所有新更改肯定會與此更改發生沖突。
  3. 將您的主分支重新定位到( --onto )新的臨時分支上,並使用合並策略“我們的”( -Xours )。
  4. 現在,使用交互式 rebase 或通過另一個 rebase --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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM