gitgit-filter-branch

How to move a file from one git repository to another while preserving history


I am trying to move a single file (call it foo.txt) from one repository to another (unrelated) repository, preserving its history. ebneter's question shows how to do this for a subdirectory. taw's question has some hints and suggestions, but not a step-by-step procedure to follow. jkeating's question looked promising, but didn't work for me. Google searches came up empty for this particular use case. What I am looking for is a clear sequence of commands to accomplish this.

The sequence of commands I started to follow was this:

$ git clone source-repo/ source-repo-copy
$ cd source-repo-copy
$ git filter-branch --tree-filter 'test ! "$@" = "foo.txt" && \
  git rm --cached --ignore-unmatch $@ || true' --prune-empty

The logic of my filter command is to git rm all files that are not foo.txt. I added the || true to the command to force it to have a zero return value to satisfy filter-branch.

My intention was to set source-repo-copy as a remote for my target repository (target-repo), and assuming git filter-branch filtered out everything but foo.txt, fetch and merge source-repo-copy into target-repo. Unfortunately, the git filter-branch command seemed to have no effect. It ran with no errors and appeared to grind through the 600+ commits in source-repo, but when it finished, the git log and files in source-repo-copy looked the same. Shouldn't all files but foo.txt be missing and all commits that didn't touch it be gone from the log?

At this point I don't know how to proceed. Any suggestions?


Solution

  • This worked for me, but with a whole directory.

    As shown here

    ~$ cd neu
    ~/neu$ git filter-branch --subdirectory-filter FooBar HEAD
    ~/neu$ git reset --hard
    ~/neu$ git remote rm origin
    ~/neu$ rm -r .git/refs/original/
    ~/neu$ git reflog expire --expire=now --all
    ~/neu$ git gc --aggressive
    ~/neu$ git prune
    ~/neu$ git remote add origin git://github.com/FooBar/neu.git
    

    EDIT: For a single file:

    Filter the directory first:

     git filter-branch --prune-empty --subdirectory-filter myDirectory -- --all
    

    Filter the single file:

     git filter-branch -f --prune-empty --index-filter "git rm --cached --ignore-unmatch $(git ls-files | grep -v 'keepthisfile.txt')"
    

    Do some cleanup:

     git reset --hard
     git gc --aggressive
     git prune
    

    This should do it.