8

I know that I can create a new repository out of a directory of a git repository. See here: https://help.github.com/articles/splitting-a-subfolder-out-into-a-new-repository/

However, how can I copy a directory from one repository to a new directory of another completely different repository, while keeping history of that directory?

Update: is it possible for that history to show up with git log?

Potherca
  • 13,207
  • 5
  • 76
  • 94
namin
  • 37,139
  • 8
  • 58
  • 74

1 Answers1

8

You could do this with git filter-branch. Basically you'd want to:

  1. Split out the subpath in the first project into a new repository, using the link you've already found.
  2. Push it to the second project's remote on a unique branch.
  3. Fetch that branch into the second repository, then use git filter-branch to index-filter it into the correct subdirectory:

    git filter-branch --index-filter '
        git ls-files -sz | 
        perl -0pe "s{\t}{\tnewsubdir/}" |
        GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
            git update-index --clear -z --index-info &&
            mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
    ' HEAD
    
  4. Finally, checkout the master branch of the second project (or whatever branch you're using), then merge in the newly-filtered branch.

Really not too awful an operation, all told. As AlexanderGladysh notes in the comments, you could also use a subtree merging strategy in the place of steps 3 and 4.

namin
  • 37,139
  • 8
  • 58
  • 74
Christopher
  • 42,720
  • 11
  • 81
  • 99
  • Couldn't you use a subtree merge strategy instead of steps 3 and 4? – Alexander Gladysh Sep 08 '12 at 01:40
  • @AlexanderGladysh Yes. You could do it with a subtree merging strategy. I edited the answer accordingly. – Christopher Sep 08 '12 at 01:45
  • Hmm. I decided to go with a subtree merge strategy. One issue is that git log on that directory only shows the last merging commit not the whole history :(. Is that expected? – namin Sep 09 '12 at 17:03
  • Regarding 3 above, --from-scratch doesn't exist in my system. – namin Sep 09 '12 at 17:35
  • The solution with filter-branch seems to work with some tweaks: in particular, after 3, I now have duplicated each file into . and newsubdir. – namin Sep 09 '12 at 19:51
  • @namin Yes. Only showing the merge commit as history in `git log` is a byproduct of the subtree merge strategy. It's actually why I recommended `filter-branch` instead of subtree merge. – Christopher Sep 10 '12 at 15:43
  • @namin Fixed the flags on `update-index` and the command generally. I'd copied that command from a git list thread that required a specific patch to git. Sorry about that. The `--index-info` flag should work with the above modifications. If it doesn't post here and I'll try it directly. Basically it should be identical to the `filter-branch` man page but use `perl` instead of `sed`. `sed`'s interpretation of `\t` changes depending on your OS. – Christopher Sep 10 '12 at 15:50
  • @Christopher could you please explain what is being done in step 3? And do I need perl to execute this command? – Maheep Feb 28 '18 at 18:37
  • @Maheep -- step 3 iproceeds through the repositories history, writing the git index out to a new file while substituting in the new directory path with each commit. It then uses a plumbing command called 'git update-index' to update git's index at each commit to inject the new, substituted index. You don't need 'perl'. It'll also work with 'sed'. Just mind your tab-characters (if you're on a mac, you've got BSD sed, if you're on a linux system, probably GNU). – Christopher Apr 27 '18 at 17:16