Two git repositories in one directory?
Asked Answered
B

12

126

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?

Bearded answered 12/1, 2009 at 17:1 Comment(2)
git subtree will get the job done.Lillalillard
If you're not dealing with too many files, you could create symlinks/junctions also.Breastplate
C
47

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.

Cly answered 13/1, 2009 at 4:24 Comment(2)
While submodules will also work, I think this is the best approach for me. The local files will follow a template, and as I make changes to the template on the MASTER branch, I can merge them into the machine local branches, incrementally updating the local config files. Thanks for the help!Bearded
use Mercurial and Git both.Irresolute
D
224

This article covers this relatively well:

https://github.com/rrrene/gitscm-next/blob/master/app/views/blog/progit/2010-04-11-environment.markdown

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.

Avoiding 2 Repos

You can accomplish a similar result by composing your branch, although you're still doing a fair bit of dancing to keep things going.

Suppose you have local files, like system-specific settings in the original question. You want to keep them out of your commits to other developers, but, you want them committed somewhere, as they're either essential to run, or, could be critical in debugging

Instead of 2 git repos you can do the following:

git checkout main
git checkout -b local
git add -A .
git commit -m "local changes"
git checkout main
git checkout -b chris
git checkout -b dev
git merge local

So, you started with local changes, that you put out on a branch called "local". You're never going to push this branch to the remote.

You set those changes aside, then made another branch off of main named with what you'll be pushing, for example named after you (in my case, "chris").

So far you have the 2 distinct branches, but, you don't have a way for them to work together. You make a third, Composed Branch - dev here is a composition of chris and local.

You do your dev in dev, then when it's time to push, you:

git checkout chris
git add -A .
git commit -m "My commit message"
git push

git checkout dev
git merge chris

This pushes the changes, clean out on chris, then comes back to dev and recomposes the branch. chris continues to remain squeaky clean, your meaningful changes are pushed, and the stuff you wanted to preserve locally stays safe on the local branch.

More special scenarios:

Pushing your local branch

Suppose you wanted to push your "local" branch afterall, for example if there's no important secrets in it and you're using git partly as a backup. Easy fix, just name the branch something long and very clear so no dev pulls it and expects it to work for them, like chris-local-settings. If you dislike typing that long name over and over (and tab completion isn't enough), branching the branch is super cheap:

git checkout chris-local-settings
git checkout -b local

Generated files

Suppose your local branch is more complex, and there are generated files that keep creating clutter. You need them to debug, but you never want to push them to your clean branch filled with just your, non-generated changes.

Your generated files probably follow a pattern, or if they don't, you probably have some say in where they generate when so they do follow a pattern. For example, if all you generated files appear in a folder named "bin" and you've just finished some important coding:

git checkout local
cd bin
git add -A .
git commit -m "Generated files"
cd ..
git checkout chris
git commit -m "Commit message about my clean handwritten changes"
git push
git checkout dev
git merge local
git merge chris

Because you know all your generated files are in the bin folder, you can just cd over and git add there. You cleanly dump them off in local, then back out to root and commit the rest to your clean branch. You then recompose your dev branch.

If in the future you need to know what your build looked like for a given day and time, you can recompose another branch, or, step back in time on the dev branch. That said, I usually throw out and recompose the dev branch often in my build, so, it has no history unfortunately:

Imagine some changes happened on main, and I need to verify they work before potentially poisoning my build:

git checkout main
git pull
git checkout dev
git checkout -b test
git merge main

Now I'd run tests to verify the build. If they go poorly:

git checkout dev
git branch -D test

And off I go to tell whoever last pushed to main, they broke it. Now I'm safe in dev away from the broken main branch. Or, if the tests go well:

git checkout chris
git merge main
git push
git branch -D dev
git checkout -b dev
git merge local

That turns dev into a clean merge of chris, which has the latest on main merged in, so those pulling the chris branch get what's latest on main plus my changes and nothing more - meaning I'm not throwing extra merge conflicts at them. But it does mean I keep losing history on dev.

Due answered 26/6, 2013 at 6:50 Comment(9)
Setting it as an alias throws this error: $ git config --global alias.pub '--git-dir=~/Server/www/.gitpublic' $ git pub add . fatal: alias 'pub' changes environment variables You can use '!git' in the alias to do this. Where would you set the "!git" in this case?Spirillum
@Spirillum Interesting - I was suggesting you use a Bash alias, not a git alias. Like alias gitone='git --git-dir=.gitone'Due
Should not be mv .git .gitone instead of git mv .git .gitone here? I get fatal: source directory is empty, source=.git, destination=.gitone with thisSubordinary
You can configure the repos to use their own exclude files and you can track those, i.e. gitone config core.excludesfile gitone.exclude and gitone add gitone.exclude. I made a script that expands on this solution: github.com/capr/multigitGrooms
@Spirillum I did it like this git config --global alias.youralias '!git --git-dir="/d/MyProject/_git"' then git youralias status :)Merry
If you make the assumption that .gitignore files are usually set-and-forget, then you can make different copies for each repo, then copy the relevant version to the directory as part of the alias. This applies to other files in the root that might conflict, such as README.md and .gitattributes.Breastplate
Does anyone know how to do this with atlassian sourcetree?Mensuration
To avoid doing the mv after git init, use git init --separate-git-dir=Nobles
Note that git init --separate-git-dir= will still require you to rm .git in order for you to run it again. Otherwise it will reinitialize the first repo in the new git folder you specify.Nepali
C
47

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.

Cly answered 13/1, 2009 at 4:24 Comment(2)
While submodules will also work, I think this is the best approach for me. The local files will follow a template, and as I make changes to the template on the MASTER branch, I can merge them into the machine local branches, incrementally updating the local config files. Thanks for the help!Bearded
use Mercurial and Git both.Irresolute
M
18

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.

Module answered 12/1, 2009 at 17:5 Comment(1)
No good for files that need to be on the root of you directory. Only chance there, is to fill the root of symlinks to these.Gorey
T
8

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).

Tanto answered 15/3, 2012 at 16:16 Comment(0)
E
7

It is possible by using the variable GIT_DIR but has many caveats if you don't know what you are doing.

Eldenelder answered 23/8, 2010 at 14:6 Comment(0)
M
5

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 .'

Monseigneur answered 21/3, 2014 at 18:44 Comment(0)
B
5

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.

Balkan answered 11/12, 2021 at 18:15 Comment(0)
S
4

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.

Stadium answered 12/1, 2009 at 18:30 Comment(0)
A
2

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:

  1. Repo1/FolderA
  2. Repo1/FolderB

And:

  1. 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.

Ablution answered 4/12, 2016 at 3:22 Comment(0)
M
1

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.

Murrell answered 7/12, 2017 at 0:9 Comment(0)
W
1

OP wants merging files from multiple repositories to single directory.

But if you do not want mering and instead need switching between repositories in single directory:

mkdir repo
cd repo
git init
git remote add first https://first/repo.git
git remote add second https://second/repo.git
git fetch first
git fetch second
git checkout first/master -b master

Now you can switch between directories and even cherry-pick commits between repositories.

Wimbush answered 11/1, 2023 at 12:56 Comment(0)
M
-1

I see a lot of answers, but I have a simpler solution:

git remote rename origin old-origin
git remote add origin [email protected] # your git repository
git push -u origin --all
git push -u origin --tags

And now you have two gits in one repository!

In gitgraken you will see these two gits like this:

enter image description here

Mascagni answered 30/8, 2023 at 13:37 Comment(1)
I won't downvote you to be polite, but, this does not solve the problem in the Question. You are explaining a basic feature of Git, which is to have multiple remotes on a given Repository. This question is about having multiple git Repositories in the same folder/directory.Due

© 2022 - 2024 — McMap. All rights reserved.