git-patch 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -22,7 +22,9 @@ npx git-patch list
22
22
 
23
23
  ```bash
24
24
  git-patch list # Human-readable hunk list
25
+ git-patch list --summary # One line per hunk (id + file/range + counts)
25
26
  git-patch list --json # Structured JSON output
27
+ git-patch list --json --summary # Flat hunk summaries for scripts/LLMs
26
28
  git-patch list --staged # Show staged hunks
27
29
  git-patch list -- src/main.rs # Filter to specific files
28
30
  ```
package/bin/git-patch.js CHANGED
@@ -10,7 +10,7 @@ function usage() {
10
10
  console.log(`git-patch — Non-interactive hunk staging for LLMs
11
11
 
12
12
  Usage:
13
- git-patch list [--json] [--staged] [-- files...]
13
+ git-patch list [--json] [--summary] [--staged] [-- files...]
14
14
  git-patch stage <selector> [--all] [--matching <regex>] [-- files...]
15
15
  git-patch unstage <selector> [--all] [--matching <regex>]
16
16
  git-patch discard <selector> [--all] [--matching <regex>] [--yes] [--dry-run] [-- files...]
@@ -39,12 +39,13 @@ try {
39
39
  args,
40
40
  options: {
41
41
  json: { type: "boolean", default: false },
42
+ summary: { type: "boolean", default: false },
42
43
  staged: { type: "boolean", default: false },
43
44
  },
44
45
  strict: true,
45
46
  });
46
47
  let { run } = await import("../lib/commands/list.js");
47
- run({ json: values.json, staged: values.staged, files });
48
+ run({ json: values.json, summary: values.summary, staged: values.staged, files });
48
49
  break;
49
50
  }
50
51
 
@@ -1,15 +1,59 @@
1
1
  import { parseDiff } from "../diff-parser.js";
2
2
  import { getDiff } from "../git.js";
3
3
 
4
- export function run({ json = false, staged = false, files = [] } = {}) {
4
+ function formatDisplayRange(file, hunk) {
5
+ let span = Math.max(hunk.oldCount, hunk.newCount, 1);
6
+ let start = hunk.oldStart > 0 ? hunk.oldStart : hunk.newStart;
7
+ let end = start + span - 1;
8
+ return `${file.file}:${start}-${end}`;
9
+ }
10
+
11
+ function summarizeHunks(fileDiffs) {
12
+ let hunks = [];
13
+
14
+ for (let file of fileDiffs) {
15
+ for (let hunk of file.hunks) {
16
+ let oldEnd = hunk.oldCount === 0 ? hunk.oldStart : hunk.oldStart + hunk.oldCount - 1;
17
+ let newEnd = hunk.newCount === 0 ? hunk.newStart : hunk.newStart + hunk.newCount - 1;
18
+
19
+ hunks.push({
20
+ id: hunk.id,
21
+ file: file.file,
22
+ range: formatDisplayRange(file, hunk),
23
+ oldRange: {
24
+ start: hunk.oldStart,
25
+ end: oldEnd,
26
+ count: hunk.oldCount,
27
+ },
28
+ newRange: {
29
+ start: hunk.newStart,
30
+ end: newEnd,
31
+ count: hunk.newCount,
32
+ },
33
+ addedCount: hunk.addedCount,
34
+ removedCount: hunk.removedCount,
35
+ context: hunk.context,
36
+ });
37
+ }
38
+ }
39
+
40
+ return hunks;
41
+ }
42
+
43
+ export function run({ json = false, staged = false, files = [], summary = false } = {}) {
5
44
  let raw = getDiff({ staged, files });
6
45
  let fileDiffs = parseDiff(raw);
7
46
 
8
47
  if (json) {
9
- let output = {
10
- type: staged ? "staged" : "unstaged",
11
- files: fileDiffs,
12
- };
48
+ let output = summary
49
+ ? {
50
+ type: staged ? "staged" : "unstaged",
51
+ hunks: summarizeHunks(fileDiffs),
52
+ }
53
+ : {
54
+ type: staged ? "staged" : "unstaged",
55
+ files: fileDiffs,
56
+ };
13
57
  console.log(JSON.stringify(output, null, 2));
14
58
  return;
15
59
  }
@@ -23,11 +67,15 @@ export function run({ json = false, staged = false, files = [] } = {}) {
23
67
 
24
68
  for (let file of fileDiffs) {
25
69
  for (let hunk of file.hunks) {
26
- let range = `${file.file}:${hunk.oldStart}-${hunk.oldStart + Math.max(hunk.oldCount, hunk.newCount) - 1}`;
70
+ let range = formatDisplayRange(file, hunk);
27
71
  let counts = `(+${hunk.addedCount} -${hunk.removedCount})`;
28
72
  let ctx = hunk.context ? ` ${hunk.context}` : "";
29
73
  console.log(` ${hunk.id} ${range} ${counts}${ctx}`);
30
74
 
75
+ if (summary) {
76
+ continue;
77
+ }
78
+
31
79
  // Show only change lines with their line indices
32
80
  let changeIndex = 0;
33
81
  for (let line of hunk.lines) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-patch",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Non-interactive hunk staging for LLMs — stage, unstage, and discard git changes by hunk or line",
5
5
  "type": "module",
6
6
  "bin": {
@@ -119,6 +119,30 @@ describe("git-patch integration", () => {
119
119
  assert.match(out, /-.*Hello/);
120
120
  });
121
121
 
122
+ it("lists only hunk headers with --summary", () => {
123
+ writeFile(
124
+ "src/app.js",
125
+ [
126
+ "function greet(name) {",
127
+ ' return "Hi, " + name;',
128
+ "}",
129
+ "",
130
+ "function farewell(name) {",
131
+ ' return "Goodbye, " + name;',
132
+ "}",
133
+ "",
134
+ "module.exports = { greet, farewell };",
135
+ "",
136
+ ].join("\n"),
137
+ );
138
+
139
+ let out = gp("list --summary");
140
+ assert.match(out, /Unstaged changes/);
141
+ assert.match(out, /src\/app\.js/);
142
+ assert.doesNotMatch(out, /return "Hi, "/);
143
+ assert.doesNotMatch(out, /return "Hello, "/);
144
+ });
145
+
122
146
  it("outputs valid JSON with --json", () => {
123
147
  writeFile(
124
148
  "src/utils.js",
@@ -144,6 +168,35 @@ describe("git-patch integration", () => {
144
168
  assert.equal(out.files[0].hunks[0].id, 1);
145
169
  });
146
170
 
171
+ it("outputs hunk summaries with --json --summary", () => {
172
+ writeFile(
173
+ "src/app.js",
174
+ [
175
+ "function greet(name) {",
176
+ ' return "Hi, " + name;',
177
+ "}",
178
+ "",
179
+ "function farewell(name) {",
180
+ ' return "Goodbye, " + name;',
181
+ "}",
182
+ "",
183
+ "module.exports = { greet, farewell };",
184
+ "",
185
+ ].join("\n"),
186
+ );
187
+
188
+ let out = JSON.parse(gp("list --json --summary"));
189
+ assert.equal(out.type, "unstaged");
190
+ assert.equal(out.hunks.length, 1);
191
+ assert.equal(out.hunks[0].id, 1);
192
+ assert.equal(out.hunks[0].file, "src/app.js");
193
+ assert.equal(out.hunks[0].addedCount, 1);
194
+ assert.equal(out.hunks[0].removedCount, 1);
195
+ assert.ok(out.hunks[0].range.startsWith("src/app.js:"));
196
+ assert.equal(out.hunks[0].oldRange.start, 1);
197
+ assert.equal(out.hunks[0].newRange.start, 1);
198
+ });
199
+
147
200
  it("lists staged hunks with --staged", () => {
148
201
  writeFile(
149
202
  "src/app.js",