Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Fixed

- **`create-method-app` at a path another repository ignores**: a destination inside another repository's work tree that the repository ignores, such as a `tmp/` its `.gitignore` lists or anywhere under a home directory kept as a repository that ignores `*`, now gets a repository of its own on `main` with the pristine commit, and the `git:` line names the repository that ignores it. The initializer used to report such a project as new files in the enclosing repository and make no repository, which left it under no version control at all; a project made that way earlier can be given one with `git init` and a first commit. A directory the repository does not ignore still gets no repository even when every file in it is ignored, as under `*` followed by `!*/`, and the `git:` line now says the project is under no version control instead of calling it new files of that repository.
- **`create-method-app` at a path another repository ignores**: a destination inside another repository's work tree that the repository ignores, such as a `tmp/` its `.gitignore` lists or anywhere under a home directory kept as a repository that ignores `*`, now gets a repository of its own on `main` with the pristine commit, and the `git:` line names the repository that ignores it. The initializer used to report such a project as new files in the enclosing repository and make no repository, which left it under no version control at all; a project made that way earlier can be given one with `git init` and a first commit. A directory the repository does not ignore still gets no repository even when every file in it is ignored, as under `*` followed by `!*/`, and the `git:` line now says the project is under no version control instead of calling it new files of that repository. A destination that is a symlink is judged by the repository its target is in, not the one the link sits in.

## [v0.5.3] - 2026-09-24

Expand Down
2 changes: 1 addition & 1 deletion initializers/js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The initializer reads git before it writes anything:
| Inside another repository's work tree | No repository and no commit: the project is new files in that repository. `--no-create` lets you commit the template there first |
| Inside a checkout of `pipelex-method-apps` or of a starter | Refused, `--no-git` included whenever git is on the PATH |

A path the enclosing repository ignores, such as a `tmp/` its `.gitignore` lists or anywhere under a home directory kept as a repository that ignores `*`, is one it does not version, so a project there gets a repository of its own rather than no version control at all. Every source git reads counts: the enclosing repository's `.gitignore` files, its `.git/info/exclude`, and your `core.excludesFile`. What counts is the directory itself, which a missing destination is made for a moment to let git read: a directory the enclosing repository does not ignore gets no repository even when every file in it is ignored, as under `*` followed by `!*/`, since a repository there would still show in the enclosing one's `git status`, and the `git:` line then says the project is under no version control.
A path the enclosing repository ignores, such as a `tmp/` its `.gitignore` lists or anywhere under a home directory kept as a repository that ignores `*`, is one it does not version, so a project there gets a repository of its own rather than no version control at all. Every source git reads counts: the enclosing repository's `.gitignore` files, its `.git/info/exclude`, and your `core.excludesFile`. What counts is the directory itself, which a missing destination is made for a moment to let git read: a directory the enclosing repository does not ignore gets no repository even when every file in it is ignored, as under `*` followed by `!*/`, since a repository there would still show in the enclosing one's `git status`, and the `git:` line then says the project is under no version control. A destination that is a symlink is read where it points: the repository whose work tree holds its target is the one whose ignores count.

A commit needs a git identity, and a missing one is refused before anything is written. When git shows none outside a repository, the initializer asks again inside a throwaway repository at the destination, which it removes before going on, so an identity given only by an `includeIf "gitdir:…"` section is found. At a path another repository ignores, it always asks inside that throwaway repository, since an identity set in the enclosing repository's own configuration does not reach the new one.

Expand Down
19 changes: 12 additions & 7 deletions initializers/js/lib/git.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,21 @@ export function readGit({ dest, from, destExists, destHasGit, env }) {
* directory is named from its parent, with no slash, since asked from inside
* it as `./` git tests it with one. The name starts with `./` because git
* reads a leading `:` as pathspec magic, and `--literal-pathspecs` is refused
* by `check-ignore`. Any answer but a plain yes, a directory that cannot be
* made included, reads as not ignored, since mistaking a tracked destination
* for an ignored one would plant a repository in the user's tracked tree. A
* directory that holds a tracked path reads as not ignored whatever the
* patterns say, but a destination the preflight accepts holds none.
* by `check-ignore`. The directory is named where it physically is: a
* destination that is a symlink sits in one repository's work tree and points
* into another's, the one every other reading of the destination sees, and
* that one is the repository whose ignores count. Any answer but a plain yes, a
* directory that cannot be made or resolved included, reads as not ignored,
* since mistaking a tracked destination for an ignored one would plant a
* repository in the user's tracked tree. A directory that holds a tracked path
* reads as not ignored whatever the patterns say, whether or not its files are
* still on disk.
*/
function ignores({ dest, from, env }) {
return withDirectories({ dest, from }, false, () => {
const name = `./${path.basename(dest)}`;
return git(["check-ignore", "-q", "--", name], { cwd: path.dirname(dest), env }).status === 0;
const real = fs.realpathSync(dest);
const name = `./${path.basename(real)}`;
return git(["check-ignore", "-q", "--", name], { cwd: path.dirname(real), env }).status === 0;
});
}

Expand Down
18 changes: 18 additions & 0 deletions initializers/js/test/git.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,24 @@ describe("readGit inside another repository's work tree", () => {
});
}

it("reads a symlinked destination by the ignores of the repository it points into", () => {
const { root, repo, env } = enclosing(["link"]);
const setup = setupEnv(root);
const theirs = path.join(root, "other");
fs.mkdirSync(theirs);
git(theirs, ["init", "-q", "-b", "main"], setup);
fs.writeFileSync(path.join(theirs, "README.md"), "other\n");
git(theirs, ["add", "-A"], setup);
git(theirs, ["commit", "-q", "-m", "Other first commit"], setup);
const target = path.join(theirs, "apps", "new");
fs.mkdirSync(target, { recursive: true });
const link = path.join(repo, "link");
fs.symlinkSync(target, link);
assert.deepEqual(readExisting(link, env), { kind: "inside", toplevel: theirs });
fs.writeFileSync(path.join(theirs, ".gitignore"), "apps/new/\n");
assert.deepEqual(readExisting(link, env), { kind: "ignored", toplevel: theirs });
});

it("reads a destination under a tracked path as inside", () => {
const { repo, env } = enclosing(["*.log"]);
assert.deepEqual(readMissing(path.join(repo, "src", "my-app"), env), {
Expand Down
Loading