{"id":13552536,"url":"https://github.com/capr/multigit","last_synced_at":"2025-04-10T16:25:04.886Z","repository":{"id":29917362,"uuid":"33463244","full_name":"capr/multigit","owner":"capr","description":":trident: Overlapped Git Repositories","archived":false,"fork":false,"pushed_at":"2023-08-24T10:18:59.000Z","size":117,"stargazers_count":147,"open_issues_count":4,"forks_count":18,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-24T14:11:46.656Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/capr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-04-06T02:30:21.000Z","updated_at":"2024-11-25T07:58:14.000Z","dependencies_parsed_at":"2024-01-16T18:58:03.445Z","dependency_job_id":"59e78ed2-37d5-4a1e-a90d-8221d87915b0","html_url":"https://github.com/capr/multigit","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capr%2Fmultigit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capr%2Fmultigit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capr%2Fmultigit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capr%2Fmultigit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/capr","download_url":"https://codeload.github.com/capr/multigit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248252665,"owners_count":21072699,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T12:02:05.759Z","updated_at":"2025-04-10T16:25:04.868Z","avatar_url":"https://github.com/capr.png","language":"Shell","funding_links":[],"categories":["Shell"],"sub_categories":[],"readme":"# multigit\n\n#### layered git repositories\n\u003csub\u003eWriten by Cosmin Apreutesei. **Public Domain**.\u003c/sub\u003e\u003cbr\u003e\n\u003csub\u003eTested on **Linux**, **Windows** and **OSX**\u003c/sub\u003e\n\nMultigit allows checking out multiple git repositories overlaid \nonto the same directory.\n\nIt is useful for projects which are made of different components\nthat are developed separately, but which need to track files \nfrom different parts of the directory structure of the project.\n\nThis cannot be done using git submodules or git subtrees, which\nonly allow subprojects to be checked out into their own private \nsubdirectories. Multigit allows repositories to track any file \nfrom any directory of the project structure, similar to a union \nfilesystem, where each repository is a layer.\n\nSome examples where this combination of change management\nand module management could be useful:\n\n  * manage customizations made to a web app in a separate repository.\n  * putting your home directory under source control, one repo per app.\n  * package manager for a project, dev platform or language.\n\n## How do I install it?\n\nMultigit is a simple shell script with no dependencies. You can either\nput it somewhere in your PATH, or you can clone it everywhere\nyou want to create a multigit project in, and call it as `./mgit`\n(or `mgit` on Windows).\n\nTo enable tab completion in bash, put `mgit-autocomplete.sh` somewhere\nand source it in your `.bashrc` along with your git completion script\n(don't call them, dot them). On Ubuntu, the git completion script is at\n`/usr/share/bash-completion/completions/git` (you need to source\nthat manually).\n\n## How do I use it?\n\nLet's see a bare bones example:\n\n\t$ mkdir project\n\t$ cd project\n\n\t$ mgit init foo                # create layered repo foo\n\t$ mgit init bar                # create layered repo bar\n\t$ mgit ls                      # list layered repos\n\tfoo\n\tbar\n\n\t$ echo \u003e foo.txt               # create file foo.txt\n\t$ echo \u003e bar.txt               # create file bar.txt\n\t$ mgit foo add -f foo.txt      # add foo.txt to project foo\n\t$ mgit bar add -f bar.txt      # add bar.txt to project bar\n\t$ mgit foo commit -m \"init\"    # commit on foo\n\t$ mgit bar commit -m \"init\"    # commit on bar\n\n\t$ ls\n\tfoo.txt bar.txt                # foo.txt and bar.txt are in the same dir\n\t$ mgit foo ls-files\n\tfoo.txt                        # but project foo only tracks foo.txt\n\t$ mgit bar ls-files\n\tbar.txt                        # and project bar only tracks bar.txt\n\nNote the `-f` (force) when adding files to git. When creating a repo with\n`mgit init foo`, the `.gitignore` file for foo is set to\n`.mgit/foo.exclude` which defaults to `*`, which means that\nall files are ignored by default, hence the need to add them with `-f`.\nThis is to prevent accidentally adding files of other projects with\n`git add -A` and ending up with multiple projects tracking the same file.\nTo recover the convenience of `git -A` and the correct reporting of\nuntracked files, change the exclude files and add patterns that are\nappropriate to each repo. Given that all repos now share the same\nnamespace, you need to be explicit about which parts of that namespace\nare \"reserved\" for which repos.\n\nYou can use git direcly on your layered repos with a subshell:\n\n\t$ mgit foo\n\tEntering subshell: git commands will affect the repo 'foo'.\n\tType `exit' to exit subshell.\n\tA  foo.txt\n\n\t[foo] $ git ls-files\n\tfoo.txt\n\t[foo] $ exit\n\t$\n\nOr you can type git commands directly without a subshell:\n\n\t$ mgit foo ls-files\n\tfoo.txt\n\nOr you can ditch multigit entirely and go bare git:\n\n\t$ export GIT_DIR=.mgit/foo/.git\n\t$ git ls-files\n\tfoo.txt\n\n## How do I clone repositories?\n\n\t$ mkdir project\n\t$ cd project\n\n\t$ mgit clone https://github.com/bob/foo\n\t$ mgit clone https://github.com/bob/bar\n\nThis will clone both repos into the current directory\n(there's no \"target directory\" arg).\n\n## But do I have to type the full URL every time?\n\nNo you don't. Since most of the submodules in a big project will probably\nreside at a common base URL, you can make an alias for that base URL and\nuse that instead when cloning the submodules:\n\n\t$ mgit baseurl bob https://github.com/bob/  # writes `https://github.com/bob/` in .mgit/bob.baseurl\n\t$ mgit clone bob/foo bob/bar                # writes `bob` in .mgit/foo.origin and .mgit/bar.origin\n\nNow that bob is _registered_ as a remote, and both foo's and bar's origins\nare registered too (they are set to `bob`), next time it will be enough\nto type `mgit clone foo bar`. Which brings us to the next question...\n\n## How do I manage module collections?\n\nEvery time you clone a repo with `mgit clone`, a file `.mgit/foo.origin`\nis created which contains the origin url of that repo, which allows you\nto clone it by name alone. These files are not tracked by default,\nthey're just sitting there, but there's nothing stopping you from\ncreating a \"meta\" repo and start tracking them:\n\n\t$ mgit init meta\n\t$ mgit meta add -f .mgit/bob.baseurl   # add bob's baseurl to meta\n\t$ mgit meta add -f .mgit/foo.origin    # add foo's origin to meta\n\t$ mgit meta add -f .mgit/bar.origin    # add bar's origin to meta\n\t$ mgit meta commit -m \"bob's place; foo and bar modules\"\n\n\u003e The meta repo is like any other layered repo, it just so happens that it\ntracks files from `.mgit` (and it doesn't have to be called meta either,\nand it doesn't have to be the _only_ repo containing meta-information).\n\nMaintaining a `meta` repo for a project allows the users of that project\nto clone the meta repo and then clone all other repos\nwith `mgit clone-all` (and list them with `mgit ls-all`).\nThis makes for a simple way to manage and share module collections\nthat later can be cloned back wholesale.\n\nNote that repos created with `mgit init foo` don't create an origin file.\nYou can add that yourself with `mgit origin foo https://github.com/bob/foo`\nor simply `mgit origin foo bob` if you have previously added bob's baseurl.\n\n## But this will always clone master. How do I lock versions?\n\n\t$ mgit release 1.0 update     # adds .mgit/1.0.release\n\nThis creates (or updates) a list with currently checked out versions\nof all repos, effectively recording a snapshot of the entire project.\nThis snapshot can later be restored with:\n\n\t$ mgit clone-release 1.0\n\n\u003e Note: Existing repos will have to be removed first. This command\nwill refuse to overwrite them. Also, this will not create branches.\nYour repos will be in \"detached HEAD state\". You can take it from there\nwith git, eg. with `mgit --all checkout -b release-1.0`.\n\nNeedless to say, you can add the .release file to your meta repo too,\njust like with the .baseurl and .origin files before, so that other people\nwill be able to clone the project at that specific release point.\n\nAnother quick way to get a snapshot of the project without using .release\nfiles is with:\n\n\t$ mgit --all ver\n\tfoo=701d080 bar=0fefd96\n\nAnd later clone the repos with:\n\n\t$ mgit clone foo=701d080 bar=0fefd96\n\nTag releases can be made with:\n\n\t$ mgit release 1.0 update tag\n\nTag releases only record the current tag as opposed to the exact\nfull version of the packages. This allows you to make stable releases\neven when some packages are \"unstable\" (i.e. they contain commits above the\nlatest tag). It also enables the controversial practice of updating a tag's\ntarget commit without having to make a new release.\n\n## What else can it do?\n\ncommand                               | description\n------------------------------------- | --------------------------------------\n`mgit st`                             | list modified files across all repos\n`mgit --all -v status -s`             | like `mgit st` but shows the repos too\n`mgit ls-unpushed`                    | list repos ahead of origin\n`mgit ls-tracked`                     | list files and the repos who track them\n`mgit ls-untracked`                   | list files untracked by any repo\n`mgit ls-double-tracked`              | list files tracked by multiple repos\n`mgit remove [--dry] REPO ...`        | remove repos from disk\n\n\nThere's more stuff, type `mgit` to see.\n\n## How does this work anyway?\n\nSimply by telling git to clone all the repositories into a common\nwork tree. The git trees are kept in `.mgit/\u003crepo\u003e/.git` and the\nwork tree is always '.'.\n\nThis is such basic and useful functionality that it should\nbe built into `git clone` and `git init` really. As dead simple\nas multigit is, it's still yet another script that you have to deploy.\n\n## Simple? But it's a 600 lines script!\n\nDon't worry about it, it's mostly fluff. The gist of it is only 6 lines:\n\nmgit init foo:\n\n\tmkdir -p .mgit/foo\n\texport GIT_DIR=.mgit/foo/.git\n\tgit init                                                  # create .mgit/foo/.git\n\tgit config --local core.worktree ../../..                 # relative to GIT_DIR\n\tgit config --local core.excludesfile .mgit/foo.exclude    # instead of .gitignore\n\t[ -f .mgit/foo.exclude ] || echo '*' \u003e .mgit/foo.exclude  # \"ignore all\"\n\nmgit foo ls-files:\n\n\texport GIT_DIR=.mgit/foo/.git    # set git to work on foo\n\tgit ls-files                     # list files of foo\n\n## What's its relation to the current directory?\n\nJust like git, mgit scans the current directory and its parents for\na `.mgit` dir, and if found, it sets the current directory to that,\nand everything else happens from there, except starting a subshell\nand executing git commands which run in the current directory.\n\n## Where is multigit used?\n\nMultigit is the package manager for [luapower](https://luapower.com).\nThe [meta-package](https://github.com/luapower/luapower-repos) contains\nthe list of packages in the distribution and a few handy multigit plugins\nspecific to luapower.\n\n## What multigit plugins?\n\nPlugins allows extending multigit with project-specific scripts\nthat are exposed as multigit commands (like build scripts, etc.).\nPlugin scripts can be included say, in the meta package of your project.\nThe way they work is very simple:\n\n  * `mgit \u003crepo\u003e \u003ccommand\u003e ...` will try to run\n    `.mgit/git-\u003ccommand\u003e.sh ...` with `$GIT_DIR` set properly,\n\t `$MULTIGIT_REPO` set to `\u003crepo\u003e`, `$PWD0` set to the\n\t invocation path and current directory set to one directory where\n\t the `.mgit` directory was found.\n\n  * `mgit \u003ccommand\u003e ...` will try to run `.mgit/\u003ccommand\u003e.sh`.\n\n  * `mgit help` will try to `cat .mgit/*.help`, which is where you should\n    place the help section of the added commands.\n\nLook at [luapower-repos](https://github.com/luapower/luapower-repos)\nfor a real-world example of this.\n\n## Environment\n\nMultigit will use the following environment variables when performing\nvarious tasks:\n\n  * `$MULTIGIT_FETCH_OPTS` will be expanded and passed to `git fetch`\n  * `$MULTIGIT_INIT_OPTS` will be expanded and passed to `git init`\n\n## Limitations\n\n  * No way to specify a different branch when cloning (must switch it after).\n  * No way to specify a different branch when making releases.\n  * No way to register multiple remotes for the same repo and specify\n  the remote when cloning.\n\n## Related efforts\n\nThere is a very similar project called [vcsh](https://github.com/RichiH/vcsh).\nIt even has a [video presentation](http://mirror.as35701.net/video.fosdem.org//2012/lightningtalks/vcsh.webm),\ncheck it out.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcapr%2Fmultigit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcapr%2Fmultigit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcapr%2Fmultigit/lists"}