{"id":20560830,"url":"https://github.com/feluxe/gitsub_deprecated","last_synced_at":"2026-05-17T17:35:49.090Z","repository":{"id":93042018,"uuid":"160901578","full_name":"feluxe/gitsub_DEPRECATED","owner":"feluxe","description":"DEPRECATED: In favor of https://github.com/ingydotnet/git-subrepo","archived":false,"fork":false,"pushed_at":"2019-11-13T00:15:00.000Z","size":36,"stargazers_count":3,"open_issues_count":7,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-01T10:53:47.653Z","etag":null,"topics":["git","nested-repositories","repositories","subrepos"],"latest_commit_sha":null,"homepage":"","language":"Python","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/feluxe.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-08T03:50:12.000Z","updated_at":"2020-12-30T14:36:40.000Z","dependencies_parsed_at":null,"dependency_job_id":"7af006c3-4f0e-4056-b871-0bdb72a7697b","html_url":"https://github.com/feluxe/gitsub_DEPRECATED","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/feluxe/gitsub_DEPRECATED","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/feluxe%2Fgitsub_DEPRECATED","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/feluxe%2Fgitsub_DEPRECATED/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/feluxe%2Fgitsub_DEPRECATED/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/feluxe%2Fgitsub_DEPRECATED/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/feluxe","download_url":"https://codeload.github.com/feluxe/gitsub_DEPRECATED/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/feluxe%2Fgitsub_DEPRECATED/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33147711,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-17T09:28:26.183Z","status":"ssl_error","status_checked_at":"2026-05-17T09:27:52.702Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["git","nested-repositories","repositories","subrepos"],"created_at":"2024-11-16T03:55:56.537Z","updated_at":"2026-05-17T17:35:49.063Z","avatar_url":"https://github.com/feluxe.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Gitsub\n\n## Description\n\nA simple but effective wrapper around git that allows for (deeply) nested repositories.\n\nIt's much simpler than `submodule` and `subtree`. There are no complicated commands to remember... Just cd into your repo (parent or child doesn't matter) and run your git commands as usual.\n\n\n## How it works\n\nIf you run `git commit ...` in a parent-repo that contains one or several child-repos, the files of the child-repos will be added to the parent-repo, except for their `.git` directories.\n\nWithout `gitsub` git would complain if you run `git add -A` in a directory that itself contains a `.git` dir in one of its subdirecotries. `gitsub` works around this by temporarily renaming each `.git` to `.gitsub_hidden` for the duration of the command.\n\nIf you run `git commit ...` on a parent-repo, the current *branch-name*, *commit-hash* and *remote-url* of each child-repo is locked into `.gitsub` (a file that sits in the root directory of the parent).\n\nSince the parent-repo does't contain the `.git` direcories of its children, you have to run `git init-children` after you clone a parent-repo. That will automatically clone each child-repo into the correct sub direcotry of its parent and set the HEAD of each child-repo to the branch/commit that was locked in `.gitsub`.\n\nIf you try to commit to a parent-repo while one of its children contains uncommited changes, gitsub will complain and tell you to commit and push the changes in order to proceed. This mechanism is needed for the parent to keep its child-repo files in sync with their commits, so that a `git clone myparent` and a `git inint-children` always create the same tree.\n\nI think that's pretty much all you need to know. It's simple but it works intuitivly well. Gitsub will warn you whenever something is needed.\n\n\n\n## Example (Walkthrough)\n\nLet's start from scratch.\n\nWe have a normal git repo called `parent_repo`. The repo already contains two directories named `child_repo1` and `child_repo2`, but they don't contain anything yet.\n\n```\n parent_repo\n ├── .git\n ├── foo\n ├── child_repo1\n └── child_repo2\n\n```\n\nNow let's init git repos for our children and create a text file for each:\n\n```\n$ cd child_repo1\n$ git init\n$ touch bar\n$ cd ..\n$ cd child_repo2\n$ git init\n$ touch baz\n```\n\nThis leaves us with this:\n\n```\n parent_repo\n ├── .git\n ├── foo\n ├── child_repo1\n │   ├── .git   # New\n │   └── bar    # New\n └── child_repo2\n     ├── .git   # New\n     └── baz    # New\n\n```\n\nNow we make our parent repo a *gitsub parent* by running `git init-parent` in `parent_repo`. This will create a `.gitsub` file for locking child data.\n\nWe have this now:\n\n\n```\n parent_repo\n ├── .git\n ├── .gitsub    # New\n ├── foo\n ├─ child_repo1\n │   ├── .git\n │   └── bar\n └── child_repo2\n      ├── .git\n      └── baz\n\n```\n\nNow I want to `add / commit / push` the changes for the parent repo, but if I run:\n\n```\ncd /parent_repo\ngit add -A\ngit commit -m 'changes'\n```\n\nI get an error such as this one:\n\n\u003e Current commit cannot be found on remote for: child_repo1\n\nThat means we have to run `commit/push` for child_repo1 and 2 in order to commit to our parent. For each child we run:\n\n```\ncd /parent_repo/child_repo[1|2]\n$ git add -A\n$ git commit -m 'changes'\n$ git push origin master\n```\n\nNow I can cd back to the parent and push the changes without gitsub complaining.\n\nOk then... \n\nNow let's imagine we go to another computer and continue working from there.\n\nWe clone the parent repo: `git clone http://...parent_repo.git`\n\nAnd get this:\n\n```\n parent_repo\n ├── .git\n ├── .gitsub\n ├── foo\n ├── child_repo1\n │    └── bar\n └── child_repo2\n      └── baz\n\n```\nAs you see, the children don't contain a `.git` dir. That's because the parent never stores the .git directories of its children. Instead, it stores the remote-urls, current commit-hash and branch-name for each child in `.gitsub`.\n\nIn order to populate the children with their git repos, you run:\n\n`git init-children`\n\nThis will give you the properly nested repo tree:\n\n```\n parent_repo\n ├── .git\n ├── .gitsub\n ├── foo\n ├─ child_repo1\n │    ├── .git   # This was added\n │    └── bar\n └── child_repo2\n      ├── .git  # This was added\n      └── baz\n\n```\n\nThis is pretty much it. The rest is just git as you know it. You cd back and forth from repo to repo and run your git commands. The only limitation is that you have to push changes of child-repos to their remote location before you can commit to a parent. But gitsub will warn you when this is the case.\n\n\n\n## Commands\n\n`git init-parent`\n\nRun this command in a git repo to create a `.gitsub` file in its root.\n\n\n`git init-children`\n\nAfter you clone a parent-repo, you need to run this command in oder to populate the children with their `.git` direcotry and set each HEAD to the correct branch/commit.\n\n\n`git check-children`\n\nRun this command to check if all children are ready to be commited. You usually don't have to do this, since gitsub will warn you anyways. But it might be handy in some situations.\n\n\n## Requirements\n\n* Unix like system\n* Tested with `git version 2.19`\n* Each child-repo must have a remote location set.\n* A cache directory: `~/.cache`\n\n\n\n## Install\n\n**build from source**\n\nYou can build `gitsub` from source and put it into your `PATH`:\n\n```\ncd /tmp\n\ngit clone https://github.com/feluxe/gitsub.git\n\ncd gitsub\n\npipenv install --dev --pre\n\npipenv run python make.py build\n\nsudo cp dist/pyinstaller/gitsub /usr/local/bin\n```\n\n**add alias**\n\nYou can call the `gitsub` binary directly or add an alias to your terminal `rc` file, e.g.\n\n`~/.zshrc`\n\n    alias git=gitsub\n\n\n## Development\n\nIn the following example we build from source and run the test command. This leaves you with a `gitsub` binary and a test boilerplate in `/tmp/gitsub` to play with.\n\n```\ngit clone https://github.com/feluxe/gitsub.git\n\ncd gitsub\n\npipenv install --dev --pre\n\npipenv run python make.py build\n\npipenv run python make.py test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffeluxe%2Fgitsub_deprecated","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffeluxe%2Fgitsub_deprecated","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffeluxe%2Fgitsub_deprecated/lists"}