Skip to content

fix: --base mode reads the base ref's tree; --since distinguishes empty-repo from no-match - #1

Merged
NovaLux12 merged 1 commit into
mainfrom
fix/base-mode-uses-base-tree
Jun 28, 2026
Merged

fix: --base mode reads the base ref's tree; --since distinguishes empty-repo from no-match#1
NovaLux12 merged 1 commit into
mainfrom
fix/base-mode-uses-base-tree

Conversation

@NovaLux12

Copy link
Copy Markdown
Owner

What this PR does

Two real bugs in the --base compare path, one in the --since empty-result path, and a related timezone-ambiguity fix in the --since / --until argument format.

1. --base mode was reading the working tree, not the base ref's tree

With dig --base v1.0, the "language line deltas sorted by magnitude" and the len(baseFilesAtHEAD) file-count metric were derived from the user's uncommitted working tree versus itself, not from v1.0 versus HEAD. baseCommits / baseContributors / baseHotFiles did use baseRef correctly, so the inconsistency made the commit / contributor / hot-file columns right but the language column and the file-count metric wrong.

Root cause: git.FilesAtHEAD and git.LinesByFile were both called with no ref argument in main.go:177/182. FilesAtHEAD is OK (it shells git ls-tree -r --name-only HEAD β€” that's the ref's committed tree, not the working tree). LinesByFile is the offender: it walks the file list and then reads content via os.ReadFile(filepath.Join(repoPath, rel)) β€” that's the working tree, not even HEAD's blobs.

Fix: add FilesAtRef(repoPath, ref) and LinesByFileAtRef(repoPath, ref). The new file-lister is the same as FilesAtHEAD but takes an explicit ref (with HEAD as the default). The new line-counter walks the ref's tree, resolves each path's OID via git ls-tree <ref> -- <path>, and reads the blob content via git cat-file blob <oid>. The existing FilesAtHEAD and LinesByFile are now thin wrappers (HEAD) and marked Deprecated in the doc for the working-tree caller. main.go's --base branch now uses the ref-parameterised versions explicitly.

Side cleanup: the dead var _ = time.Time{} placeholder in helpers.go is removed (it was the only consumer of the time import; the new code doesn't need it, so the import is dropped too).

2. --since 2099-01-01 on a real repo used to say "repository has no commits" and exit 1

The repo is fine; --since just filtered everything out. The two failure modes are now distinguished:

  • empty repo (no --since): "repository has no commits" + exit 1 (unchanged)
  • --since with no matches: "no commits match --since=<ts> (try an earlier date or drop --since)" + exit 64 (usage)

3. log.go's --since= / --until= argument to git was missing the Z

The time was already converted to UTC at opts.Since.UTC(), but the format string was "2006-01-02T15:04:05" (no Z). The bare wall-clock form is ambiguous β€” git on some systems interprets it as local time of the host running dig. Append the Z so the argument is unambiguous. The H2 fix's error message format string was updated in lockstep.

Verification

  • go build ./... β€” green
  • go vet ./... β€” green
  • go test ./... β€” 3 packages ok, 1 no tests
  • dig --version β†’ "dig dev"
  • dig --out /tmp/dig-self.html . on this repo β†’ 19,067-byte single-ref report
  • dig --base 85e8268 --out /tmp/dig-compare.html . β†’ 22,482-byte delta report (new code path)
  • dig --since 2030-01-01T00:00:00Z . β†’ "no commits match --since=2030-01-01T00:00:00Z (try an earlier date or drop --since)" + exit 64 (new H2 behaviour)

Risk

  • FilesAtHEAD and LinesByFile behaviour is unchanged when called as before (still HEAD, still working-tree-bytes). The deprecation notes are comments only.
  • LinesByFileAtRef shells one extra git ls-tree <ref> -- <path> and one git cat-file blob <oid> per file in the base's tree. For a small-to-medium repo this is a few extra seconds in --base mode only. Not used in the single-ref path.
  • The Z suffix in --since / --until is a behaviour change for callers that passed local-time strings (impossible β€” parseSince already converts to UTC before this code runs) or relied on the no-zone form being interpreted as their local zone (that was the bug).

… --since distinguishes empty-repo from no-match

Two real bugs in the --base compare path and one in the --since
empty-result path, plus a related timezone-ambiguity fix in the
--since/--until argument format.

1. --base compare mode was reading the working tree for the file list
   AND the working tree for the file content. With --base v1.0, the
   'language line deltas sorted by magnitude' and the
   'len(baseFilesAtHEAD)' file count were derived from the user's
   uncommitted working tree versus itself, not from v1.0 versus HEAD.
   baseCommits/baseContributors/baseHotFiles used baseRef correctly,
   so the inconsistency made the commit/contributor/hot-file columns
   right but the language column and the file-count metric wrong.

   Fix: add FilesAtRef(repoPath, ref) and LinesByFileAtRef(repoPath,
   ref). FilesAtRef uses 'git ls-tree -r --name-only <ref>' (the
   committed tree of the ref, not the working tree).
   LinesByFileAtRef walks the ref's tree, resolves each path's OID
   via 'git ls-tree <ref> -- <path>', and reads the blob content
   via 'git cat-file blob <oid>' so the working tree is irrelevant.
   The existing FilesAtHEAD and LinesByFile are now thin wrappers
   around the new functions (HEAD) and marked Deprecated in the doc
   for the working-tree caller. main.go's --base branch now uses the
   ref-parameterised versions explicitly.

   The dead 'var _ = time.Time{}' placeholder in helpers.go is
   removed (it was the only consumer of the 'time' import; the new
   code doesn't need it, so the import is dropped too).

2. --since 2099-01-01 (or any future date) on a real repo used to
   print 'repository has no commits' and exit 1 (exitNotARepo). The
   repo is fine; --since just filtered everything out. The two
   failure modes are now distinguished: empty repo (no --since)
   keeps the original 'repository has no commits' + exit 1; --since
   with no matches prints 'no commits match --since=<ts> (try an
   earlier date or drop --since)' and exits 64 (exitUsage).

3. Related fix: log.go's --since=/--until= argument to git was
   formatted as '2006-01-02T15:04:05' (no Z) even though the time
   was already converted to UTC. The bare wall-clock form is
   ambiguous β€” git on some systems interprets it as local time of
   the host running dig. Append the Z so the argument is
   unambiguous. The H2 fix's error message format string was
   updated in lockstep.

go build, go vet, go test ./... all green. Verified with a real
binary: --base at commit 85e8268 produces a 22K delta report;
--since=2030-01-01T00:00:00Z prints the new no-match message and
exits 64.
@NovaLux12
NovaLux12 merged commit ac852a7 into main Jun 28, 2026
1 check failed
@NovaLux12
NovaLux12 deleted the fix/base-mode-uses-base-tree branch June 28, 2026 22:17
NovaLux12 added a commit that referenced this pull request Jul 2, 2026
… --since distinguishes empty-repo from no-match (#1)

Two real bugs in the --base compare path and one in the --since
empty-result path, plus a related timezone-ambiguity fix in the
--since/--until argument format.

1. --base compare mode was reading the working tree for the file list
   AND the working tree for the file content. With --base v1.0, the
   'language line deltas sorted by magnitude' and the
   'len(baseFilesAtHEAD)' file count were derived from the user's
   uncommitted working tree versus itself, not from v1.0 versus HEAD.
   baseCommits/baseContributors/baseHotFiles used baseRef correctly,
   so the inconsistency made the commit/contributor/hot-file columns
   right but the language column and the file-count metric wrong.

   Fix: add FilesAtRef(repoPath, ref) and LinesByFileAtRef(repoPath,
   ref). FilesAtRef uses 'git ls-tree -r --name-only <ref>' (the
   committed tree of the ref, not the working tree).
   LinesByFileAtRef walks the ref's tree, resolves each path's OID
   via 'git ls-tree <ref> -- <path>', and reads the blob content
   via 'git cat-file blob <oid>' so the working tree is irrelevant.
   The existing FilesAtHEAD and LinesByFile are now thin wrappers
   around the new functions (HEAD) and marked Deprecated in the doc
   for the working-tree caller. main.go's --base branch now uses the
   ref-parameterised versions explicitly.

   The dead 'var _ = time.Time{}' placeholder in helpers.go is
   removed (it was the only consumer of the 'time' import; the new
   code doesn't need it, so the import is dropped too).

2. --since 2099-01-01 (or any future date) on a real repo used to
   print 'repository has no commits' and exit 1 (exitNotARepo). The
   repo is fine; --since just filtered everything out. The two
   failure modes are now distinguished: empty repo (no --since)
   keeps the original 'repository has no commits' + exit 1; --since
   with no matches prints 'no commits match --since=<ts> (try an
   earlier date or drop --since)' and exits 64 (exitUsage).

3. Related fix: log.go's --since=/--until= argument to git was
   formatted as '2006-01-02T15:04:05' (no Z) even though the time
   was already converted to UTC. The bare wall-clock form is
   ambiguous β€” git on some systems interprets it as local time of
   the host running dig. Append the Z so the argument is
   unambiguous. The H2 fix's error message format string was
   updated in lockstep.

go build, go vet, go test ./... all green. Verified with a real
binary: --base at commit c93cd33 produces a 22K delta report;
--since=2030-01-01T00:00:00Z prints the new no-match message and
exits 64.

Co-authored-by: Nova Lux <NovaLux12@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant