How to move a file from one git repository to another while preserving history
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.
Related videos on Youtube
Comments
-
Randall Cook 8 months
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 ! "[email protected]" = "foo.txt" && \ git rm --cached --ignore-unmatch [email protected] || 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, thegit 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, thegit 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?
-
tripleee about 7 yearsPossible duplicate of How to move files from one git repo to another (not a clone), preserving history
-
-
Randall Cook almost 11 yearsYes, the
--subdirectory-filter
examples all seem straightforward, but I'm looking to move a single file, not a subdirectory. Any ideas with that? -
blang almost 11 yearsplease try it on a testrepo first, the new solution works for me
-
Randall Cook almost 11 yearsI filtered the directory as you suggested, using '.' instead of 'myDirectory'. I was left with zero files in the directory (just .git).
git log
showed only merge commits (12 of them). I then filtered the single file. I got errors becausegit rm
was being called with an empty parameter list. I added|| true
to the end of the command to prevent git rm's complaints from aborting the filter. After that, still zero files, and only 4 merge commits ingit log
. I finished up withgit reset/gc/prune
but saw no change. I guess it didn't work. :( I'm glad I tried this on a test repo. :) -
blang almost 11 yearsPlease upload a testrepo with your structure on github or something. (Or tell me the hierachy). If the git consists of testDir/myFile{1..3}.txt it works fine. The filter for the single file can't work if ur directory is empty, try to irgnore the directory filter and step on the filter the single. There's a chance your shell doesn't recognize the command well. git filter-branch -f --prune-empty --index-filter 'git rm --cached --ignore-unmatch $(git ls-files | grep -v "keepFile.txt")'
-
Randall Cook almost 11 yearsJust after I posted the question I went on vacation, and haven't been able to test this answer thoroughly. Still, I appreciate blang's work. You get the check mark. :)
-
Kyle Strand about 8 yearsTortoiseGit makes this trivial, at least in Windows (right-click-drag the files from one explorer window to another, and select
Git Copy and add files to this WC
). Any idea how this is implemented under the hood, and whether it can be done similarly at the command line? I'd be really surprised if it removes and then restores theorigin
, or makes a temporary clone, though I guess it's possible... -
Micah Smith over 7 yearsIt wasn't clear to me at first the steps to take for a single file rather than directory. This is my understanding, for anyone similarly confused: The two
filter-branch
commands in the EDIT should replace the onefilter-branch
command in the original code. Then, the "Do some cleanup" commands are actually included in the rest of the original code section and should be run as such. Finally, you need togit push origin new-branch
. -
Dolphin about 4 yearsThis can also be used for more than one file by modifying the
grep
statement with an or:grep -v 'file1\|file2\|file3'