Two git repositories in one directory?
GitGit SubmodulesGit SubrepoGit Problem Overview
Is it possible to have 2 git repositories in one directory? I'd think not, but thought I'd ask. Basically, I'd like to check in my home directory config files (e.g. .emacs) which should be common across all of the machines I work on, but have a second repository for local files (e.g. .emacs.local), which contains machine-specific configurations. The only way I can think of to do that is to have the local config in a subdirectory and ignore that subdirectory from the main git repository. Any other ideas?
Git Solutions
Solution 1 - Git
This article covers this relatively well:
Basically if you're working from the command-line this is simpler than you might guess. Suppose you want 2 git repos:
.gitone
.gittwo
You could set them up like so:
git init .
mv .git .gitone
git init .
mv .git .gittwo
You could add a file and commit it to only one like so:
git --git-dir=.gitone add test.txt
git --git-dir=.gitone commit -m "Test"
So the options for git come first, then the command, then the git command's options. You could easily enough alias a git command like:
#!/bin/sh
alias gitone='git --git-dir=.gitone'
alias gittwo='git --git-dir=.gittwo'
So you can commit to one or the other with a bit less typing, like gitone commit -m "blah"
.
What appears to get trickier is ignores. Since .gitignore normally sits in the project root, you'd need to find a way to switch this as well without switching the entire root. Or, you could use .git/info/exclude, but all the ignores you perform then won't be committed or pushed - which could screw up other users. Others using either repo might push a .gitignore, which may cause conflicts. It's not clear to me the best way to resolve these issues.
If you prefer GUI tools like TortoiseGit you'd also have some challenges. You could write a small script that renames .gitone or .gittwo to .git temporarily so these tools' assumptions are met.
Solution 2 - Git
If I understand what you're doing, you can handle it all in one repository, using separate branches for each machine, and a branch containing your common home directory config files.
Initialize the repo and commit the common files to it, perhaps renaming the MASTER branch as Common. Then create a separate branch from there for each machine that you work with, and commit machine-specific files into that branch. Any time that you change your common files, merge the common branch into each of the machine branches and push to your other machines (write a script for that if there are many).
Then on each machine, checkout that machine's branch, which will also include the common config files.
Solution 3 - Git
Have a look at git submodule.
> Submodules allow foreign repositories > to be embedded within a dedicated > subdirectory of the source tree, > always pointed at a particular commit.
Solution 4 - Git
It is possible by using the variable GIT_DIR
but has many caveats if you don't know what you are doing.
Solution 5 - Git
RichiH wrote a tool called vcsh which a tool to manage dotfiles using git's fake bare repos to put more than one working directory into $HOME. Nothing to do with csh AFAIK.
However, if you did have multiple directories, an alternative to git-submodules (which are a pain in the best of circumstances and this example usage is not the best of circumstances) is gitslave which leaves the slave repos checked out on the tip of a branch at all times and doesn't required the three step process to make a change in the subsidiary repo (checkout onto the correct branch, make & commit the change, then go into the superproject and commit the new submodule commit).
Solution 6 - Git
Yeah, submodules are probably what you want. Another option would be to have your working copy in a subdirectory and then point symlinks from you home directory to the files of interest.
Solution 7 - Git
my preferred method is using a repo in a subdir, and use recursive symbolic links:
git clone repo1
cd somerepo
git clone repo2
cd repo2
./build
where the 'repo/build'-file looks like:
#!/bin/bash
SELF_PATH="$(dirname "$(readlink -f "$0")" )" # get current dir
cd .. && git stash && git clean -f -d '' # remove previous symlinks
cp -sR "$SELF_PATH"/* ../. # create recursive symlinks in root
caution: dont use 'git add .'
Solution 8 - Git
The other option is to they on separate folders and create symbolic hard links from one folder to the other.
For example, if there are the repositories:
- Repo1/FolderA
- Repo1/FolderB
And:
- Repo2/FolderC
You may symlink the folders FolderA
and FolderB
from the Repo1 into the Repo2. For windows the command to run on the Repo1 would be:
User@Repo1$ mklink /J FullPath/Repo2/FolderA FullPath/Repo1/FolderA
User@Repo1$ mklink /J FullPath/Repo2/FolderB FullPath/Repo1/FolderB
User@Repo1$ printf "/FolderA/*\n/FolderB/*\n" >> .gitignore
For the files on the main repositories you would need to symlink each one of them, also adding them to repository .gitignore
to avoid noise, unless you want to it.
Solution 9 - Git
Yes, it is possible to have two git repositories in one directory.
I'm assuming that one remote repository is in GitHub and the other in GitLab. I'm also using two different SSH keys to connect to these remote repositories.
You can have both remote repositories in one of GitHub / GitLab (and use a single SSH key) - not much would change.
Pre-requisites:
-
Public SSH keys (
id_ecdsa.pub
/id_rsa.pub
/id_ed25519.pub
, etc.) are present in your GitHub and GitLab profiles -
Private SSH keys (
id_ecdsa
/id_rsa
/id_ed25519
, etc.) are added and persisted in your OS's keychain -
SSH config file has keys specified for GitHub and GitLab:
Host github.com Hostname github.com AddKeysToAgent yes UseKeychain yes IdentityFile ~/.ssh/id_ecdsa Host gitlab.com Hostname gitlab.com AddKeysToAgent yes UseKeychain yes IdentityFile ~/.ssh/id_rsa
Here's a break down of Chris's answer emulating a workflow:
-
Initialize git in a directory:
git init
-
Connect git to one remote repository (located in GitHub)
git remote add origin [email protected]:your-username/your-repo.git
-
Rename
.git
to something like.github
mv .git .github
-
Initialize git again
git init
-
Connect git to the other remote repository (located in GitLab)
git remote add origin [email protected]:your-username/your-repo.git
-
Rename
.git
to something like.gitlab
mv .git .gitlab
-
Verify that current directory is connected to two different remote repositories
git --git-dir=.github remote -v
git --git-dir=.gitlab remote -v
-
Pull remote (GitHub and GitLab) repositories
git --git-dir=.github pull origin main git --git-dir=.gitlab pull origin main
-
Add a file to both repositories
git --git-dir=.github add README.md git --git-dir=.gitlab add README.md
-
Write commit message
git --git-dir=.github commit -m "operational overview" git --git-dir=.gitlab commit -m "operational overview"
-
Push to remote
git --git-dir=.github push -u origin main git --git-dir=.gitlab push -u origin main
The only additional thing we're doing here is using the --git-dir
flag.
If you plan on doing this frequently you could add an alias in your shell config file (like .zprofile
, bashrc
, etc.):
export github="git --git-dir=.github"
export gitlab="git --git-dir=.gitlab"
Future operations like pull
, push
, add
, commit
can be performed like - github pull origin main
, gitlab pull origin main
, etc.
Solution 10 - Git
Disclaimer: This is not advertising. I'm the developer of the provided library.
I've created a git extension to handle cases where you want to mix multiple repositories into one folder. The advantage of the lib is, to keep track of the repositories and file conflicts. you can find it on github. There are also 2 example repositories to try it out.