git-sich 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +178 -2
- package/dist/cli.js +1570 -0
- package/package.json +42 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marin Sokol
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,179 @@
|
|
|
1
|
-
#
|
|
1
|
+
# sich
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
CLI for managing multiple git repositories within a single base git repo.
|
|
4
|
+
|
|
5
|
+
Useful for:
|
|
6
|
+
- versioning private files alongside your public files, in the same folder
|
|
7
|
+
- access control per file: some files can be public while others belong to Org1 and others to Org2, all living side by side
|
|
8
|
+
|
|
9
|
+
`sich` lets a normal git repo (**base**, contained in `.git`) coexist smoothly with any number of other git repos (**layers**, contained in `.sich/X`). They share the same working tree, each with its own separate commit history and remote.
|
|
10
|
+
|
|
11
|
+
Works with GitHub out of the box: every new layer can automatically become a new private repo, and GitHub then handles access control for it.
|
|
12
|
+
|
|
13
|
+
Plain `git` commands only ever see **base** files. Files from other layers never appear in the
|
|
14
|
+
**base** repo: not in commits, not in `.gitignore`. Other contributors never even know that `sich` is in use or that the other repositories exist; everything works for them as usual.
|
|
15
|
+
|
|
16
|
+
**The name** is a shortened (easier-to-type) version of *sietch*, the hidden cave communities of the Fremen in Frank Herbert's *Dune*, where only the tribe knows the way in. Such are the layers of `sich`: only visible to those with access to them.
|
|
17
|
+
|
|
18
|
+
## Why
|
|
19
|
+
|
|
20
|
+
- Notes, LLM conversations, roadmaps, or random scratch files you want versioned and synced, but not public.
|
|
21
|
+
- Secrets or local config shared with a few collaborators or your GitHub org, but not the world.
|
|
22
|
+
- No submodules, no symlinks, no separate folders. Files stay where they belong.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
Requires Node ≥ 22 and git ≥ 2.28.
|
|
27
|
+
Optionally the GitHub CLI (`gh`) 2.x, for `sich new --gh`.
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npm install -g git-sich # the package is git-sich; the command is sich
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quickstart
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
cd myproject # an existing git repo, called base from now on; say it has a public remote at git@github.com:user/myproject.git
|
|
37
|
+
sich init # creates an empty .sich folder, excludes it from base (.git/info/exclude) and installs the pre-commit guard
|
|
38
|
+
sich new personal --gh # creates a layer (a sietch) called "personal": .sich/personal is a separate git repo, in the same format as the top-level .git that defines base
|
|
39
|
+
# with --gh it also creates the private GitHub repo user/myproject-personal and sets it as the remote (git@github.com:user/myproject-personal.git)
|
|
40
|
+
sich add personal my-notes.md # the personal layer claims my-notes.md; base now ignores it automatically
|
|
41
|
+
sich personal commit -m "adding notes" # commits in the personal repo; or `sich commit -m "..."` commits base + every layer with changes at once
|
|
42
|
+
sich push personal # pushes personal to its remote (setting its upstream the first time); or `sich push` pushes base + all layers
|
|
43
|
+
|
|
44
|
+
sich new team --gh # creates another layer, "team", and the private repo user/myproject-team, which you can share with your team
|
|
45
|
+
sich add team roadmap.md api-keys.env llm-transcripts/ # claims these for the team layer; base's exclude now hides all three
|
|
46
|
+
# ...
|
|
47
|
+
|
|
48
|
+
# A collaborator with sich installed and access to user/myproject-team:
|
|
49
|
+
git clone git@github.com:user/myproject.git && cd myproject
|
|
50
|
+
sich attach team git@github.com:user/myproject-team.git
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
For a full walkthrough with real output, the resulting files and every file sich
|
|
54
|
+
generates, see **[docs/example.md](docs/example.md)**.
|
|
55
|
+
|
|
56
|
+
## CLI
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
❯ sich --help
|
|
60
|
+
sich - private git layers that share one working tree with a normal repo
|
|
61
|
+
|
|
62
|
+
base your normal repo (.git), usually public
|
|
63
|
+
layer a private repo in .sich/<layer>/ with its own remote and access list
|
|
64
|
+
claim a file or folder a layer owns; base and other layers never see it
|
|
65
|
+
|
|
66
|
+
usage: sich [-C <dir>] <command> [args]
|
|
67
|
+
sich [-C <dir>] <layer|base> <git args...>
|
|
68
|
+
|
|
69
|
+
setup
|
|
70
|
+
init set up .sich/, base excludes, commit guard
|
|
71
|
+
new <layer> [--remote <url> | --gh [name]]
|
|
72
|
+
create a layer (--gh: private GitHub repo)
|
|
73
|
+
attach <layer> <url> join an existing layer (collaborators)
|
|
74
|
+
|
|
75
|
+
ownership
|
|
76
|
+
add <layer> <path...> [--move] claim files/folders for a layer, stage them
|
|
77
|
+
(--move: take them from base/another layer)
|
|
78
|
+
rm <layer> <path...> release claims; files stay on disk
|
|
79
|
+
which <path> show who owns a path
|
|
80
|
+
ls [layer] list claims and tracked files
|
|
81
|
+
|
|
82
|
+
everyday
|
|
83
|
+
status [-v] [--fetch] per repo: branch, ahead/behind, changes
|
|
84
|
+
commit [repo...] -m <msg> add -A + commit base + all layers (or named)
|
|
85
|
+
pull [repo...] pull --rebase base + all layers (or named)
|
|
86
|
+
push [repo...] push base + all layers (or named)
|
|
87
|
+
sync [repo...] pull, then push
|
|
88
|
+
check [--fix] [--staged] find leaks and ownership problems
|
|
89
|
+
(--staged: what the pre-commit hook runs)
|
|
90
|
+
|
|
91
|
+
passthrough
|
|
92
|
+
<layer> <git args...> run git in a layer, e.g. sich notes log
|
|
93
|
+
base <git args...> run git in the base repo
|
|
94
|
+
|
|
95
|
+
options
|
|
96
|
+
-C <dir> run as if started in <dir>
|
|
97
|
+
-h, --help show help (also: sich <command> --help)
|
|
98
|
+
-V, --version print version
|
|
99
|
+
|
|
100
|
+
environment
|
|
101
|
+
SICH_BIN sich binary the pre-commit hook runs
|
|
102
|
+
(default: sich on PATH)
|
|
103
|
+
SICH_GH gh binary for new --gh (default: gh)
|
|
104
|
+
NO_COLOR disable colors
|
|
105
|
+
|
|
106
|
+
Layer names match ^[a-z0-9][a-z0-9._-]*$; base and command names are reserved.
|
|
107
|
+
Claims can't nest across layers.
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## How it works
|
|
111
|
+
|
|
112
|
+
It's plain git all the way down; `sich` is a tiny wrapper on top.
|
|
113
|
+
To see every file it generates for a real project, look at
|
|
114
|
+
[section 4 of the example](docs/example.md#4-the-files-sich-generates).
|
|
115
|
+
|
|
116
|
+
**Layers.** Layer `L` is a regular, non-bare git dir at `.sich/L/` with
|
|
117
|
+
`core.worktree = ../..`, so its working tree is the project root. sich always
|
|
118
|
+
runs git with explicit `--git-dir`/`--work-tree`, and strips `GIT_DIR`,
|
|
119
|
+
`GIT_INDEX_FILE` and friends from the environment.
|
|
120
|
+
|
|
121
|
+
**Manifests.** `.sich/L.paths` lists what `L` owns: one root-relative path per
|
|
122
|
+
line, directories end with `/`, `#` comments allowed. The manifest is tracked by
|
|
123
|
+
layer `L` itself, so collaborators get it on `attach`/`pull`. It is the single
|
|
124
|
+
source of truth for ownership; sich keeps it sorted (comments move to the top).
|
|
125
|
+
Paths that a line can't hold (starting with `#`, starting or ending with
|
|
126
|
+
whitespace, containing line breaks) can't be claimed.
|
|
127
|
+
|
|
128
|
+
**Generated excludes.** sich owns a marked block in each repo's `info/exclude`
|
|
129
|
+
(`# >>> sich: managed, do not edit >>>` … `# <<< sich <<<`; anything outside the
|
|
130
|
+
block is left alone) and regenerates it on every command:
|
|
131
|
+
|
|
132
|
+
- base: `/.sich/` plus every claim of every layer.
|
|
133
|
+
- layer `L`: a whitelist. `/*` ignores everything, then each claim is re-included
|
|
134
|
+
along with its parent chain (git can't re-include a file inside an excluded
|
|
135
|
+
directory), e.g. `a/b/c.md` becomes `!/a/` `/a/*` `!/a/b/` `/a/b/*` `!/a/b/c.md`.
|
|
136
|
+
|
|
137
|
+
Claims never nest across layers: if `notes` owns `docs/`, no other layer can
|
|
138
|
+
claim `docs/api.env` (and `keys` owning `docs/api.env` stops `notes` from
|
|
139
|
+
claiming `docs/` without `--move`, which takes the whole folder). Two files in
|
|
140
|
+
the same folder can still belong to different layers.
|
|
141
|
+
|
|
142
|
+
Because each layer only "sees" its claims, `git add -A` in a layer is safe, and
|
|
143
|
+
new files created inside a claimed directory automatically belong to that layer.
|
|
144
|
+
|
|
145
|
+
**Guard.** The base `pre-commit` hook runs `sich check --staged`. It blocks a
|
|
146
|
+
base commit that stages a claimed path or anything under `.sich/` (e.g. after
|
|
147
|
+
`git add -f`; on case-insensitive filesystems under any spelling), and it blocks
|
|
148
|
+
while ownership between layers is ambiguous (overlapping claims, a file tracked
|
|
149
|
+
by the wrong layer), since that could leak one layer's files into another.
|
|
150
|
+
Stale exclude rules only warn. Where sich isn't set up (a linked `git worktree`)
|
|
151
|
+
it does nothing. Skip the check once with `git commit --no-verify`.
|
|
152
|
+
|
|
153
|
+
## FAQ
|
|
154
|
+
|
|
155
|
+
**Why not just keep private files gitignored?**
|
|
156
|
+
Then they aren't versioned, synced between your machines or shareable with collaborators.
|
|
157
|
+
|
|
158
|
+
**Why not just a second, private GitHub repo?**
|
|
159
|
+
That's essentially what a layer is, but its files stay in the same folder as the main repo, next to the code that uses them or the context that explains them, and it scales easily to any number of layers.
|
|
160
|
+
|
|
161
|
+
**Why not a git submodule?**
|
|
162
|
+
Besides the benefit above (files stay next to what uses them), changing a layer never needs a commit in base, since their commit histories are fully separate, and the layer's repo name never appears in the main repo: from the outside, it doesn't exist.
|
|
163
|
+
|
|
164
|
+
That said, sometimes you do want a base commit to pin a submodule version: when the two only work with specific versions of each other (say, a package, library or API spec inside your own package) and must always move together, so checking out base at `HEAD~1` has to move the other as well. Submodules are the better fit there.
|
|
165
|
+
|
|
166
|
+
**Can't GitHub do per-file permissions?**
|
|
167
|
+
No. Visibility is set per repo, and anyone who can clone a repo gets every file in it.
|
|
168
|
+
Their per-repo access control is great, though, and sich builds on it!
|
|
169
|
+
|
|
170
|
+
**Is this like git-crypt or sops?**
|
|
171
|
+
No. sich doesn't encrypt anything; it relies on who can access each repo. You can combine the two for real secrets.
|
|
172
|
+
|
|
173
|
+
## Caveats and contributing
|
|
174
|
+
|
|
175
|
+
[CONTRIBUTING.md](CONTRIBUTING.md)
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
[MIT](LICENSE)
|