threadlines 0.2.22 → 0.2.23
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/dist/git/diff.js +49 -8
- package/package.json +1 -1
package/dist/git/diff.js
CHANGED
|
@@ -145,11 +145,22 @@ async function getCommitAuthor(repoRoot, sha) {
|
|
|
145
145
|
* Get diff for a PR/MR context in CI environments.
|
|
146
146
|
*
|
|
147
147
|
* This is a shared implementation for CI environments that do shallow clones.
|
|
148
|
-
*
|
|
148
|
+
* Uses three-dots logic (merge base) to show only the developer's changes,
|
|
149
|
+
* avoiding drift from main moving forward.
|
|
149
150
|
*
|
|
150
151
|
* Strategy:
|
|
151
152
|
* 1. Fetch target branch: origin/${targetBranch}:refs/remotes/origin/${targetBranch}
|
|
152
|
-
* 2.
|
|
153
|
+
* 2. Find merge base (common ancestor) using git merge-base (plumbing command)
|
|
154
|
+
* 3. Fetch merge base commit (always fetch, assume not available)
|
|
155
|
+
* 4. Diff: ${mergeBase}..HEAD (shows only developer's changes, not changes from main)
|
|
156
|
+
*
|
|
157
|
+
* Why three dots (merge base) instead of two dots (direct comparison)?
|
|
158
|
+
* - Two dots: Shows all differences between target branch tip and HEAD
|
|
159
|
+
* - Includes changes that happened in main since branching (drift)
|
|
160
|
+
* - Can show files the developer didn't touch
|
|
161
|
+
* - Three dots: Shows only changes from merge base to HEAD
|
|
162
|
+
* - Shows only what the developer actually changed
|
|
163
|
+
* - Industry standard for "change detection" in PR/MR reviews
|
|
153
164
|
*
|
|
154
165
|
* Why HEAD instead of origin/${sourceBranch}?
|
|
155
166
|
* - CI shallow clones only have HEAD available by default
|
|
@@ -175,12 +186,42 @@ async function getPRDiff(repoRoot, targetBranch, logger) {
|
|
|
175
186
|
`This is required for PR/MR diff comparison. ` +
|
|
176
187
|
`Error: ${fetchError instanceof Error ? fetchError.message : 'Unknown error'}`);
|
|
177
188
|
}
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
189
|
+
// Find merge base (common ancestor) using plumbing command
|
|
190
|
+
// Three dots logic: compare against merge base to show only developer's changes
|
|
191
|
+
// This avoids drift from main moving forward (two dots would include those changes)
|
|
192
|
+
let mergeBase;
|
|
193
|
+
try {
|
|
194
|
+
mergeBase = (0, child_process_1.execSync)(`git merge-base origin/${targetBranch} HEAD`, {
|
|
195
|
+
encoding: 'utf-8',
|
|
196
|
+
cwd: repoRoot
|
|
197
|
+
}).trim();
|
|
198
|
+
if (!mergeBase || mergeBase.length !== 40) {
|
|
199
|
+
throw new Error(`Invalid merge base SHA: "${mergeBase}"`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
204
|
+
throw new Error(`Failed to find merge base between origin/${targetBranch} and HEAD. ` +
|
|
205
|
+
`This is required to show only the developer's changes (avoids drift from main). ` +
|
|
206
|
+
`Error: ${errorMessage}`);
|
|
207
|
+
}
|
|
208
|
+
// Always fetch merge base (assume it's not available, fetch is fast/no-op if it is)
|
|
209
|
+
// This ensures we have the merge base available for diff comparison
|
|
210
|
+
logger?.debug(`Fetching merge base: ${mergeBase}`);
|
|
211
|
+
try {
|
|
212
|
+
await git.fetch(['origin', mergeBase, '--depth=1']);
|
|
213
|
+
}
|
|
214
|
+
catch (fetchError) {
|
|
215
|
+
throw new Error(`Failed to fetch merge base ${mergeBase} from origin. ` +
|
|
216
|
+
`This is required for PR/MR diff comparison in shallow clones. ` +
|
|
217
|
+
`Ensure 'origin' remote is configured and accessible. ` +
|
|
218
|
+
`Error: ${fetchError instanceof Error ? fetchError.message : 'Unknown error'}`);
|
|
219
|
+
}
|
|
220
|
+
// Use merge base for diff (three dots logic: shows only developer's changes)
|
|
221
|
+
// git diff is plumbing command, ignores shallow boundaries
|
|
222
|
+
logger?.debug(`Comparing ${mergeBase}..HEAD (merge base vs PR branch)`);
|
|
223
|
+
const diff = await git.diff([`${mergeBase}..HEAD`, '-U200']);
|
|
224
|
+
const diffSummary = await git.diffSummary([`${mergeBase}..HEAD`]);
|
|
184
225
|
const changedFiles = diffSummary.files.map(f => f.file);
|
|
185
226
|
return {
|
|
186
227
|
diff: diff || '',
|