3

I have a project with the following structure:

.
└── maindir
    ├── subdir1
    └── subdir2

I started the project by setting a git repo in both subdir1 and subdir2. The project has grown bigger and now I had to create a parent dir maindir and created a git repo in it too. However now it seems that the change made in subdir1 and subdir2 are not tracked by the git repo of maindir. Moreover, for simplicity reason, I want to merge all the commit info of all the git repos into one single git repo, namely the one of maindir. How can I do that?

EDIT 1

So I tried the answer of the link (Git: Combining two independent repos to one without losing the history) posted in the comment and got the following output. It seems to be more complicated than in the other SO question. What shall I do next?

$ git pull subdir1
remote: Enumerating objects: 58, done.
remote: Counting objects: 100% (58/58), done.
remote: Compressing objects: 100% (54/54), done.
remote: Total 58 (delta 25), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (58/58), 9.96 KiB | 231.00 KiB/s, done.
From standard_deviation
 * branch            HEAD       -> FETCH_HEAD
hint: Pulling without specifying how to reconcile divergent branches is
hint: discouraged. You can squelch this message by running one of the following
hint: commands sometime before your next pull:
hint:
hint:   git config pull.rebase false  # merge (the default strategy)
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
fatal: refusing to merge unrelated histories

Edit 2

I am trying the git filter-repo package and trying to follow the commands but there are some details I do not understand. Therefore I anoted it to see if I understood it correctly

cd /path/to/subdir1 # now at level of subdir1
cd .. # now at level of maindir
mv subdir1 maindir # while at level of maindir, now renaming subdir1 to maindir, so there is maindir containing another maindir
git filter-repo --to-subdirectory-filter subdir1/ # using the package
cd ../subdir2 # This is what I do not understand: why the `..`? because we should still be at the level of the first maindir. What am I not getting right?
git filter-repo --to-subdirectory-filter subdir2/ # did not try from this point on
cd ../maindir # did not try
git remote add -f s2 ../subdir2 # did not try
git merge --allow-unrelated-histories s2/main # did not try
git remote remove s2 # did not try
ecjb
  • 5,169
  • 12
  • 43
  • 79
  • Does this answer your question? [Git: Combining two independent repos to one without losing the history](https://stackoverflow.com/questions/46304870/git-combining-two-independent-repos-to-one-without-losing-the-history) – evolutionxbox Feb 04 '23 at 20:42
  • Thank you for your comment @evolutionxbox. The link you posted seems very interesting. However, the solution seems more complicated. I edited the question with the details. – ecjb Feb 04 '23 at 21:02

2 Answers2

1

One option would be to reference subdir1 and subdir2 as submodules inside maindir.

  • move subdir repositories outside of maindir.
  • add them back as submodules:
cd /path/to/maindir
mv subdir1 ..
mv subdir2 ..
git rm --cached subdir1
git rm --cached subdir2
git commit -m "remove nested Git repositories"

git submodule add https://url/subdir1/remote/repository subdir1
git submodule add https://url/subdir2/remote/repository subdir2
git commit -m "Reference subdir1 and subdir2 repository inside maindir"
git push

Note: the git rm --cached is not needed if you never git add'ed the nested git repositories in maindir. But you still need to move them out of the way.
And this assumes the subdir repositories have been pushed to their corresponding remote repositories (on GitHub, GitLab, Bitbucket, ...)


The other option, to avoid the "unrelated history", is to transform one of your subdir repository into maindir. And importing the second one.
This uses git filter-repo (which needs to be installed first).
And it follows this gist, using a path shortcut option:

--to-subdirectory-filter <directory>

Treat the project root as instead being under <directory>.
Equivalent to using --path-rename :<directory>/.

So:

cd /path/to/subdir1
cd ..
mv subdir1 maindir
cd maindir
git filter-repo --to-subdirectory-filter subdir1/
cd ../subdir2
git filter-repo --to-subdirectory-filter subdir2/
cd ../maindir
git remote add -f s2 ../subdir2
git merge --allow-unrelated-histories s2/main
git remote remove s2

This time:

  • you do not depend on remote repositories (GitHub, GitLab, Bitbucket...)
  • you do not use submodules (which are references to repositories)
  • meaning you end up with one single git repo (instead of one repository referencing two other repositories)
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Thank you very much @VonC for your very extensive answer. I actually don't work with remote repositories currently. Everything is local on my computer. I think I would like to go to the `filter-repo` path, but so far could not make it work. What I did: I downloaded the file (https://raw.githubusercontent.com/newren/git-filter-repo/main/git-filter-repo), named it `git-filter-repo.py` and wrote the following in my `.zshrc`: `export PATH=$PATH:/path/to/git-filter-repo.py` and then `source ~./zshrc`. But when I try `git filter-repo` command it doesnt work. Any idea about how I should proceed? – ecjb Feb 05 '23 at 12:25
  • @ecjb It is best to follow the [installation instruction](https://github.com/newren/git-filter-repo/blob/main/INSTALL.md). In particular, the "making sure to preserve its name (`git-filter-repo`, **with no extension**)" bit. – VonC Feb 05 '23 at 16:57
  • Thanks for your comment @VonC. So I tried part of the commands line that you showed but there are some points I did not get right. I edited the question again accordingly. Do you see what am I missing? – ecjb Feb 05 '23 at 21:21
  • @ecjb I missed a `cd maindir`. I have edited the answer. – VonC Feb 05 '23 at 21:28
1

Using git subtree:

git init maindir
cd maindir

# At least 1 commit is required
# because `git subtree add` needs an existing branch
git commit --allow-empty -m "Initial commit"

git subtree add --prefix=subdir1 URL-for-repo1 master
git subtree add --prefix=subdir2 URL-for-repo2 master

For an existing repository instead of git init + git commit just do git clone.

phd
  • 82,685
  • 13
  • 120
  • 165
  • Dang, so much simpler than the merge with unrelated histories I've been using. I wish I new about this a few weeks ago! – joanis Feb 23 '23 at 02:26