git-watchtower 1.9.3 → 1.9.5
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 +14 -130
- package/package.json +1 -1
- package/src/polling/engine.js +2 -0
package/bin/git-watchtower.js
CHANGED
|
@@ -81,9 +81,9 @@ const { parseGitHubPr, parseGitLabMr, parseGitHubPrList, parseGitLabMrList, isBa
|
|
|
81
81
|
// ============================================================================
|
|
82
82
|
// Security & Validation (imported from src/git/branch.js and src/git/commands.js)
|
|
83
83
|
// ============================================================================
|
|
84
|
-
const { isValidBranchName, sanitizeBranchName, getGoneBranches, deleteGoneBranches } = require('../src/git/branch');
|
|
84
|
+
const { isValidBranchName, sanitizeBranchName, getGoneBranches, deleteGoneBranches, getCurrentBranch: getCurrentBranchRaw, getAllBranches: getAllBranchesRaw } = require('../src/git/branch');
|
|
85
85
|
const { pruneStaleEntries } = require('../src/polling/engine');
|
|
86
|
-
const { isGitAvailable: checkGitAvailable, execGit, execGitSilent, getDiffStats: getDiffStatsSafe, getAheadBehind, getDiffShortstat } = require('../src/git/commands');
|
|
86
|
+
const { isGitAvailable: checkGitAvailable, execGit, execGitSilent, getDiffStats: getDiffStatsSafe, getAheadBehind, getDiffShortstat, hasUncommittedChanges: checkUncommittedChanges } = require('../src/git/commands');
|
|
87
87
|
|
|
88
88
|
// Session stats (always-on, non-casino stats)
|
|
89
89
|
const sessionStats = require('../src/stats/session');
|
|
@@ -743,8 +743,9 @@ const actions = require('../src/ui/actions');
|
|
|
743
743
|
// Diff stats parsing and stash imported from src/git/commands.js
|
|
744
744
|
const { parseDiffStats, stash: gitStash, stashPop: gitStashPop } = require('../src/git/commands');
|
|
745
745
|
|
|
746
|
-
// Server process command parsing
|
|
746
|
+
// Server process command parsing and static server utilities
|
|
747
747
|
const { parseCommand } = require('../src/server/process');
|
|
748
|
+
const { getMimeType, injectLiveReload } = require('../src/server/static');
|
|
748
749
|
|
|
749
750
|
// State (non-store globals)
|
|
750
751
|
let previousBranchStates = new Map(); // branch name -> commit hash
|
|
@@ -781,39 +782,7 @@ const MAX_HISTORY = 20;
|
|
|
781
782
|
let lastSparklineUpdate = 0;
|
|
782
783
|
const SPARKLINE_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
783
784
|
|
|
784
|
-
//
|
|
785
|
-
const MIME_TYPES = {
|
|
786
|
-
'.html': 'text/html',
|
|
787
|
-
'.css': 'text/css',
|
|
788
|
-
'.js': 'application/javascript',
|
|
789
|
-
'.json': 'application/json',
|
|
790
|
-
'.png': 'image/png',
|
|
791
|
-
'.jpg': 'image/jpeg',
|
|
792
|
-
'.jpeg': 'image/jpeg',
|
|
793
|
-
'.gif': 'image/gif',
|
|
794
|
-
'.svg': 'image/svg+xml',
|
|
795
|
-
'.ico': 'image/x-icon',
|
|
796
|
-
'.webp': 'image/webp',
|
|
797
|
-
'.woff': 'font/woff',
|
|
798
|
-
'.woff2': 'font/woff2',
|
|
799
|
-
'.ttf': 'font/ttf',
|
|
800
|
-
'.xml': 'application/xml',
|
|
801
|
-
'.txt': 'text/plain',
|
|
802
|
-
'.md': 'text/markdown',
|
|
803
|
-
'.pdf': 'application/pdf',
|
|
804
|
-
};
|
|
805
|
-
|
|
806
|
-
// Live reload script
|
|
807
|
-
const LIVE_RELOAD_SCRIPT = `
|
|
808
|
-
<script>
|
|
809
|
-
(function() {
|
|
810
|
-
var source = new EventSource('/livereload');
|
|
811
|
-
source.onmessage = function(e) {
|
|
812
|
-
if (e.data === 'reload') location.reload();
|
|
813
|
-
};
|
|
814
|
-
})();
|
|
815
|
-
</script>
|
|
816
|
-
</body>`;
|
|
785
|
+
// MIME_TYPES and LIVE_RELOAD_SCRIPT imported from src/server/static.js (via getMimeType and injectLiveReload)
|
|
817
786
|
|
|
818
787
|
// ============================================================================
|
|
819
788
|
// Utility Functions
|
|
@@ -1379,20 +1348,9 @@ function hideStashConfirm() {
|
|
|
1379
1348
|
// ============================================================================
|
|
1380
1349
|
|
|
1381
1350
|
async function getCurrentBranch() {
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
if (stdout === 'HEAD') {
|
|
1386
|
-
store.setState({ isDetachedHead: true });
|
|
1387
|
-
// Get the short commit hash instead
|
|
1388
|
-
const { stdout: commitHash } = await execGit(['rev-parse', '--short', 'HEAD'], { cwd: PROJECT_ROOT });
|
|
1389
|
-
return `HEAD@${commitHash}`;
|
|
1390
|
-
}
|
|
1391
|
-
store.setState({ isDetachedHead: false });
|
|
1392
|
-
return stdout;
|
|
1393
|
-
} catch (e) {
|
|
1394
|
-
return null;
|
|
1395
|
-
}
|
|
1351
|
+
const result = await getCurrentBranchRaw(PROJECT_ROOT);
|
|
1352
|
+
store.setState({ isDetachedHead: result.isDetached });
|
|
1353
|
+
return result.name;
|
|
1396
1354
|
}
|
|
1397
1355
|
|
|
1398
1356
|
async function checkRemoteExists() {
|
|
@@ -1406,87 +1364,14 @@ async function checkRemoteExists() {
|
|
|
1406
1364
|
}
|
|
1407
1365
|
|
|
1408
1366
|
async function hasUncommittedChanges() {
|
|
1409
|
-
|
|
1410
|
-
const { stdout } = await execGit(['status', '--porcelain'], { cwd: PROJECT_ROOT, timeout: 5000 });
|
|
1411
|
-
return stdout.length > 0;
|
|
1412
|
-
} catch (e) {
|
|
1413
|
-
return false;
|
|
1414
|
-
}
|
|
1367
|
+
return checkUncommittedChanges(PROJECT_ROOT);
|
|
1415
1368
|
}
|
|
1416
1369
|
|
|
1417
1370
|
// isAuthError, isMergeConflict, isNetworkError imported from src/utils/errors.js
|
|
1418
1371
|
|
|
1419
1372
|
async function getAllBranches() {
|
|
1420
1373
|
try {
|
|
1421
|
-
await
|
|
1422
|
-
|
|
1423
|
-
const branchList = [];
|
|
1424
|
-
const seenBranches = new Set();
|
|
1425
|
-
|
|
1426
|
-
// Get local branches
|
|
1427
|
-
const { stdout: localOutput } = await execGit(
|
|
1428
|
-
['for-each-ref', '--sort=-committerdate', '--format=%(refname:short)|%(committerdate:iso8601)|%(objectname:short)|%(subject)', 'refs/heads/'],
|
|
1429
|
-
{ cwd: PROJECT_ROOT }
|
|
1430
|
-
);
|
|
1431
|
-
|
|
1432
|
-
for (const line of localOutput.split('\n').filter(Boolean)) {
|
|
1433
|
-
const [name, dateStr, commit, subject] = line.split('|');
|
|
1434
|
-
if (!seenBranches.has(name) && isValidBranchName(name)) {
|
|
1435
|
-
seenBranches.add(name);
|
|
1436
|
-
branchList.push({
|
|
1437
|
-
name,
|
|
1438
|
-
commit,
|
|
1439
|
-
subject: subject || '',
|
|
1440
|
-
date: new Date(dateStr),
|
|
1441
|
-
isLocal: true,
|
|
1442
|
-
hasRemote: false,
|
|
1443
|
-
hasUpdates: false,
|
|
1444
|
-
});
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
// Get remote branches (using configured remote name)
|
|
1449
|
-
const remoteResult = await execGitSilent(
|
|
1450
|
-
['for-each-ref', '--sort=-committerdate', '--format=%(refname:short)|%(committerdate:iso8601)|%(objectname:short)|%(subject)', `refs/remotes/${REMOTE_NAME}/`],
|
|
1451
|
-
{ cwd: PROJECT_ROOT }
|
|
1452
|
-
);
|
|
1453
|
-
const remoteOutput = remoteResult ? remoteResult.stdout : '';
|
|
1454
|
-
|
|
1455
|
-
const remotePrefix = `${REMOTE_NAME}/`;
|
|
1456
|
-
for (const line of remoteOutput.split('\n').filter(Boolean)) {
|
|
1457
|
-
const [fullName, dateStr, commit, subject] = line.split('|');
|
|
1458
|
-
const name = fullName.replace(remotePrefix, '');
|
|
1459
|
-
if (name === 'HEAD') continue;
|
|
1460
|
-
if (!isValidBranchName(name)) continue;
|
|
1461
|
-
|
|
1462
|
-
const existing = branchList.find(b => b.name === name);
|
|
1463
|
-
if (existing) {
|
|
1464
|
-
existing.hasRemote = true;
|
|
1465
|
-
existing.remoteCommit = commit;
|
|
1466
|
-
existing.remoteDate = new Date(dateStr);
|
|
1467
|
-
existing.remoteSubject = subject || '';
|
|
1468
|
-
if (commit !== existing.commit) {
|
|
1469
|
-
existing.hasUpdates = true;
|
|
1470
|
-
// Use remote's date when it has updates (so it sorts to top)
|
|
1471
|
-
existing.date = new Date(dateStr);
|
|
1472
|
-
existing.subject = subject || existing.subject;
|
|
1473
|
-
}
|
|
1474
|
-
} else if (!seenBranches.has(name)) {
|
|
1475
|
-
seenBranches.add(name);
|
|
1476
|
-
branchList.push({
|
|
1477
|
-
name,
|
|
1478
|
-
commit,
|
|
1479
|
-
subject: subject || '',
|
|
1480
|
-
date: new Date(dateStr),
|
|
1481
|
-
isLocal: false,
|
|
1482
|
-
hasRemote: true,
|
|
1483
|
-
hasUpdates: false,
|
|
1484
|
-
});
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
branchList.sort((a, b) => b.date - a.date);
|
|
1489
|
-
return branchList; // Return all branches, caller will slice
|
|
1374
|
+
return await getAllBranchesRaw({ remoteName: REMOTE_NAME, fetch: true, cwd: PROJECT_ROOT });
|
|
1490
1375
|
} catch (e) {
|
|
1491
1376
|
addLog(`Failed to get branches: ${e.message || e}`, 'error');
|
|
1492
1377
|
return [];
|
|
@@ -1837,6 +1722,8 @@ async function pollGitChanges() {
|
|
|
1837
1722
|
const updatedBranches = [];
|
|
1838
1723
|
const currentBranchName = store.get('currentBranch');
|
|
1839
1724
|
for (const branch of pollFilteredBranches) {
|
|
1725
|
+
// Clear previous cycle's flag so only freshly-updated branches are highlighted
|
|
1726
|
+
branch.justUpdated = false;
|
|
1840
1727
|
if (branch.isDeleted) continue;
|
|
1841
1728
|
const prevCommit = previousBranchStates.get(branch.name);
|
|
1842
1729
|
if (prevCommit && prevCommit !== branch.commit && branch.name !== currentBranchName) {
|
|
@@ -2105,7 +1992,7 @@ function handleLiveReload(req, res) {
|
|
|
2105
1992
|
|
|
2106
1993
|
function serveFile(res, filePath, logPath) {
|
|
2107
1994
|
const ext = path.extname(filePath).toLowerCase();
|
|
2108
|
-
const mimeType =
|
|
1995
|
+
const mimeType = getMimeType(ext);
|
|
2109
1996
|
|
|
2110
1997
|
fs.readFile(filePath, (err, data) => {
|
|
2111
1998
|
if (err) {
|
|
@@ -2116,10 +2003,7 @@ function serveFile(res, filePath, logPath) {
|
|
|
2116
2003
|
}
|
|
2117
2004
|
|
|
2118
2005
|
if (mimeType === 'text/html') {
|
|
2119
|
-
|
|
2120
|
-
if (html.includes('</body>')) {
|
|
2121
|
-
html = html.replace('</body>', LIVE_RELOAD_SCRIPT);
|
|
2122
|
-
}
|
|
2006
|
+
const html = injectLiveReload(data.toString());
|
|
2123
2007
|
res.writeHead(200, { 'Content-Type': mimeType });
|
|
2124
2008
|
res.end(html);
|
|
2125
2009
|
} else {
|
package/package.json
CHANGED
package/src/polling/engine.js
CHANGED
|
@@ -57,6 +57,8 @@ function detectDeletedBranches(knownBranchNames, fetchedBranchNames, existingBra
|
|
|
57
57
|
function detectUpdatedBranches(branches, previousStates, currentBranch) {
|
|
58
58
|
const updated = [];
|
|
59
59
|
for (const branch of branches) {
|
|
60
|
+
// Clear previous cycle's flag so only freshly-updated branches are highlighted
|
|
61
|
+
branch.justUpdated = false;
|
|
60
62
|
if (branch.isDeleted) continue;
|
|
61
63
|
const prevCommit = previousStates.get(branch.name);
|
|
62
64
|
if (prevCommit && prevCommit !== branch.commit && branch.name !== currentBranch) {
|