sha256convert: dedup message-rewrite path; fix package doc and lints · Entire

sha256convert: dedup message-rewrite path; fix package doc and lints

57380c7→main·

nodo·1mo ago·2 files·+178 added/-145 removed

Address a batch of PR #66 review findings:

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com

Sessions

09b743cc5de5View transcript

Changes

2

// a temporary on-disk SHA1 bare repo, then walks every reachable object and
// re-emits it under SHA256 into a new bare repo at the user-supplied path.
//
// The tool is intentionally scoped: no hash mapping is persisted, GPG
// signatures on commits and tags are dropped (they sign over the original
// SHA1 byte stream and would be invalid post-rewrite), and any submodule
// gitlink fails the run so the caller chooses which refs to exclude. The
// linked-to repository's URL still points at an upstream SHA1 store,
// which has no way to resolve a SHA256-rewritten gitlink, so rewriting
// would produce a tree that fsck-passes but breaks
// `git submodule update`.
// The SHA1 → SHA256 mapping is preserved, so the original hashes stay
// recoverable: by default as a refs/notes/sha1-origin notes ref in the
// converted repo (disable with --no-origin-notes), and optionally as a
// sidecar TSV via --write-mapping.
package sha256convert

import (

// switching to a "(N more)" suffix.
const previewMax = 5

// previewJoin renders items as a comma-separated list, inlining at most
// previewMax of them and appending ", ... (N more<suffix>)" when the list
// is longer. suffix points at where the full list lives (e.g.
// "; full list in --json"); pass "" for none.
func previewJoin(items []string, suffix string) string {
    if len(items) <= previewMax {
        return strings.Join(items, ", ")
    }
    return fmt.Sprintf("%s, ... (%d more%s)",
        strings.Join(items[:previewMax], ", "), len(items)-previewMax, suffix)
}

// Lines satisfies the human-readable output contract used by other git-sync subcommands.
func (r Result) Lines() []string {
    lines := []string{
        lines = append(lines, fmt.Sprintf("rewrote %d SHA1 hash reference(s) in commit/tag messages", r.MessageRewrites))
    }
    if n := len(r.AmbiguousMessageRefs); n > 0 {
        preview := r.AmbiguousMessageRefs
        extra := 0
        if len(preview) > previewMax {
            extra = len(preview) - previewMax
            preview = preview[:previewMax]
        }
        line := fmt.Sprintf("warning: %d ambiguous SHA1 hex prefix(es) in messages left unrewritten (look up via the mapping file): %s",
            n, strings.Join(preview, ", "))
        if extra > 0 {
            line += fmt.Sprintf(", ... (%d more)", extra)
        }
        lines = append(lines, line)
    }
    if r.SkippedPullRefs > 0 {
        lines = append(lines, fmt.Sprintf("excluded %d foreign pull/merge-request ref(s) (refs/pull/*, refs/pull-requests/*, refs/merge-requests/*) from --all-refs; pass --include-pull-refs to convert them", r.SkippedPullRefs))
    }
}

// rewriteHashesInMessage scans msg for short and full SHA1 hashes,
// replacing any that uniquely identify a commit or tag in t.reachable
// with the corresponding full SHA256 hex from t.mapping. Returns the
// rewritten message and the number of substitutions made. Ambiguous
// prefixes are left in place and recorded in t.ambiguousMessageRefs so the
// caller can surface a warning at the end of the run.
func (t *translator) rewriteHashesInMessage(msg string) (string, int) {
    count := 0
    out := hashPattern.ReplaceAllStringFunc(msg, func(s string) string {
        sha1, result := t.resolveMessageRef(s)
        switch result {
        case matchNone:
            return s
        case matchAmbiguous:
            t.ambiguousMessageRefs[s] = struct{}{}
            return s
        }
    })
}