git-watchtower 1.13.1 → 1.14.1

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)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-watchtower",
3
- "version": "1.13.1",
3
+ "version": "1.14.1",
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": {
@@ -198,7 +198,13 @@ async function log(branchName, options = {}) {
198
198
  }
199
199
 
200
200
  /**
201
- * Get commit count by day for sparkline
201
+ * Get commit count by day for sparkline.
202
+ *
203
+ * Buckets commits by local calendar date rather than by dividing a ms
204
+ * difference by 86 400 000, which breaks on DST transitions (a
205
+ * spring-forward day is only 23 h, causing Math.floor(23/24) = 0 and
206
+ * merging yesterday's commits into today's bucket).
207
+ *
202
208
  * @param {string} branchName - Branch name
203
209
  * @param {number} [days=7] - Number of days
204
210
  * @param {string} [cwd] - Working directory
@@ -215,15 +221,23 @@ async function getCommitsByDay(branchName, days = 7, cwd) {
215
221
 
216
222
  if (!stdout) return counts;
217
223
 
224
+ // Build a map from "YYYY-MM-DD" → bucket index. Using setDate()
225
+ // to step backwards is DST-safe because it adjusts the calendar
226
+ // day without relying on a fixed ms offset.
218
227
  const today = new Date();
219
- today.setHours(0, 0, 0, 0);
228
+ const dayBuckets = new Map();
229
+ for (let i = 0; i < days; i++) {
230
+ const d = new Date(today.getFullYear(), today.getMonth(), today.getDate() - i);
231
+ const key = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
232
+ dayBuckets.set(key, days - 1 - i);
233
+ }
220
234
 
221
235
  for (const line of stdout.split('\n').filter(Boolean)) {
222
236
  const commitDate = new Date(line);
223
- commitDate.setHours(0, 0, 0, 0);
224
- const daysDiff = Math.floor((today.getTime() - commitDate.getTime()) / (1000 * 60 * 60 * 24));
225
- if (daysDiff >= 0 && daysDiff < days) {
226
- counts[days - 1 - daysDiff]++;
237
+ const key = `${commitDate.getFullYear()}-${String(commitDate.getMonth() + 1).padStart(2, '0')}-${String(commitDate.getDate()).padStart(2, '0')}`;
238
+ const idx = dayBuckets.get(key);
239
+ if (idx !== undefined) {
240
+ counts[idx]++;
227
241
  }
228
242
  }
229
243
  } catch (error) {