gitgit-filter-repo

git replace file content in history


I would like to replace the content of a file in all branches with the current visible version of that file. I have been trying something like this, but it does not work:

FILE_PATH="$1"

cp "$FILE_PATH" "/tmp/file_to_replace_in_git_history"

git filter-repo --path "$FILE_PATH" --force --blob-callback "
  if filepath == b'$FILE_PATH':
    with open('/tmp/file_to_replace_in_git_history', 'rb') as f:
      blob.data = f.read()
"

rm "/tmp/file_to_replace_in_git_history"

I wondering, but I could not find any ready to use solution. Probably you have a better idea.

I want to remove encrypted files which could contain passwords etc, which should not be there. So the files are binary and should be replaced complete.


Solution

  • Replacing entire file content seems to be the area where git filter-repo has problems. Blob callback doesn't know filename, --replace-text can only replace line by line AFAIU.

    I can solve your task with git filter-branch. Yes, I know it's outdated and not recommended. It works on one branch at a time. I use --tree-filter which is extra slow because it checks out every commit, updates it and commits it back. But still here is the working solution:

    FILE_PATH="$1"
    
    cp "$FILE_PATH" "/tmp/file_to_replace_in_git_history"
    
    FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch --tree-filter "
        cp '/tmp/file_to_replace_in_git_history' '$FILE_PATH'
    " HEAD
    
    rm "/tmp/file_to_replace_in_git_history"
    

    After that run git log -- "$FILE_PATH" — the log should contain only one commit, the initial commit.

    Upd. Process all branches and copy tags:

    FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch --tree-filter "
        cp '/tmp/file_to_replace_in_git_history' '$FILE_PATH'
    " --tag-name-filter cat -- --all