@jwilger/pi-development-system 0.23.0 → 0.24.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/package.json +1 -1
- package/src/gates/commit-guard.ts +28 -16
package/package.json
CHANGED
|
@@ -146,7 +146,7 @@ async function reviewNeeds(
|
|
|
146
146
|
},
|
|
147
147
|
];
|
|
148
148
|
}
|
|
149
|
-
const stale = stagedThenEdited(names, command);
|
|
149
|
+
const stale = stagedThenEdited(names, command, ctx.cwd);
|
|
150
150
|
return stale.length === 0
|
|
151
151
|
? []
|
|
152
152
|
: [
|
|
@@ -157,21 +157,35 @@ async function reviewNeeds(
|
|
|
157
157
|
];
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
type Invocations = ReturnType<typeof resolveGit>["invocations"];
|
|
161
|
+
|
|
160
162
|
/** Files staged and also changed since staging; empty when the commit stages everything itself. */
|
|
161
163
|
const stagedThenEdited = (
|
|
162
164
|
names: { staged: readonly string[]; unstaged: readonly string[] },
|
|
163
165
|
command: string,
|
|
166
|
+
cwd: string,
|
|
164
167
|
): string[] => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
// Only what runs before or at the first commit can change what that commit records.
|
|
169
|
+
const all = resolveGit(command).invocations;
|
|
170
|
+
const firstCommit = all.findIndex((i) => i.sub === "commit");
|
|
171
|
+
const upto = firstCommit === -1 ? all : all.slice(0, firstCommit + 1);
|
|
172
|
+
if (stagesAllTracked(upto, cwd)) return [];
|
|
173
|
+
// A path (or a directory holding it) that is an argument of the commit or add itself is committed or
|
|
174
|
+
// re-staged from the work tree, which was reviewed. Message text is one argument and never equals a path.
|
|
175
|
+
const named = upto
|
|
176
|
+
.filter((i) => (i.sub === "commit" || i.sub === "add") && !isDryRun(i) && inThisDir(i, cwd))
|
|
177
|
+
.flatMap((i) => i.args.filter((a) => !a.startsWith("-")).map(cleanPath));
|
|
178
|
+
const covered = (name: string) => named.some((n) => name === n || name.startsWith(`${n}/`));
|
|
179
|
+
return names.staged.filter((name) => names.unstaged.includes(name) && !covered(name));
|
|
173
180
|
};
|
|
174
181
|
|
|
182
|
+
const cleanPath = (arg: string): string => arg.replace(/^(\.\/)+/, "").replace(/\/+$/, "");
|
|
183
|
+
const isDryRun = (i: Invocations[number]): boolean =>
|
|
184
|
+
i.sub === "add" && i.args.some((a) => a === "-n" || a === "--dry-run");
|
|
185
|
+
/** The invocation runs in the session's directory (no `cd`/`-C`, or one that leads back to it). */
|
|
186
|
+
const inThisDir = (i: Invocations[number], cwd: string): boolean =>
|
|
187
|
+
i.dir === undefined || resolve(cwd, i.dir) === resolve(cwd);
|
|
188
|
+
|
|
175
189
|
const STAGE_ALL = new Set(["-A", "--all", "-u", "--update", "."]);
|
|
176
190
|
const BOOLEAN_SHORT = /[enqsvz]/;
|
|
177
191
|
|
|
@@ -187,15 +201,13 @@ function clusterHasA(arg: string): boolean {
|
|
|
187
201
|
|
|
188
202
|
/**
|
|
189
203
|
* The command re-stages every tracked change of this repository before it commits: `commit -a`, or
|
|
190
|
-
* `git add -A|-u|.` with no pathspec, in
|
|
191
|
-
* invocations, not message text.
|
|
204
|
+
* `git add -A|-u|.` with no pathspec, in this directory. Read from parsed invocations, not message text.
|
|
192
205
|
*/
|
|
193
|
-
function stagesAllTracked(invocations:
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (i.dir !== undefined) return false;
|
|
206
|
+
function stagesAllTracked(invocations: Invocations, cwd: string): boolean {
|
|
207
|
+
return invocations.some((i) => {
|
|
208
|
+
if (!inThisDir(i, cwd) || isDryRun(i)) return false;
|
|
197
209
|
if (i.sub === "commit") return i.args.some((a) => a === "--all" || clusterHasA(a));
|
|
198
|
-
if (i.sub !== "add"
|
|
210
|
+
if (i.sub !== "add") return false;
|
|
199
211
|
const paths = i.args.filter((a) => !a.startsWith("-"));
|
|
200
212
|
return i.args.some((a) => STAGE_ALL.has(a)) && paths.every((a) => a === ".");
|
|
201
213
|
});
|