gitrepository-design

Move file to separate repository without moving file on disk


This question is related to, e.g., How to move files from one git repo to another (not a clone), preserving history Detach (move) subdirectory into separate Git repository but the problem (or at least the solution, I think) is different. I have the following directory structure:

abc/
    .git/
    def/
       file1.txt
       file2.txt
    xyz/
       file3.txt

The directory abc is the root of a git repository, say repo1.

What I want is to put file2.txt in a different repository, repo2 (local and linked to a remote). However, I do not want to move file2.txt out of the directory (as in the related questions). All other files and folders (def, file1.txt, xyz/*) have to stay in repo1.

When I do git commit or git push, changes to file1.txt should go to repo1 and changes to file2.txt should go to repo2.

As a workaround, I could make an additional directory in def and move file2.txt there, if that makes it any easier. However, I don't want to move file2.txt out of the current tree.

The history of file2.txt does not necessarily need to be preserved in this move.


Solution

  • First of all, make sure you commit or stash any change and your work tree is clean. Run git status to be sure.

    It's possible to accomplish your goal in two easy steps.

    1. Create a new empty repo in abc/def. Configure it to ignore everything but file2.txt (an any other file you want to process the same way as file2.txt). Add .gitignore and file2.txt to the new repo and commit:

      $ cd abc/def
      $ git init
      $ echo '*' > .gitignore
      $ echo '!/file2.txt' >> .gitignore
      $ git add .
      $ git status
      $ git commit
      

      Read carefully the output of git status and verify it added only the desired files to the repo. Commit if everything is ok or tweak .gitignore and the index until they reach the state you need.

    2. Go to the abc repo, add def/file2.txt and def/.gitignore to the ignore list of this repo, remove def/file2.txt from the index and commit:

      $ cd ..
      $ echo '/def/file2.txt' >> .gitignore
      $ echo '/def/.gitignore' >> .gitignore
      $ git rm --cached def/file2.txt
      $ git status
      $ git commit
      

      Again, read the output of git status before committing.

    Remarks

    You can work with both repos as usual. They are independent, they are not linked in any way (apart from the fact that each repo is configured to ignore the files managed by the other one).

    If you don't push the inner repo to a remote location, I recommend you to move its .git directory (the repository itself) somewhere outside the abc directory to prevent its accidental removal. The command is simple. Inside the abc/def directory run:

    git init --separate-git-dir=/path-where-to-move-the-git-dir
    

    You can run this command line when you create the abc/def repo or you can run it after that. The data contained in the repo is safe. It only moves the repo to the indicated location.