0

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.

Community
  • 1
  • 1
Marijn
  • 1,640
  • 14
  • 24
  • The closest thing of which I'm aware which could get close to what you want would be to use a submodule which would contain `file2.txt`. – Tim Biegeleisen Feb 08 '17 at 13:17

1 Answers1

1

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.

axiac
  • 68,258
  • 9
  • 99
  • 134
  • Nice, I didn't know the option to create a repository in a separate directory. I notice it's creating a file `.git` which contains the path to the actual git directory. Would this file also needed to be ignored by the "host" repository, like `.gitignore` and `file2.txt`, or is it handled specially? – mkrieger1 Feb 08 '17 at 14:34
  • Worked like a charm, thanks! I guess I have to manually change both `.gitignore`s when I rename a file? – Marijn Feb 08 '17 at 14:50
  • *" I notice it's creating a file .git which contains the path to the actual git directory."* -- that's what the [documentation of `git init`](https://git-scm.com/docs/git-init#git-init---separate-git-dirltgitdirgt) says too. I didn't find an official reference yet but it seems Git knows that a file or a folder named `.git` in the working tree makes its parent directory a Git repo and it ignores the `.git` file or directory without being told so. The Git handling of [submodules](https://git-scm.com/docs/git-submodule) also use this technique. – axiac Feb 08 '17 at 14:53
  • Indeed, you have to change both `.gitignore` files if you want to rename the file. Remember that the file `abc/.gitignore` tells the outer repo what files to ignore and the file `abc/def/.gitignore` tells the inner repo what files to **not** ignore. – axiac Feb 08 '17 at 14:54
  • Alternatively, you can put the name of `file2.txt` in [`.git/info/exclude`](https://git-scm.com/docs/gitignore) (instead of `.gitignore`) in one or both repos. The advantage is that you don't need to change and commit `.gitignore` anymore if you add/remove/rename files in the inner repo. The disadvantage is that `.git/info/exclude` is local to the repo. It cannot be pushed to a remote repository. – axiac Feb 08 '17 at 14:59