git-watchtower 1.9.2 → 1.9.4
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 +19 -4
- package/package.json +1 -1
- package/src/polling/engine.js +33 -0
package/bin/git-watchtower.js
CHANGED
|
@@ -82,6 +82,7 @@ const { parseGitHubPr, parseGitLabMr, parseGitHubPrList, parseGitLabMrList, isBa
|
|
|
82
82
|
// Security & Validation (imported from src/git/branch.js and src/git/commands.js)
|
|
83
83
|
// ============================================================================
|
|
84
84
|
const { isValidBranchName, sanitizeBranchName, getGoneBranches, deleteGoneBranches } = require('../src/git/branch');
|
|
85
|
+
const { pruneStaleEntries } = require('../src/polling/engine');
|
|
85
86
|
const { isGitAvailable: checkGitAvailable, execGit, execGitSilent, getDiffStats: getDiffStatsSafe, getAheadBehind, getDiffShortstat } = require('../src/git/commands');
|
|
86
87
|
|
|
87
88
|
// Session stats (always-on, non-casino stats)
|
|
@@ -1423,13 +1424,16 @@ async function getAllBranches() {
|
|
|
1423
1424
|
const seenBranches = new Set();
|
|
1424
1425
|
|
|
1425
1426
|
// Get local branches
|
|
1427
|
+
// Use \x1f (Unit Separator) as delimiter since | can appear in commit subjects
|
|
1428
|
+
const delimiter = '\x1f';
|
|
1426
1429
|
const { stdout: localOutput } = await execGit(
|
|
1427
|
-
['for-each-ref', '--sort=-committerdate',
|
|
1430
|
+
['for-each-ref', '--sort=-committerdate', `--format=%(refname:short)${delimiter}%(committerdate:iso8601)${delimiter}%(objectname:short)${delimiter}%(subject)`, 'refs/heads/'],
|
|
1428
1431
|
{ cwd: PROJECT_ROOT }
|
|
1429
1432
|
);
|
|
1430
1433
|
|
|
1431
1434
|
for (const line of localOutput.split('\n').filter(Boolean)) {
|
|
1432
|
-
const [name, dateStr, commit,
|
|
1435
|
+
const [name, dateStr, commit, ...subjectParts] = line.split(delimiter);
|
|
1436
|
+
const subject = subjectParts.join(delimiter);
|
|
1433
1437
|
if (!seenBranches.has(name) && isValidBranchName(name)) {
|
|
1434
1438
|
seenBranches.add(name);
|
|
1435
1439
|
branchList.push({
|
|
@@ -1446,14 +1450,15 @@ async function getAllBranches() {
|
|
|
1446
1450
|
|
|
1447
1451
|
// Get remote branches (using configured remote name)
|
|
1448
1452
|
const remoteResult = await execGitSilent(
|
|
1449
|
-
['for-each-ref', '--sort=-committerdate',
|
|
1453
|
+
['for-each-ref', '--sort=-committerdate', `--format=%(refname:short)${delimiter}%(committerdate:iso8601)${delimiter}%(objectname:short)${delimiter}%(subject)`, `refs/remotes/${REMOTE_NAME}/`],
|
|
1450
1454
|
{ cwd: PROJECT_ROOT }
|
|
1451
1455
|
);
|
|
1452
1456
|
const remoteOutput = remoteResult ? remoteResult.stdout : '';
|
|
1453
1457
|
|
|
1454
1458
|
const remotePrefix = `${REMOTE_NAME}/`;
|
|
1455
1459
|
for (const line of remoteOutput.split('\n').filter(Boolean)) {
|
|
1456
|
-
const [fullName, dateStr, commit,
|
|
1460
|
+
const [fullName, dateStr, commit, ...subjectParts] = line.split(delimiter);
|
|
1461
|
+
const subject = subjectParts.join(delimiter);
|
|
1457
1462
|
const name = fullName.replace(remotePrefix, '');
|
|
1458
1463
|
if (name === 'HEAD') continue;
|
|
1459
1464
|
if (!isValidBranchName(name)) continue;
|
|
@@ -1817,6 +1822,16 @@ async function pollGitChanges() {
|
|
|
1817
1822
|
}
|
|
1818
1823
|
}
|
|
1819
1824
|
|
|
1825
|
+
// Prune stale entries: remove branches from tracking sets/caches
|
|
1826
|
+
// that no longer exist in git (deleted >30s ago or already gone)
|
|
1827
|
+
pruneStaleEntries({
|
|
1828
|
+
knownBranchNames,
|
|
1829
|
+
fetchedBranchNames,
|
|
1830
|
+
allBranches,
|
|
1831
|
+
caches: [previousBranchStates, prInfoCache, store.get('sparklineCache'), store.get('aheadBehindCache')],
|
|
1832
|
+
now,
|
|
1833
|
+
});
|
|
1834
|
+
|
|
1820
1835
|
// Note: isNew flag is only cleared when branch becomes current (see below)
|
|
1821
1836
|
|
|
1822
1837
|
// Keep deleted branches in the list (don't remove them)
|
package/package.json
CHANGED
package/src/polling/engine.js
CHANGED
|
@@ -147,6 +147,38 @@ function restoreSelection(branches, previousName, previousIndex) {
|
|
|
147
147
|
return { selectedIndex: previousIndex, selectedBranchName: previousName };
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Prune stale entries from tracking sets and caches for branches
|
|
152
|
+
* that no longer exist in git.
|
|
153
|
+
* @param {Object} opts
|
|
154
|
+
* @param {Set<string>} opts.knownBranchNames - Known branch names (mutated)
|
|
155
|
+
* @param {Set<string>} opts.fetchedBranchNames - Currently fetched branch names
|
|
156
|
+
* @param {Array<{name: string, isDeleted?: boolean, deletedAt?: number}>} opts.allBranches - All branches including deleted
|
|
157
|
+
* @param {Map<string, *>[]} opts.caches - Maps to prune stale keys from
|
|
158
|
+
* @param {number} [opts.retentionMs=30000] - How long to keep deleted branches before pruning
|
|
159
|
+
* @param {number} [opts.now] - Current timestamp (defaults to Date.now())
|
|
160
|
+
* @returns {string[]} Names of pruned branches
|
|
161
|
+
*/
|
|
162
|
+
function pruneStaleEntries({ knownBranchNames, fetchedBranchNames, allBranches, caches, retentionMs = 30000, now }) {
|
|
163
|
+
const timestamp = now ?? Date.now();
|
|
164
|
+
const pruned = [];
|
|
165
|
+
for (const knownName of [...knownBranchNames]) {
|
|
166
|
+
if (fetchedBranchNames.has(knownName)) continue;
|
|
167
|
+
const deletedBranch = allBranches.find(b => b.name === knownName && b.isDeleted);
|
|
168
|
+
const shouldPrune = deletedBranch
|
|
169
|
+
? (deletedBranch.deletedAt && (timestamp - deletedBranch.deletedAt) > retentionMs)
|
|
170
|
+
: true; // Not in allBranches at all — stale entry
|
|
171
|
+
if (shouldPrune) {
|
|
172
|
+
knownBranchNames.delete(knownName);
|
|
173
|
+
for (const cache of caches) {
|
|
174
|
+
cache.delete(knownName);
|
|
175
|
+
}
|
|
176
|
+
pruned.push(knownName);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return pruned;
|
|
180
|
+
}
|
|
181
|
+
|
|
150
182
|
module.exports = {
|
|
151
183
|
detectNewBranches,
|
|
152
184
|
detectDeletedBranches,
|
|
@@ -154,4 +186,5 @@ module.exports = {
|
|
|
154
186
|
sortBranches,
|
|
155
187
|
calculateAdaptiveInterval,
|
|
156
188
|
restoreSelection,
|
|
189
|
+
pruneStaleEntries,
|
|
157
190
|
};
|