megit-app 0.7.0 → 0.8.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/dist/index.html CHANGED
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <link rel="icon" type="image/svg+xml" href="/logo.svg" />
7
7
  <title>megit</title>
8
- <script type="module" crossorigin src="/assets/index-Ch9W5KCh.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-C5YS6cns.css">
8
+ <script type="module" crossorigin src="/assets/index-j_kzUnBQ.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-ByiPZ5H3.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -271,6 +271,14 @@ async function revParse(repo, ref) {
271
271
  const out = await git(repo, ['rev-parse', '--verify', '--quiet', ref], [0, 1]);
272
272
  return out.trim() || null;
273
273
  }
274
+ // Remote branches that already contain this sha. Rewriting such a commit leaves the
275
+ // remote needing a force push, which megit never does — so both amend paths ask first.
276
+ async function pushedTo(repo, sha) {
277
+ return (await git(repo, ['branch', '-r', '--contains', sha]))
278
+ .split('\n').map(s => s.trim())
279
+ // drop the "origin/HEAD -> origin/main" symref line: it's a pointer, not a second branch
280
+ .filter(r => r && !r.includes(' -> '));
281
+ }
274
282
  // Uncommitted work goes to a stash before anything that moves the worktree, so no
275
283
  // path can fail on (or silently carry over) a dirty tree. Returns whether it stashed.
276
284
  async function stashIfDirty(repo, why) {
@@ -542,12 +550,7 @@ app.post('/api/commit', repoGuard, async (req, res) => {
542
550
  if (!message)
543
551
  throw httpError(400, 'empty commit message');
544
552
  if (req.body.force !== true) {
545
- // rewriting a pushed commit leaves the remote needing a force push, which
546
- // megit never does — so the client has to ask again before this proceeds
547
- const onRemote = (await git(repo, ['branch', '-r', '--contains', sha]))
548
- .split('\n').map(s => s.trim())
549
- // drop the "origin/HEAD -> origin/main" symref line: it's a pointer, not a second branch
550
- .filter(r => r && !r.includes(' -> '));
553
+ const onRemote = await pushedTo(repo, sha);
551
554
  if (onRemote.length)
552
555
  throw httpError(409, `already pushed to ${onRemote.join(', ')}`);
553
556
  }
@@ -653,9 +656,28 @@ app.post('/api/wip', repoGuard, async (req, res) => {
653
656
  const message = String(req.body.message ?? '').trim();
654
657
  if (!message)
655
658
  throw httpError(400, 'empty commit message');
659
+ const amend = req.body.amend === true;
660
+ if (amend) {
661
+ // amend rewrites HEAD, so there has to be a HEAD, and a branch to move
662
+ if (!(await git(repo, ['branch', '--show-current'])).trim())
663
+ throw httpError(409, 'detached HEAD — check out a branch first');
664
+ const head = await revParse(repo, 'HEAD');
665
+ if (!head)
666
+ throw httpError(409, 'no commit to amend');
667
+ // mid-merge/rebase the staged tree belongs to the operation in progress:
668
+ // folding it into the previous commit would swallow that commit whole
669
+ const op = await readOperation(repo);
670
+ if (op)
671
+ throw httpError(409, `${op.kind} in progress — finish it first`);
672
+ if (req.body.force !== true) {
673
+ const onRemote = await pushedTo(repo, head);
674
+ if (onRemote.length)
675
+ throw httpError(409, `already pushed to ${onRemote.join(', ')}`);
676
+ }
677
+ }
656
678
  // hooks run as they would in a terminal — this commit has real content —
657
679
  // but a hook that blocks on stdin gets killed rather than wedging the request
658
- await git(repo, ['commit', `--message=${message}`], [0], NET_TIMEOUT);
680
+ await git(repo, amend ? ['commit', '--amend', `--message=${message}`] : ['commit', `--message=${message}`], [0], NET_TIMEOUT);
659
681
  res.json({ ok: true, hash: (await git(repo, ['rev-parse', 'HEAD'])).trim() });
660
682
  return;
661
683
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "megit-app",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Git repository viewer in the browser: commit graph with branch lanes, diffs, stashes and WIP",
5
5
  "license": "MIT",
6
6
  "author": "Hoang Vuong Vu",
@@ -60,6 +60,6 @@
60
60
  "react-dom": "^19.2.8",
61
61
  "typescript": "^7.0.2",
62
62
  "vite": "^8.2.1",
63
- "vitest": "^4.1.10"
63
+ "vitest": "^4.1.11"
64
64
  }
65
65
  }