gent-cli 9.0.0 → 9.1.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": "9.0.0",
3
+ "version": "9.1.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": {
@@ -89,6 +89,9 @@ async function commit(options) {
89
89
  }
90
90
  }
91
91
 
92
+ // ponytail: accounts with no name set fall back to email as identity
93
+ if (!authorName && authorEmail) authorName = authorEmail;
94
+
92
95
  if (!authorName || !authorEmail) {
93
96
  spinner.stop();
94
97
  console.error(chalk.red('Author identity unknown'));
@@ -91,9 +91,10 @@ async function explain(ref, options = {}) {
91
91
  );
92
92
  void stagedTree;
93
93
  } else {
94
- const targetHash = ref
95
- ? (commits.find(c => c.hash === ref || c.hash.startsWith(ref)) || {}).hash
96
- : repository.branches[repository.currentBranch];
94
+ const headHash = repository.branches[repository.currentBranch];
95
+ const targetHash = (!ref || ref === 'HEAD')
96
+ ? headHash
97
+ : (commits.find(c => c.hash === ref || c.hash.startsWith(ref)) || {}).hash;
97
98
  const commit = targetHash ? commitMap.get(targetHash) : null;
98
99
  if (!commit) {
99
100
  console.log(chalk.yellow(ref ? `Commit '${ref}' not found` : 'No commits yet'));
@@ -95,9 +95,15 @@ async function push(remoteName, branchName, options) {
95
95
 
96
96
  // Determine which commits to push (since last pushed ref)
97
97
  config.remoteRefs = config.remoteRefs || {};
98
- const lastPushed = config.remoteRefs[`${remote}/${branch}`] || null;
98
+ // Everything reachable from any already-pushed ref of this remote is the
99
+ // boundary — a merge can pull in commits from a branch that was never
100
+ // pushed, so we can't bound by this branch's ref alone.
101
+ const remoteHave = Object.entries(config.remoteRefs)
102
+ .filter(([name]) => name.startsWith(`${remote}/`))
103
+ .map(([, sha]) => sha)
104
+ .filter(Boolean);
99
105
  const commits = repository.commits || [];
100
- const commitsToPush = getCommitsSince(commits, localHead, lastPushed);
106
+ const commitsToPush = getCommitsSince(commits, localHead, remoteHave);
101
107
 
102
108
  if (commitsToPush.length === 0) {
103
109
  spinner.succeed(chalk.green('Everything up-to-date'));
@@ -257,25 +263,44 @@ async function push(remoteName, branchName, options) {
257
263
  }
258
264
 
259
265
  /**
260
- * Get commits from tip back to (but excluding) stopHash.
266
+ * Get commits reachable from tip (following BOTH parents, so merges are
267
+ * covered) that the remote doesn't already have. Post-order → parents precede
268
+ * children in the returned array.
261
269
  * @param {Array} allCommits
262
270
  * @param {String} tipHash
263
- * @param {String|null} stopHash
271
+ * @param {String[]} remoteHave - shas the remote already has (its ref tips)
264
272
  * @returns {Array}
265
273
  */
266
- function getCommitsSince(allCommits, tipHash, stopHash) {
274
+ function getCommitsSince(allCommits, tipHash, remoteHave) {
267
275
  const commitMap = new Map(allCommits.map(c => [c.hash, c]));
268
- const result = [];
269
- let current = tipHash;
270
276
 
271
- while (current && current !== stopHash) {
272
- const commit = commitMap.get(current);
273
- if (!commit) break;
274
- result.push(commit);
275
- current = commit.parent;
277
+ // Boundary = every commit reachable from a remote ref via both parents.
278
+ const have = new Set();
279
+ const stack = [...(remoteHave || [])].filter(Boolean);
280
+ while (stack.length) {
281
+ const sha = stack.pop();
282
+ if (!sha || have.has(sha)) continue;
283
+ const c = commitMap.get(sha);
284
+ if (!c) continue; // not local → treat as already-remote boundary
285
+ have.add(sha);
286
+ if (c.parent) stack.push(c.parent);
287
+ if (c.mergeParent) stack.push(c.mergeParent);
276
288
  }
277
289
 
278
- return result.reverse(); // oldest first
290
+ const result = [];
291
+ const visited = new Set();
292
+ const visit = (sha) => {
293
+ if (!sha || visited.has(sha) || have.has(sha)) return;
294
+ const c = commitMap.get(sha);
295
+ if (!c) return;
296
+ visited.add(sha);
297
+ visit(c.parent);
298
+ visit(c.mergeParent);
299
+ result.push(c); // after both parents → oldest first
300
+ };
301
+ visit(tipHash);
302
+
303
+ return result;
279
304
  }
280
305
 
281
306
  module.exports = push;