git-watchtower 1.13.0 → 1.14.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.
@@ -1583,8 +1583,33 @@ async function pullCurrentBranch() {
1583
1583
  addLog(`Pulling from ${REMOTE_NAME}/${branch}...`, 'update');
1584
1584
  render();
1585
1585
 
1586
- await execGit(['pull', REMOTE_NAME, branch], { cwd: PROJECT_ROOT, timeout: 60000 });
1587
- addLog('Pulled successfully', 'success');
1586
+ // Capture HEAD before pull so we can diff against it when git pull
1587
+ // doesn't put a clean "already up to date" message on stdout.
1588
+ const preHead = await execGitSilent(['rev-parse', 'HEAD'], { cwd: PROJECT_ROOT });
1589
+ const oldCommit = preHead && preHead.stdout ? preHead.stdout.trim() : null;
1590
+
1591
+ const result = await execGit(['pull', REMOTE_NAME, branch], { cwd: PROJECT_ROOT, timeout: 60000 });
1592
+ const pullOutput = `${result.stdout || ''}\n${result.stderr || ''}`;
1593
+
1594
+ if (/already up[- ]to[- ]date/i.test(pullOutput)) {
1595
+ addLog(`Already up to date with ${REMOTE_NAME}/${branch}`, 'success');
1596
+ } else {
1597
+ // Prefer the summary line from git's own output (it's locale-sensitive
1598
+ // but matches how git reports its work). Fall back to a diff against
1599
+ // the old HEAD when git's summary line is missing (e.g. merge commit
1600
+ // without --stat).
1601
+ let summary = '';
1602
+ const diffStats = parseDiffStats(pullOutput);
1603
+ if (diffStats.added || diffStats.deleted) {
1604
+ summary = ` (+${diffStats.added}/-${diffStats.deleted})`;
1605
+ } else if (oldCommit) {
1606
+ const fallback = await getDiffStats(oldCommit, 'HEAD');
1607
+ if (fallback.added || fallback.deleted) {
1608
+ summary = ` (+${fallback.added}/-${fallback.deleted})`;
1609
+ }
1610
+ }
1611
+ addLog(`Pulled ${REMOTE_NAME}/${branch}${summary}`, 'success');
1612
+ }
1588
1613
  pendingDirtyOperation = null;
1589
1614
  notifyClients();
1590
1615
  return { success: true };
@@ -1952,7 +1977,6 @@ async function pollGitChanges() {
1952
1977
 
1953
1978
  try {
1954
1979
  await execGit(['pull', REMOTE_NAME, autoPullBranchName], { cwd: PROJECT_ROOT, timeout: 60000 });
1955
- addLog(`Pulled successfully from ${autoPullBranchName}`, 'success');
1956
1980
  currentInfo.hasUpdates = false;
1957
1981
  // Update the stored commit to the new one
1958
1982
  const newCommit = await execGit(['rev-parse', '--short', 'HEAD'], { cwd: PROJECT_ROOT });
@@ -1962,9 +1986,10 @@ async function pollGitChanges() {
1962
1986
  // Reload browsers
1963
1987
  notifyClients();
1964
1988
 
1965
- // Calculate actual diff for stats tracking
1989
+ // Calculate actual diff for stats tracking + status message
1990
+ let diffStats = { added: 0, deleted: 0 };
1966
1991
  if (oldCommit) {
1967
- const diffStats = await getDiffStats(oldCommit, 'HEAD');
1992
+ diffStats = await getDiffStats(oldCommit, 'HEAD');
1968
1993
  const totalLines = diffStats.added + diffStats.deleted;
1969
1994
  // Always track session churn
1970
1995
  sessionStats.recordChurn(diffStats.added, diffStats.deleted);
@@ -1978,6 +2003,12 @@ async function pollGitChanges() {
1978
2003
  }
1979
2004
  }
1980
2005
  }
2006
+
2007
+ // Indicate what was updated so the log isn't just a generic "success"
2008
+ const summary = (diffStats.added || diffStats.deleted)
2009
+ ? ` (+${diffStats.added}/-${diffStats.deleted})`
2010
+ : '';
2011
+ addLog(`Auto-pulled ${autoPullBranchName}${summary}`, 'success');
1981
2012
  } catch (e) {
1982
2013
  const errMsg = e.stderr || e.stdout || e.message || String(e);
1983
2014
  if (isMergeConflict(errMsg)) {
@@ -3352,6 +3383,14 @@ function restartProcess() {
3352
3383
  }
3353
3384
  stopWebDashboard();
3354
3385
 
3386
+ // Release the per-repo monitor lock before spawning the replacement, so the
3387
+ // child can acquire it. The parent stays alive waiting on child.on('close'),
3388
+ // so without this the child sees the parent as an active owner and refuses.
3389
+ if (monitorLockFile) {
3390
+ try { monitorLock.release(monitorLockFile); } catch (_) { /* ignore */ }
3391
+ monitorLockFile = null;
3392
+ }
3393
+
3355
3394
  console.log('\n♻ Restarting git-watchtower...\n');
3356
3395
 
3357
3396
  const { spawn: spawnChild } = require('child_process');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-watchtower",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
4
4
  "description": "Terminal-based Git branch monitor with activity sparklines and optional dev server with live reload",
5
5
  "main": "bin/git-watchtower.js",
6
6
  "bin": {