git-watchtower 1.4.0 → 1.5.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/bin/git-watchtower.js +92 -22
- package/package.json +1 -1
package/bin/git-watchtower.js
CHANGED
|
@@ -1190,6 +1190,11 @@ function render() {
|
|
|
1190
1190
|
if (state.errorToast) {
|
|
1191
1191
|
renderer.renderErrorToast(state, write);
|
|
1192
1192
|
}
|
|
1193
|
+
|
|
1194
|
+
// Stash confirmation dialog renders on top of everything
|
|
1195
|
+
if (state.stashConfirmMode) {
|
|
1196
|
+
renderer.renderStashConfirm(state, write);
|
|
1197
|
+
}
|
|
1193
1198
|
}
|
|
1194
1199
|
|
|
1195
1200
|
function showFlash(message) {
|
|
@@ -1224,7 +1229,6 @@ function showErrorToast(title, message, hint = null, duration = 8000) {
|
|
|
1224
1229
|
|
|
1225
1230
|
errorToastTimeout = setTimeout(() => {
|
|
1226
1231
|
store.setState({ errorToast: null });
|
|
1227
|
-
pendingDirtyOperation = null;
|
|
1228
1232
|
render();
|
|
1229
1233
|
}, duration);
|
|
1230
1234
|
}
|
|
@@ -1240,6 +1244,26 @@ function hideErrorToast() {
|
|
|
1240
1244
|
}
|
|
1241
1245
|
}
|
|
1242
1246
|
|
|
1247
|
+
function showStashConfirm(operationLabel) {
|
|
1248
|
+
store.setState({
|
|
1249
|
+
stashConfirmMode: true,
|
|
1250
|
+
stashConfirmSelectedIndex: 0,
|
|
1251
|
+
pendingDirtyOperationLabel: operationLabel,
|
|
1252
|
+
});
|
|
1253
|
+
render();
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
function hideStashConfirm() {
|
|
1257
|
+
if (store.get('stashConfirmMode')) {
|
|
1258
|
+
store.setState({
|
|
1259
|
+
stashConfirmMode: false,
|
|
1260
|
+
stashConfirmSelectedIndex: 0,
|
|
1261
|
+
pendingDirtyOperationLabel: null,
|
|
1262
|
+
});
|
|
1263
|
+
render();
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1243
1267
|
// ============================================================================
|
|
1244
1268
|
// Git Functions
|
|
1245
1269
|
// ============================================================================
|
|
@@ -1365,13 +1389,8 @@ async function switchToBranch(branchName, recordHistory = true) {
|
|
|
1365
1389
|
const isDirty = await hasUncommittedChanges();
|
|
1366
1390
|
if (isDirty) {
|
|
1367
1391
|
addLog(`Cannot switch: uncommitted changes in working directory`, 'error');
|
|
1368
|
-
addLog(`Press S to stash changes, or commit manually`, 'warning');
|
|
1369
1392
|
pendingDirtyOperation = { type: 'switch', branch: branchName };
|
|
1370
|
-
|
|
1371
|
-
'Cannot Switch Branch',
|
|
1372
|
-
'You have uncommitted changes in your working directory that would be lost.',
|
|
1373
|
-
'Press S to stash changes'
|
|
1374
|
-
);
|
|
1393
|
+
showStashConfirm(`switch to ${branchName}`);
|
|
1375
1394
|
return { success: false, reason: 'dirty' };
|
|
1376
1395
|
}
|
|
1377
1396
|
|
|
@@ -1404,6 +1423,7 @@ async function switchToBranch(branchName, recordHistory = true) {
|
|
|
1404
1423
|
}
|
|
1405
1424
|
|
|
1406
1425
|
addLog(`Switched to ${safeBranchName}`, 'success');
|
|
1426
|
+
pendingDirtyOperation = null;
|
|
1407
1427
|
|
|
1408
1428
|
// Restart server if configured (command mode)
|
|
1409
1429
|
if (SERVER_MODE === 'command' && RESTART_ON_SWITCH && serverProcess) {
|
|
@@ -1423,13 +1443,8 @@ async function switchToBranch(branchName, recordHistory = true) {
|
|
|
1423
1443
|
);
|
|
1424
1444
|
} else if (errMsg.includes('local changes') || errMsg.includes('overwritten')) {
|
|
1425
1445
|
addLog(`Cannot switch: local changes would be overwritten`, 'error');
|
|
1426
|
-
addLog(`Press S to stash changes, or commit manually`, 'warning');
|
|
1427
1446
|
pendingDirtyOperation = { type: 'switch', branch: branchName };
|
|
1428
|
-
|
|
1429
|
-
'Cannot Switch Branch',
|
|
1430
|
-
'Your local changes would be overwritten by checkout.',
|
|
1431
|
-
'Press S to stash changes'
|
|
1432
|
-
);
|
|
1447
|
+
showStashConfirm(`switch to ${branchName}`);
|
|
1433
1448
|
} else {
|
|
1434
1449
|
addLog(`Failed to switch: ${errMsg}`, 'error');
|
|
1435
1450
|
showErrorToast(
|
|
@@ -1481,6 +1496,7 @@ async function pullCurrentBranch() {
|
|
|
1481
1496
|
|
|
1482
1497
|
await execAsync(`git pull "${REMOTE_NAME}" "${branch}"`);
|
|
1483
1498
|
addLog('Pulled successfully', 'success');
|
|
1499
|
+
pendingDirtyOperation = null;
|
|
1484
1500
|
notifyClients();
|
|
1485
1501
|
return { success: true };
|
|
1486
1502
|
} catch (e) {
|
|
@@ -1488,13 +1504,8 @@ async function pullCurrentBranch() {
|
|
|
1488
1504
|
addLog(`Pull failed: ${errMsg}`, 'error');
|
|
1489
1505
|
|
|
1490
1506
|
if (errMsg.includes('local changes') || errMsg.includes('overwritten') || errMsg.includes('uncommitted changes')) {
|
|
1491
|
-
addLog(`Press S to stash changes, or commit manually`, 'warning');
|
|
1492
1507
|
pendingDirtyOperation = { type: 'pull' };
|
|
1493
|
-
|
|
1494
|
-
'Pull Failed',
|
|
1495
|
-
'Your local changes would be overwritten by pull.',
|
|
1496
|
-
'Press S to stash changes'
|
|
1497
|
-
);
|
|
1508
|
+
showStashConfirm('pull');
|
|
1498
1509
|
} else if (isMergeConflict(errMsg)) {
|
|
1499
1510
|
store.setState({ hasMergeConflict: true });
|
|
1500
1511
|
showErrorToast(
|
|
@@ -1535,6 +1546,7 @@ async function stashAndRetry() {
|
|
|
1535
1546
|
|
|
1536
1547
|
pendingDirtyOperation = null;
|
|
1537
1548
|
hideErrorToast();
|
|
1549
|
+
hideStashConfirm();
|
|
1538
1550
|
|
|
1539
1551
|
addLog('Stashing uncommitted changes...', 'update');
|
|
1540
1552
|
render();
|
|
@@ -1556,9 +1568,13 @@ async function stashAndRetry() {
|
|
|
1556
1568
|
const popResult = await gitStashPop();
|
|
1557
1569
|
if (popResult.success) {
|
|
1558
1570
|
addLog('Stashed changes restored', 'info');
|
|
1571
|
+
showFlash('Stashed changes restored (switch failed)');
|
|
1559
1572
|
} else {
|
|
1560
1573
|
addLog('Warning: could not restore stashed changes. Run: git stash pop', 'error');
|
|
1574
|
+
showErrorToast('Stash Pop Failed', 'Could not restore stashed changes.', 'Run: git stash pop');
|
|
1561
1575
|
}
|
|
1576
|
+
} else {
|
|
1577
|
+
showFlash(`Stashed & switched to ${operation.branch}`);
|
|
1562
1578
|
}
|
|
1563
1579
|
await pollGitChanges();
|
|
1564
1580
|
} else if (operation.type === 'pull') {
|
|
@@ -1568,9 +1584,13 @@ async function stashAndRetry() {
|
|
|
1568
1584
|
const popResult = await gitStashPop();
|
|
1569
1585
|
if (popResult.success) {
|
|
1570
1586
|
addLog('Stashed changes restored', 'info');
|
|
1587
|
+
showFlash('Stashed changes restored (pull failed)');
|
|
1571
1588
|
} else {
|
|
1572
1589
|
addLog('Warning: could not restore stashed changes. Run: git stash pop', 'error');
|
|
1590
|
+
showErrorToast('Stash Pop Failed', 'Could not restore stashed changes.', 'Run: git stash pop');
|
|
1573
1591
|
}
|
|
1592
|
+
} else {
|
|
1593
|
+
showFlash('Stashed & pulled successfully');
|
|
1574
1594
|
}
|
|
1575
1595
|
await pollGitChanges();
|
|
1576
1596
|
}
|
|
@@ -2309,6 +2329,52 @@ function setupKeyboardInput() {
|
|
|
2309
2329
|
return; // Ignore other keys in action mode
|
|
2310
2330
|
}
|
|
2311
2331
|
|
|
2332
|
+
// Handle stash confirmation dialog
|
|
2333
|
+
if (store.get('stashConfirmMode')) {
|
|
2334
|
+
if (key === '\u001b[A' || key === 'k') { // Up
|
|
2335
|
+
const idx = store.get('stashConfirmSelectedIndex');
|
|
2336
|
+
if (idx > 0) {
|
|
2337
|
+
store.setState({ stashConfirmSelectedIndex: idx - 1 });
|
|
2338
|
+
render();
|
|
2339
|
+
}
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2342
|
+
if (key === '\u001b[B' || key === 'j') { // Down
|
|
2343
|
+
const idx = store.get('stashConfirmSelectedIndex');
|
|
2344
|
+
if (idx < 1) {
|
|
2345
|
+
store.setState({ stashConfirmSelectedIndex: idx + 1 });
|
|
2346
|
+
render();
|
|
2347
|
+
}
|
|
2348
|
+
return;
|
|
2349
|
+
}
|
|
2350
|
+
if (key === '\r' || key === '\n') { // Enter — execute selected option
|
|
2351
|
+
const idx = store.get('stashConfirmSelectedIndex');
|
|
2352
|
+
hideStashConfirm();
|
|
2353
|
+
if (idx === 0 && pendingDirtyOperation) {
|
|
2354
|
+
await stashAndRetry();
|
|
2355
|
+
} else {
|
|
2356
|
+
addLog('Stash cancelled — handle changes manually', 'info');
|
|
2357
|
+
pendingDirtyOperation = null;
|
|
2358
|
+
}
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
if (key === 'S') { // S shortcut — stash directly
|
|
2362
|
+
hideStashConfirm();
|
|
2363
|
+
if (pendingDirtyOperation) {
|
|
2364
|
+
await stashAndRetry();
|
|
2365
|
+
}
|
|
2366
|
+
return;
|
|
2367
|
+
}
|
|
2368
|
+
if (key === '\u001b') { // Escape — cancel
|
|
2369
|
+
hideStashConfirm();
|
|
2370
|
+
addLog('Stash cancelled — handle changes manually', 'info');
|
|
2371
|
+
pendingDirtyOperation = null;
|
|
2372
|
+
render();
|
|
2373
|
+
return;
|
|
2374
|
+
}
|
|
2375
|
+
return; // Ignore other keys in stash confirm mode
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2312
2378
|
// Dismiss flash on any key
|
|
2313
2379
|
if (store.get('flashMessage')) {
|
|
2314
2380
|
hideFlash();
|
|
@@ -2324,7 +2390,6 @@ function setupKeyboardInput() {
|
|
|
2324
2390
|
return;
|
|
2325
2391
|
}
|
|
2326
2392
|
hideErrorToast();
|
|
2327
|
-
pendingDirtyOperation = null;
|
|
2328
2393
|
if (key !== '\u001b[A' && key !== '\u001b[B' && key !== '\r' && key !== 'q') {
|
|
2329
2394
|
return;
|
|
2330
2395
|
}
|
|
@@ -2475,9 +2540,14 @@ function setupKeyboardInput() {
|
|
|
2475
2540
|
break;
|
|
2476
2541
|
}
|
|
2477
2542
|
|
|
2478
|
-
case 'S': // Stash changes
|
|
2543
|
+
case 'S': // Stash changes — open confirm dialog or show hint
|
|
2479
2544
|
if (pendingDirtyOperation) {
|
|
2480
|
-
|
|
2545
|
+
const label = pendingDirtyOperation.type === 'switch'
|
|
2546
|
+
? `switch to ${pendingDirtyOperation.branch}`
|
|
2547
|
+
: 'pull';
|
|
2548
|
+
showStashConfirm(label);
|
|
2549
|
+
} else {
|
|
2550
|
+
showFlash('No pending operation — stash with S after a failed switch or pull');
|
|
2481
2551
|
}
|
|
2482
2552
|
break;
|
|
2483
2553
|
|
package/package.json
CHANGED