Tomes
Tomes

Reputation: 185

Apply change to all commits

I did a terrible mistake at a current project. When I created my java project, I used a wrong subpackage.

The best case scenario would enable me to rename the package at my first commit and transitive change all following commits to the correct package name.

The reason is that I reference the commits and packages in a scientific paper. Commiting the rename now would be very unfortune.

Is there a way to apply this change transitive, so I dont have to edit all commits by hand? All commits are already pushed to the remote.

I am the only author and have all administrive privileges. I want to preserve the commit history - meaning i do not want to squash all commits. Their identity can be changed if necessary.

EDIT:

I applied the required change with both approaches and want to summarize for future reference.

The rebasing approach via git rebase -i is very tedious for many commits. One has to apply the neccesary changes on all commits to all files and all directories. Due to the number of commits in my case, I was not able to do it without any mistakes, resulting in a few squashed commits. Without the capabilities of grep, find or my editor, I would not have succeeded.

The command git filter-branch as suggested by the answer https://stackoverflow.com/a/66122950/6783797 did the job reliably. The changes to apply were:

  1. rename directories
  2. find files where I need to change the content

Renaming the directories:

JAVADIR="$PWD/path/to/parent/package"
if [ -d "$JAVADIR/wrong" ]; then
  mv "$JAVADIR/wrong" "$JAVADIR/right"
fi

To find the appropriate files, i piped find and sed -i 's/wrong/right/g' from the accepted answer. Assembling these together gave: git filter-branch --tree-filter "sh /path/to/rename_script.sh; find . -type f \( -iname '*.java' -o -iname '*.xml' \) | xargs -r sed -i 's/wrong/right/g'"

What I ended up with was a diverged branch, which I wanted to use on my remote. A quick search offered me this answer: https://stackoverflow.com/a/40201484/6783797

Upvotes: 1

Views: 253

Answers (1)

Marco Luzzara
Marco Luzzara

Reputation: 6036

As @WilliamPursell suggested, in your case filter-branch is the most suitable command. It is a very performant and flexible tool provided by git, but pay attention because it is dangerous if used inappropriately.

First of all, filter-branch rewrites the history, the whole history if needed, and accepts several options. In your case the --tree-filter because you need to change the content of one or more file I guess. The old commits are not lost forever, but they are still reachable because the old HEAD is still pointed by refs/original/refs/heads/your_branch. You can see the old commits using git log --all. Given that you are alone on that project is certainly an advantage, no one is going to complain that their history, or part of it has been rewritten.

Honestly, I do not know if the backup branch created after the filter-branch command stays there forever or after sometime it is automatically gced. I am sure that you cannot do another filter-branch before deleting that pointer, unless you force it -f.

After all these warnings, you probably are going to run something similar to this:

git filter-branch --tree-filter "sed -i 's/import my.wrong.package/import my.right.package;/g' target_file"

Upvotes: 2

Related Questions