feat: add pr diff and checkout (task 0014)
All checks were successful
CI / test (pull_request) Successful in 42s
All checks were successful
CI / test (pull_request) Successful in 42s
Add the two PR commands that touch content and the local worktree:
- `pr diff <n>` fetches the raw diff from the `.diff` endpoint (forcing a
text response, which the JSON-defaulting client would otherwise discard),
truncates at 4000 chars with separate `truncated`/`original_length` fields
and a prepended `--full` suggestion; `--full` returns the raw diff.
- `pr checkout <n>` reads the PR head branch from the PR fetch and fetches
`refs/pull/{n}/head` from origin (uniform for same-repo and fork PRs, ADR
0011), three-cased on local branch state so re-checkout is idempotent and
divergent local commits fail with `GIT_ERROR` rather than being discarded.
Introduces the `GIT_ERROR` mapping (`runGit`) carrying git's first stderr
line plus a remediation help line, and widens the PR 404 classifier so a
`.diff` path still resolves to `PR_NOT_FOUND`.
This commit is contained in:
58
src/diff.ts
Normal file
58
src/diff.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import type { GiteaClient } from "./client.js";
|
||||
import type { RepoContext } from "./context.js";
|
||||
import { classifyHttpError } from "./errors.js";
|
||||
|
||||
/** The raw-diff truncation limit, distinct from the body/comment limits. */
|
||||
export const DIFF_TRUNCATE_LIMIT = 4000;
|
||||
|
||||
/**
|
||||
* A rendered pull-request diff. `truncated`/`original_length` are present only
|
||||
* when the diff was cut short — signalled as separate fields rather than an
|
||||
* inline hint, so the diff text itself stays a verbatim (if partial) prefix.
|
||||
*/
|
||||
export interface DiffResult {
|
||||
diff: string;
|
||||
truncated?: true;
|
||||
original_length?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a pull request's raw unified diff from `GET /pulls/{index}.diff`. The
|
||||
* generated client defaults every response to JSON parsing (its baseApiParams
|
||||
* set `format: "json"`), which would discard a plain-text diff body — so `text`
|
||||
* is forced per call to read the response verbatim.
|
||||
*/
|
||||
export async function fetchPullDiff(
|
||||
api: GiteaClient,
|
||||
context: RepoContext,
|
||||
number: number,
|
||||
): Promise<string> {
|
||||
try {
|
||||
const response = await api.repos.repoDownloadPullDiffOrPatch(
|
||||
context.owner,
|
||||
context.name,
|
||||
number,
|
||||
"diff",
|
||||
undefined,
|
||||
{ format: "text" },
|
||||
);
|
||||
return response.data ?? "";
|
||||
} catch (error) {
|
||||
throw classifyHttpError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate a diff to {@link DIFF_TRUNCATE_LIMIT}, reporting the original length
|
||||
* so the caller can offer `--full`. `full` returns the raw diff untouched.
|
||||
*/
|
||||
export function truncateDiff(diff: string, full: boolean): DiffResult {
|
||||
if (full || diff.length <= DIFF_TRUNCATE_LIMIT) {
|
||||
return { diff };
|
||||
}
|
||||
return {
|
||||
diff: diff.slice(0, DIFF_TRUNCATE_LIMIT),
|
||||
truncated: true,
|
||||
original_length: diff.length,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user