简体   繁体   中英

Can I create a remote git repository with a local repository inside?

I am required for a university course to handle a git folder of a local repo for every assignment I do, but I would like to have the folder containing all the projects as a remote repository. Is that possible? Because I tried and gitlab says the data inside the folder with the local repo is inaccesible. I am thinking of using a.gitignore file, but I don't know if that would do the job or only waste more time.

You can get close, but not quite there. Specifically, a repository cannot contain a repository. (A work-tree can, but a repository cannot.)

In normal use, a Git repository is stored within a subdirectory named .git . Let's define these terms:

  • Work-tree: the place in the file system where you do your work.
  • Top level: the path to this work-tree, without any additional subdirectories.
  • Path component: see below.
  • Nested repository: a repository and work-tree that is contained underneath another repository's work-tree.

For instance if /home/users/fred/projects/proj1 is the top level, it will contain a directory named .git . This is where the repository itself is. The rest of the stuff in /home/users/fred/projects/proj1 is the work-tree for this repository. If there is a /home/users/fred/projects/proj1/subproj/.git , this .git is a nested repository within the work-tree for the proj1 repository.

Commits inside the repository contain files. The files inside the commits have path names. These path names never start with slash, but may contain slashes (always forward slashes, even on Windows), so a file might be named, eg, path/to/file.ext . When Git extracts this file into the work-tree, it makes the path/ and path/to sub-directories on demand so that path/to/file.ext can exist in the way the OS itself requires (as a file named file.ext in a dir/folder named to that is in a dir/folder named path ). The various parts separated by slashes are path components .

What Git won't do is store a file or directory with a component named .git . 1 No commit should ever contain a file that has this as any of its path component. 2

What this means is that cloning a repository, then extracting some commit, never creates any sub-directory within the work-tree that is named .git . To contain an actual repository, you'd have to have a Git extract a subdirectory (not at the top level) named .git , which would then contain all the files that go in the .git of a fresh clone—and, except for security holes that are now fixed, Git just won't allow this.

You can , however, create new Git repositories within the work-tree of an existing Git repository. What you can't do is add and commit those, because adding one of those requires adding its .git directory, and a component name that includes .git is forbidden. Git will (in modern times anyway) cleverly detect that there is a .git , so this must be some kind of sub-project, and will automatically attempt to submodule-ize things, to some extent. (See below.)


1 In a security hole type bug, very-old versions of Git did not check for uppercase variants, which caused problems on case-insensitive file systems such as the default ones on Windows and MacOS. That is, you could store a .Git or .GIT or .gIt or some such. All modern Git versions forbid these as well.

2 Since existing commits cannot be changed, all modern Gits check for this in existing commits, and refuse to extract such files, too.


What you can do

There are two things you could do here, other than using a third-party add-on like Google's repo . 3 One is more sensible than the other, and also more automatic.

You could:

  • make your nested repositories manually, ie, create a repository with a work-tree and in that work-tree make a sub-repository
  • then just before git add -ing and git commit -ing in the top level work-tree, that contains nested repositories, rename the .git directory in the sub-repository:

     (cd sub; mv.git XGIT); git add.; git commit -m 'add nested fake repo'
  • then when you extract this commit, manually go in and rename XGIT to .git

and now you have a standard nested repository within a repository.

This is not the right way to do it and is just for illustration.

Or, you can:

  • use submodules

This is the right way to do it but it means that you have to have more than one GitHub or GitLab repository: you need one per project, in fact, plus one more "superproject" repository that simply exists to name all the sub-projects.

A submodule is simply a way to have one Git repository refer to another Git repository. The superproject—the containing repository—has a file named .gitmodules that lists the subproject info for git clone , and contains a series of commits. Each commit in the superproject lists:

  • the path name of the submodule Git, and
  • the commit hash ID that the superproject will git checkout in the submodule.

The superproject therefore directs a Git to clone and git checkout any number of additional Git repositories, all automatically. There are a number of drawbacks to submodules, but they do work right out of the box.

To use a submodule, after creating a sub-project .git within a superproject work-tree, use git submodule add in the superproject. This sets up the .gitmodules file correctly for you. If you let Git auto-detect the existence of the nested repository, Git creates the right part in the commit but the data needed to clone the submodule is missing.


3 Google's repo exists mainly because submodules were much more dysfunctional back in the 2005-2012 or so era. Submodules work a lot better today.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM