gent-cli 20.0.0 → 21.0.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gent-cli",
3
- "version": "20.0.0",
3
+ "version": "21.0.0",
4
4
  "description": "A modern, Git-like version control CLI with cloud sync, AI-powered superpowers (ask/review/docs/changelog), and zero-friction setup (gent setup/doctor/config).",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -155,11 +155,15 @@ const handlers = {
155
155
  if (options.abort) return merge.abortMerge(repo);
156
156
  if (options.continue) return merge.concludeMerge(repo, options.message);
157
157
  const result = await merge.merge(repo, branch, options);
158
- console.log(result.status);
159
158
  if (result.status === 'conflicts') {
159
+ for (const conflict of result.conflicts) {
160
+ if (conflict.kind === 'content') console.log(`Auto-merging ${conflict.path}`);
161
+ console.log(`CONFLICT (${conflict.kind}): Merge conflict in ${conflict.path}`);
162
+ }
163
+ console.log('Automatic merge failed; fix conflicts and then commit the result.');
160
164
  console.log('Resolve files, stage with gent add, then gent merge --continue or gent commit -m <message>.');
161
165
  process.exitCode = 1;
162
- }
166
+ } else console.log(result.status);
163
167
  },
164
168
  async resolve(repo) {
165
169
  const index = await GitIndex.read(repo.indexPath);
@@ -130,9 +130,10 @@ function matchBaseToOther(baseLines, otherLines) {
130
130
  * @param {String[]} baseLines
131
131
  * @param {String[]} oursLines
132
132
  * @param {String[]} theirsLines
133
+ * @param {{ours?: String, theirs?: String}} [labels]
133
134
  * @returns {{merged: String[], conflicts: Array, hasConflicts: Boolean}}
134
135
  */
135
- function threeWayMerge(baseLines, oursLines, theirsLines) {
136
+ function threeWayMerge(baseLines, oursLines, theirsLines, labels = {}) {
136
137
  const oMatch = matchBaseToOther(baseLines, oursLines);
137
138
  const tMatch = matchBaseToOther(baseLines, theirsLines);
138
139
 
@@ -157,7 +158,7 @@ function threeWayMerge(baseLines, oursLines, theirsLines) {
157
158
  const oursSeg = oursLines.slice(prevO, a.o);
158
159
  const theirsSeg = theirsLines.slice(prevT, a.t);
159
160
 
160
- resolveRegion(baseSeg, oursSeg, theirsSeg, merged, conflicts);
161
+ resolveRegion(baseSeg, oursSeg, theirsSeg, merged, conflicts, labels);
161
162
 
162
163
  // Emit the synchronized anchor line (skip the end sentinel).
163
164
  if (a.b < baseLines.length) {
@@ -180,8 +181,9 @@ function threeWayMerge(baseLines, oursLines, theirsLines) {
180
181
  * @param {String[]} theirsSeg
181
182
  * @param {String[]} merged - output accumulator (mutated)
182
183
  * @param {Array} conflicts - output accumulator (mutated)
184
+ * @param {{ours?: String, theirs?: String}} [labels]
183
185
  */
184
- function resolveRegion(baseSeg, oursSeg, theirsSeg, merged, conflicts) {
186
+ function resolveRegion(baseSeg, oursSeg, theirsSeg, merged, conflicts, labels = {}) {
185
187
  if (baseSeg.length === 0 && oursSeg.length === 0 && theirsSeg.length === 0) {
186
188
  return;
187
189
  }
@@ -213,11 +215,11 @@ function resolveRegion(baseSeg, oursSeg, theirsSeg, merged, conflicts) {
213
215
  oursContent: oursSeg,
214
216
  theirsContent: theirsSeg
215
217
  });
216
- merged.push('<<<<<<< ours');
218
+ merged.push(`<<<<<<< ${labels.ours || 'ours'}`);
217
219
  merged.push(...oursSeg);
218
220
  merged.push('=======');
219
221
  merged.push(...theirsSeg);
220
- merged.push('>>>>>>> theirs');
222
+ merged.push(`>>>>>>> ${labels.theirs || 'theirs'}`);
221
223
  }
222
224
 
223
225
  // ─── Sub-Merge (fine-grained) ───────────────────────────
@@ -390,9 +392,10 @@ function parseConflictMarkers(content) {
390
392
  * @param {String} oursContent
391
393
  * @param {String} theirsContent
392
394
  * @param {String} [fileName] - used to pick a language-aware strategy
395
+ * @param {{ours?: String, theirs?: String}} [labels] - conflict marker labels
393
396
  * @returns {{content: String, hasConflicts: Boolean, conflicts: Array}}
394
397
  */
395
- function mergeFileContent(baseContent, oursContent, theirsContent, fileName) {
398
+ function mergeFileContent(baseContent, oursContent, theirsContent, fileName, labels) {
396
399
  if (fileName && /\.json$/i.test(fileName)) {
397
400
  const jsonResult = mergeJsonContent(baseContent || '', oursContent || '', theirsContent || '');
398
401
  if (jsonResult) return jsonResult;
@@ -401,7 +404,7 @@ function mergeFileContent(baseContent, oursContent, theirsContent, fileName) {
401
404
  const base = splitLines(baseContent || '');
402
405
  const ours = splitLines(oursContent || '');
403
406
  const theirs = splitLines(theirsContent || '');
404
- const result = threeWayMerge(base, ours, theirs);
407
+ const result = threeWayMerge(base, ours, theirs, labels);
405
408
  return { content: result.merged.join('\n'), hasConflicts: result.hasConflicts, conflicts: result.conflicts };
406
409
  }
407
410
 
@@ -158,7 +158,8 @@ async function merge(repo, theirRevision, options = {}) {
158
158
  baseBytes.toString('utf-8'),
159
159
  oursBytes.toString('utf-8'),
160
160
  theirsBytes.toString('utf-8'),
161
- filePath
161
+ filePath,
162
+ { ours: 'HEAD', theirs: label }
162
163
  );
163
164
  const content = Buffer.from(result.content, 'utf-8');
164
165
  const oid = await repo.objects.write('blob', content);
@@ -183,7 +184,7 @@ async function merge(repo, theirRevision, options = {}) {
183
184
  }
184
185
  await index.write(repo.indexPath);
185
186
 
186
- const message = options.message || `Merge ${label} into ${head.branch || 'HEAD'}`;
187
+ const message = options.message || `Merge branch '${label}'`;
187
188
 
188
189
  if (conflicts.length) {
189
190
  await ops.writeMergeState(repo, [theirs], `${message}\n\nConflicts:\n${conflicts.map(c => ' ' + c.path).join('\n')}\n`);