threadlines 0.2.21 → 0.2.22

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.
Files changed (2) hide show
  1. package/dist/git/diff.js +34 -28
  2. package/package.json +1 -1
package/dist/git/diff.js CHANGED
@@ -190,7 +190,7 @@ async function getPRDiff(repoRoot, targetBranch, logger) {
190
190
  /**
191
191
  * Get diff for a specific commit (or HEAD if no SHA provided).
192
192
  *
193
- * Fetches the parent commit on-demand to ensure git show can generate a proper diff.
193
+ * Uses plumbing commands consistently to work reliably in shallow clones.
194
194
  * This works regardless of CI checkout depth settings (depth=1 or depth=2).
195
195
  *
196
196
  * Strategy:
@@ -199,8 +199,11 @@ async function getPRDiff(repoRoot, targetBranch, logger) {
199
199
  * - Porcelain commands (git show) respect shallow boundaries and hide parents in shallow clones
200
200
  * - This is critical for CI environments that use shallow clones (depth=1)
201
201
  * 2. Parse first parent line (handles standard commits and merge commits)
202
- * 3. Fetch parent commit if available (git fetch origin <parentSHA> --depth=1)
203
- * 4. Use git show to get diff (now parent is available for comparison)
202
+ * 3. Fetch parent commit (git fetch origin <parentSHA> --depth=1)
203
+ * 4. Use git diff <PARENT_SHA> HEAD to get diff (plumbing command, ignores shallow boundaries)
204
+ * - git show HEAD still respects .git/shallow even after fetching parent
205
+ * - git diff <PARENT_SHA> HEAD compares tree objects directly, ignoring shallow boundaries
206
+ * - This is the key fix: must use plumbing commands consistently, not mix with porcelain
204
207
  *
205
208
  * Used by:
206
209
  * - All CI environments for push/commit context (GitHub, GitLab, Bitbucket, Vercel)
@@ -235,9 +238,14 @@ async function getCommitDiff(repoRoot, sha = 'HEAD') {
235
238
  throw new Error(`Commit ${sha} has no parent (it might be the root commit of the repository)`);
236
239
  }
237
240
  // Extract SHA from "parent <sha>" line
238
- parentSha = parentLine.split(' ')[1].trim();
241
+ // Format: "parent <40-char-sha>"
242
+ const parts = parentLine.split(' ');
243
+ if (parts.length < 2 || !parts[1]) {
244
+ throw new Error(`Malformed parent line in commit object: "${parentLine}"`);
245
+ }
246
+ parentSha = parts[1].trim();
239
247
  if (!parentSha || parentSha.length !== 40) {
240
- throw new Error(`Invalid parent SHA format: "${parentSha}"`);
248
+ throw new Error(`Invalid parent SHA format: "${parentSha}" (expected 40 characters)`);
241
249
  }
242
250
  }
243
251
  catch (error) {
@@ -246,33 +254,31 @@ async function getCommitDiff(repoRoot, sha = 'HEAD') {
246
254
  `This is required to generate a proper diff. ` +
247
255
  `Error: ${errorMessage}`);
248
256
  }
249
- // Fetch parent commit (root commits have no parent, so this won't execute for them)
250
- if (parentSha && parentSha.length === 40) {
251
- try {
252
- // Fetch just this one commit (depth=1 is fine, we only need the parent)
253
- await git.fetch(['origin', parentSha, '--depth=1']);
254
- }
255
- catch (error) {
256
- const errorMessage = error instanceof Error ? error.message : String(error);
257
- throw new Error(`Failed to fetch parent commit ${parentSha} from origin. ` +
258
- `This is required to generate a proper diff in shallow clones. ` +
259
- `Ensure 'origin' remote is configured and accessible. ` +
260
- `Error: ${errorMessage}`);
261
- }
257
+ // Fetch parent commit (we've already validated parentSha is valid above)
258
+ // If we get here, parentSha is guaranteed to be a valid 40-character SHA
259
+ try {
260
+ // Fetch just this one commit (depth=1 is fine, we only need the parent)
261
+ await git.fetch(['origin', parentSha, '--depth=1']);
262
+ }
263
+ catch (error) {
264
+ const errorMessage = error instanceof Error ? error.message : String(error);
265
+ throw new Error(`Failed to fetch parent commit ${parentSha} from origin. ` +
266
+ `This is required to generate a proper diff in shallow clones. ` +
267
+ `Ensure 'origin' remote is configured and accessible. ` +
268
+ `Error: ${errorMessage}`);
262
269
  }
263
- // If no parent (root commit), git show will show the full commit content
264
- // This is expected behavior for root commits
265
- // Get diff using git show (now parent should be available)
270
+ // Get diff using plumbing command (git diff) instead of porcelain (git show)
271
+ // git show respects .git/shallow boundaries and will still treat HEAD as root commit
272
+ // git diff <PARENT_SHA> HEAD ignores shallow boundaries and compares tree objects directly
273
+ // This is critical for shallow clones - we must use plumbing commands consistently
266
274
  let diff;
267
275
  let changedFiles;
268
276
  try {
269
- diff = await git.show([sha, '--format=', '--no-color', '-U200']);
270
- // Get changed files using git show --name-only
271
- const commitFiles = await git.show([sha, '--name-only', '--format=', '--pretty=format:']);
272
- changedFiles = commitFiles
273
- .split('\n')
274
- .filter(line => line.trim().length > 0)
275
- .map(line => line.trim());
277
+ // Use git diff to compare parent against HEAD (plumbing command, ignores shallow boundaries)
278
+ diff = await git.diff([`${parentSha}..${sha}`, '-U200']);
279
+ // Get changed files using git diff --name-only
280
+ const diffSummary = await git.diffSummary([`${parentSha}..${sha}`]);
281
+ changedFiles = diffSummary.files.map(f => f.file);
276
282
  }
277
283
  catch (error) {
278
284
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threadlines",
3
- "version": "0.2.21",
3
+ "version": "0.2.22",
4
4
  "description": "Threadlines CLI - AI-powered linter based on your natural language documentation",
5
5
  "main": "dist/index.js",
6
6
  "bin": {