When working with Git, you might encounter a situation where you want to:
"Make it as if this file was never committed in the first place."
If you simply delete the file from the current branch, you can use git rm and commit. However, this only removes the file from the current state; it remains in the past commit history.
This article introduces a method using interactive rebase (git rebase --committer-date-is-author-date -i) to remove changes related to a specific file from past commits.
Please note that this method rewrites the commit history. Be cautious when using it on branches that have already been pushed to a remote repository or are shared among multiple people.
1. Find the Commits Related to the Target File
First, identify the commits in which the file you want to remove was added or modified.
If you know the file path, run the following command:
git log --oneline --name-status -- path/to/file
If you don't know the exact name or path of the target file, you can check the commits and the files that were changed together:
git log --oneline --name-status
The important thing here is the status displayed on the left side of the file.
A path/to/file
M path/to/file
You mainly need to check the following two:
-
A: The file was newly added. -
M: An existing file was modified.
The approach will differ depending on this distinction.
2. Rebase from the Oldest Commit Related to the Target File
After identifying the commits related to the target file, find the oldest commit among them.
If that commit is earliest_commit_id, start the interactive rebase as follows:
git rebase --committer-date-is-author-date -i {oldest_commit_id}^
The ^ is added to include the target commit itself in the rebase range.
For example, if the oldest commit is abc1234, then:
git rebase --committer-date-is-author-date -i abc1234^
3. Change the Target Commit to drop or edit
When you run the command, an editor will open and display the list of commits.
pick abc1234 add config
pick def5678 update config
pick ghi9012 implement feature
From here, you will modify the commits related to the target file.
If the Commit Only Changes That File
Since the commit itself is unnecessary, change pick to drop.
drop abc1234 add config
This will remove the commit itself from the history.
However, do not use drop if the commit contains changes you want to keep.
If the Commit Includes Changes to Other Files
Since you want to keep the changes to other files, you cannot delete the commit itself.
In this case, change pick to edit.
edit abc1234 implement feature
When you start the rebase, the process will temporarily stop at that commit.
At this point, remove only the changes related to the target file.
4. Remove Changes to the Target File from the edit Commit
From here, the operation changes depending on whether the target file was newly added or an existing file was modified in that commit.
If the File Was Newly Added
If the file was newly added in that commit, delete the file.
git rm path/to/file
Then, recreate the commit.
git commit --amend
Modify the commit message as needed.
If an Existing File Was Modified
If the target file was modified in that commit, revert the target file to its state in the commit immediately before that commit.
git restore --source={commit_id}^ --staged --worktree -- path/to/file
For example, if the target commit is abc1234, then:
git restore --source=abc1234^ --staged --worktree -- path/to/file
This will remove the changes to the target file that were included in that commit.
Next, modify the commit.
git commit --amend
If there are any descriptions about the target file in the commit message, modify them here as appropriate.
5. Continue the Rebase
After finishing the commit modification, continue the rebase.
git rebase --continue
If there are multiple commits that you changed to edit, the process will stop again at the next edit.
Each time, repeat the following:
- Remove the changes to the target file.
-
git commit --amend. -
git rebase --continue.
If you complete all the steps, the rebase will be finished.
Finally, Check the History
After the rebase is complete, confirm that the history related to the target file is as intended.
git log --name-status -- path/to/file
Also, check:
git status
and
git log --oneline
to be sure about the overall state of the branch.
Caution: If You Have Already Pushed
git rebase --committer-date-is-author-date -i is an operation that rewrites past commits.
Therefore, if you have already pushed the target commit to a remote repository, the commit IDs will be different between the local and remote repositories after the rebase.
If you need to reflect the rewritten history in the remote repository, you will need to perform a force push.
git push --force-with-lease
It is safer to use --force-with-lease instead of --force, as it is less likely to overwrite changes that others have added to the remote repository.
However, rewriting the history of a shared branch will affect other developers. If you are working on a branch that is used by a team, be sure to check the scope of the impact before running it.
Summary
If you want to remove changes related to a specific file from the commit history, follow these steps:
Find the commits related to the target file.
↓
Start git rebase --committer-date-is-author-date -i from the oldest commit.
↓
If the commit is unnecessary, use drop.
↓
If it includes other changes, use edit.
↓
Remove only the changes to the target file.
↓
git commit --amend.
↓
git rebase --continue.
The key is to use drop if you can delete the commit, and edit if you only want to delete part of the commit.
Also, if using edit, check whether the target file was "newly added" or "modified to an existing file" in that commit before proceeding.
Using interactive rebase, you can remove changes related to a specific file from past commits while keeping other changes.
Gitのコミット履歴から特定のファイルだけ「なかったこと」にする方法
Gitで作業していると、
「このファイル、そもそもコミットしたこと自体をなかったことにしたい」
というケースがあります。
単純に現在のブランチからファイルを削除するだけなら git rm してコミットすれば済みます。しかし、それでは過去のコミット履歴にはファイルが残ったままです。
この記事では、interactive rebase(git rebase --committer-date-is-author-date -i)を使って、特定のファイルに関する変更を過去のコミットから取り除く方法を紹介します。
なお、この方法はコミット履歴を書き換えます。すでにリモートへ push しているブランチや、複数人で共有しているブランチで実行する場合は注意してください。
1. 対象ファイルに関係するコミットを探す
まず、削除したいファイルがどのコミットで追加・変更されたのかを確認します。
ファイルパスがわかっている場合は、次のコマンドを実行します。
git log --oneline --name-status -- path/to/file
対象ファイルの正確な名前やパスがわからない場合は、コミットと変更されたファイルをまとめて確認します。
git log --oneline --name-status
ここで重要なのが、ファイルの左側に表示されるステータスです。
A path/to/file
M path/to/file
主に確認するのは次の2つです。
-
A:ファイルが新規追加された -
M:既存ファイルが変更された
後ほど、この違いによって対応方法が変わります。
2. 一番古い対象コミットからrebaseする
対象ファイルに関係するコミットを洗い出したら、その中で一番古いコミットを確認します。
そのコミットを earliest_commit_id とすると、次のように interactive rebase を開始します。
git rebase --committer-date-is-author-date -i {oldest_commit_id}^
^ を付けているのは、対象コミット自身を rebase の範囲に含めるためです。
たとえば対象となる最古のコミットが abc1234 なら、
git rebase --committer-date-is-author-date -i abc1234^
となります。
3. 対象コミットをdropまたはeditに変更する
コマンドを実行すると、エディターが開いてコミット一覧が表示されます。
pick abc1234 add config
pick def5678 update config
pick ghi9012 implement feature
ここから、対象ファイルに関係するコミットを修正していきます。
そのファイルだけを変更しているコミットの場合
そのコミット自体が不要なので、pick を drop に変更します。
drop abc1234 add config
これによって、そのコミット自体が履歴から削除されます。
ただし、同じコミットに残したい変更が含まれている場合は drop してはいけません。
他のファイルの変更も含まれている場合
対象ファイル以外の変更は残したいので、コミットそのものを削除することはできません。
その場合は pick を edit に変更します。
edit abc1234 implement feature
rebase を開始すると、そのコミットのところで処理が一時停止します。
そこで、対象ファイルの変更だけを取り除きます。
4. editしたコミットから対象ファイルの変更を取り除く
ここからは、そのコミットで対象ファイルを新規追加したのか、既存ファイルを変更したのかによって操作が変わります。
ファイルを新規追加していた場合
そのコミットでファイル自体を新しく追加していたのであれば、ファイルを削除します。
git rm path/to/file
その後、コミットを作り直します。
git commit --amend
必要に応じてコミットメッセージも修正します。
既存ファイルを変更していた場合
そのコミットで対象ファイルに変更を加えていた場合は、対象ファイルだけをそのコミットの直前の状態に戻します。
git restore --source={commit_id}^ --staged --worktree -- path/to/file
たとえば対象コミットが abc1234 なら、
git restore --source=abc1234^ --staged --worktree -- path/to/file
とします。
これによって、そのコミットに含まれていた対象ファイルの変更を取り除くことができます。
続いてコミットを修正します。
git commit --amend
コミットメッセージに対象ファイルについての記述があれば、ここで適宜修正します。
5. rebaseを続行する
コミットの修正が終わったら、rebaseを続けます。
git rebase --continue
edit にしたコミットが複数ある場合は、次の edit の位置で再び処理が停止します。
そのたびに、
- 対象ファイルの変更を取り除く
-
git commit --amendする -
git rebase --continueする
という作業を繰り返します。
すべて処理できれば rebase は完了です。
最後に履歴を確認する
rebase が完了したら、対象ファイルに関する履歴が意図したとおりになっているか確認します。
git log --name-status -- path/to/file
あわせて、
git status
や、
git log --oneline
などでブランチ全体の状態も確認しておくと安心です。
注意:すでにpushしている場合
git rebase --committer-date-is-author-date -i は過去のコミットを書き換える操作です。
そのため、対象コミットをすでにリモートへ push している場合、rebase 後はローカルとリモートでコミットIDが異なる状態になります。
リモート側にも書き換え後の履歴を反映する必要がある場合は force push が必要になります。
git push --force-with-lease
--force よりも、他の人がリモートへ追加した変更を誤って上書きしにくい --force-with-lease を使うほうが安全です。
ただし、共有ブランチの履歴を書き換えると他の開発者にも影響します。チームで使用しているブランチの場合は、実行前に影響範囲を確認しておきましょう。
まとめ
特定のファイルに関する変更だけをコミット履歴から取り除きたい場合は、次の流れになります。
対象ファイルに関係するコミットを調べる
↓
一番古いコミットから git rebase --committer-date-is-author-date -i
↓
不要なコミットなら drop
↓
他の変更も含むなら edit
↓
対象ファイルの変更だけ取り除く
↓
git commit --amend
↓
git rebase --continue
ポイントは、コミット単位で消してよいなら drop、コミットの一部だけ消したいなら edit と使い分けることです。
また、edit の場合は対象ファイルがそのコミットで「新規追加されたもの」なのか「既存ファイルへの変更」なのかを確認してから操作します。
interactive rebase を使えば、他の変更を残したまま、特定のファイルに関する変更だけを過去のコミットから取り除くことができます。
Top comments (0)