1

What are the steps to transfer selected files, along with their history, from one project to another in Git? For example, I have

Project A
    File A.1
    File A.2
    File a.3
    File a.4

Project B
    File B.1
    File B.2

which are unrelated (through Git) and I want to end up with

Project B
    File B.1
    File B.2
    File A.1
    File A.2

along with full histories for A.1 and A.2, but none for a.3 or a.4, (assume for simplicity I don't care whether A.1 and A.2 also leave histories in Project B). What what are the steps involved? I can gather from reading here and elsewhere that this is possible, but I'm having trouble pasting the bits and pieces into a step by step workflow.

orome
  • 35,904
  • 38
  • 156
  • 345
  • Is `Project B` a fork, branch, or clone of `Project A`? – Sam Jan 06 '14 at 19:50
  • @SamSullivan: Good question. Project B is distinct from Project A: no relationship through Git. Edited. – orome Jan 06 '14 at 19:53
  • I'd try creating a new repo of `Project B`, and then using some ideas from [this question](http://stackoverflow.com/questions/1425892/how-do-you-merge-two-git-repositories) to merge `Project A`'s history into `Project B`.. – Sam Jan 06 '14 at 19:55
  • That will give me all of A in B though, right? – orome Jan 06 '14 at 21:04
  • Oh yea..there is no great way to do this. You could *potentially* checkout a new branch on `Project B`, merge in `Project A`, remove unwanted files (everything but `File A` and `File B`), then merge this temporary branch into your main `Project B` branch. – Sam Jan 06 '14 at 21:15
  • @SamSullivan: That sounds like part of the solution. But the question is: what's the procedure, and how do I eliminate history for the files I don't want to move over? – orome Jan 09 '14 at 04:28
  • I just posted an answer that does step-by-step and includes eliminating history. – Sam Jan 09 '14 at 04:51

1 Answers1

1

Dislamer: it is probably good to push both repositories to a remote host like GitHub or BitBucket before attempting. I just tried locally and all went well, but no guarantees.

This is a multi-step process, and it may not be a perfect solution. Starting with 2 local repositories, Project A and Project B. They may have some overlapping files and both may have some history. The end goal is to merge Project A into Project B, keep the history, but completely remove select files from the final repo.

Start by merging Project A into Project B. To do this, we will have add Project A as a remote branch to Project B.

git remote add old /path/to/project_a   # old is the remote name
git fetch old <branch>                  # fetch history from Project A (can specify single branch after)
git merge old/<branch>                  # merge the Project A branch into your working tree (most likely master)

Now you will have a merged Project A and Project B in Project B's working tree. You may have merge conflicts to resolve (if so, commit after this).

Next step is to remove our unwanted files (or directories) from this combined repository. git filter-branch will be used to do this historically. Note: this will rewrite history, so if someone is working off of Project B there will be major conflicts and is not recommended (instead, just remove the files normally and make an extra commit..this will keep history sane).

git filter-branch --tree-filter 'rm -rf path/to/unwanted/dirs' HEAD
git filter-branch --tree-filter 'rm -f unwanted_file' HEAD

This translates to run this command in every commit of this tree (branch) up until the HEAD (working copy). You can also specify a range by replacing it HEAD with a1b2c3...HEAD. The -r in the first command is needed to recursively remove files from an unwanted directory. The -f is needed in both commands to ignore non-existent files (some commits may not have the file you want to remove, since the commits come from 2 repositories).

Finally you can cleanup by removing the link between Project B and the old Project A.

git remote rm old    # or whatever you named this branch
Sam
  • 18,756
  • 2
  • 40
  • 65
  • 1
    FWIW: For `filter-branch` I had to make sure that I'd simplified the structure of my project considerably (removing any remotes and paring down to a single branch). And I ended up using different flags (neither those above nor the ones I found in other answers worked smoothly). But that's all probably down to some peculiarities of my project structure. I also reversed the steps, prunig (a copy of) A before merging into B. – orome Jan 14 '14 at 18:23
  • Good notes @raxacoricofallapatorius. I haven't ever used `filter-branch` in a real scenario, so not sure what up with it..but I'm glad you were able to get everything sorted. – Sam Jan 14 '14 at 19:22