Address review nits in sha256convert · Entire
Address review nits in sha256convert
f980228→main· nodo·1mo ago·1 file·+52 added/-18
- signBranchTips: explain the os.Stderr departure from req.Out; TTy inheritance is required for gpg/ssh-agent prompts.
- Don't double-count when a commit/tag has both Signature and SignatureSHA256 (two encodings of the same signature). Relabel the warning to "signature(s) / mergetag header(s)" so the count matches what it actually represents.
- writeOriginNotes now honors SOURCE_DATE_EPOCH and otherwise pins the wrapper-commit timestamp to the Unix epoch, so the notes-ref hash is reproducible across runs over identical source state. The timestamp is bookkeeping; it carries no information about the underlying SHA1 history.
- writeLoose uses zlib level 1, matching git's core.looseCompression default — loose objects are short-lived before gc packs them, so write speed > size.
- Clarify encodeBody's "scratch MemoryObject" comment so the unused format argument is no longer mystifying on re-read.
Sessions
Transcript data is unavailable for this checkpoint.
Changes
1
cmd/git-sync/internal/sha256convert
Msha256convert.go+52/-18
146 unmodified lines
147
148
149
150
150
151
152
153
154
155
156
291 unmodified lines
448
449
450
448
449
450
451
452
453
454
455
456
457
458
459
460
539 unmodified lines
1000
1001
1002
996
1003
1004
1005
1006
1007
1008
998
999
1000
1009
1010
1011
49 unmodified lines
1061
1062
1063
1056
1064
1065
1066
1067
1058
1059
1060
1068
1069
1070
10 unmodified lines
1081
1082
1083
1077
1078
1079
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
41 unmodified lines
1135
1136
1137
1128
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
153 unmodified lines
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
50 unmodified lines
1371
1372
1373
1344
1345
1374
1375
1376
1377
1378
1379
1380
1381
1382
146 unmodified lines
fmt.Sprintf("refs written: %d", r.RefsConverted),
}
if r.SignaturesStripped > 0 {
lines = append(lines, fmt.Sprintf("warning: stripped %d GPG signature(s); they no longer match the rewritten object content", r.SignaturesStripped))
// Mixes commit/tag signatures (GPG/SSH/X.509) and embedded
// mergetag headers — each counts as one signed artifact whose
// signature became invalid post-rewrite.
lines = append(lines, fmt.Sprintf("warning: stripped %d signature(s) / mergetag header(s); they no longer match the rewritten object content", r.SignaturesStripped))
}
if r.MessageRewrites > 0 {
lines = append(lines, fmt.Sprintf("rewrote %d SHA1 hash reference(s) in commit/tag messages", r.MessageRewrites))
291 unmodified lines
args = append(args, tagName, refName)
cmd := exec.CommandContext(ctx, gitBin, args...)
// Inherit stdio so gpg/ssh-agent passphrase prompts work. We
// intentionally do not capture stdout/stderr — the user needs
// to see them when authenticating.
// Deliberate departure from the req.Out plumbing the rest of
// Run uses: gpg/ssh-agent and pinentry need a real TTY for
// passphrase prompts, so we inherit the parent's stdio
// directly. The consequence is that callers passing
// req.Out = io.Discard (e.g. tests) still see subprocess
// output on real stderr — that's the cost of working
// authentication.
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stderr // git tag -s is usually quiet on success
cmd.Stderr = os.Stderr
539 unmodified lines
t.messageRewrites += n
}
}
if c.Signature != "" {
// A commit can carry both Signature (SHA1 form, "gpgsig") and
// SignatureSHA256 ("gpgsig-sha256") in a transitional dual-hash
// repo, but they encode the same logical signature. Strip both
// fields if present, count once.
if c.Signature != "" || c.SignatureSHA256 != "" {
c.Signature = ""
t.signaturesStripped++
}
if c.SignatureSHA256 != "" {
c.SignatureSHA256 = ""
t.signaturesStripped++
}
49 unmodified lines
t.messageRewrites += n
}
}
if tag.Signature != "" {
// Same as commits: Signature and SignatureSHA256 are two encodings
// of the same logical signature in a transitional dual-hash repo.
if tag.Signature != "" || tag.SignatureSHA256 != "" {
tag.Signature = ""
t.signaturesStripped++
}
if tag.SignatureSHA256 != "" {
tag.SignatureSHA256 = ""
t.signaturesStripped++
}
10 unmodified lines
return newHash, nil
}
// encodeBody runs an object's go-git Encode method into a SHA1-hasher
// MemoryObject (the hasher we use to capture bytes is irrelevant; we only
// read the body back out) and returns just the payload bytes — without the
// encodeBody runs an object's go-git Encode method into a scratch
// MemoryObject and returns just the payload bytes — without the
// "<type> <size>\x00" header. writeLoose adds the SHA256-correct header.
func encodeBody(typ plumbing.ObjectType, encode func(plumbing.EncodedObject) error) ([]byte, error) {
scratch := plumbing.NewMemoryObject(plumbing.FromObjectFormat(formatcfg.SHA1))
scratch.SetType(typ)
41 unmodified lines
}
var buf bytes.Buffer
zw := zlib.NewWriter(&buf)
// Level 1 matches git's core.looseCompression default. Loose objects
// are short-lived (gc rolls them into packs), so optimizing for write
// speed over size is the standard trade-off.
zw, err := zlib.NewWriterLevel(&buf, zlib.BestSpeed)
if err != nil {
return plumbing.ZeroHash, fmt.Errorf("zlib writer: %w", err)
}
if _, err := zw.Write(header); err != nil {
return plumbing.ZeroHash, fmt.Errorf("zlib write header: %w", err)
}
153 unmodified lines
return out
}
// notesCommitTime returns the committer/author timestamp for the
// synthetic notes wrapper commit. Reads SOURCE_DATE_EPOCH (the
// reproducible-builds convention) when set, falling back to the Unix
// epoch so two runs over identical source state always produce the
// same notes-ref hash.
func notesCommitTime() time.Time {
if raw := os.Getenv("SOURCE_DATE_EPOCH"); raw != "" {
if secs, err := strconv.ParseInt(raw, 10, 64); err == nil {
return time.Unix(secs, 0).UTC()
}
}
return time.Unix(0, 0).UTC()
}
// writeOriginNotes writes a `git notes` ref to dst that records each
// translated commit's original SHA1, keyed by its new SHA256. Standard
// git tooling (`git log --notes=<ref>`, `git notes --ref=<ref> show
50 unmodified lines
return "", fmt.Errorf("store notes tree: %w", err)
}
now := time.Now().UTC()
sig := object.Signature{Name: "git-sync", Email: "noreply@entire.io", When: now}
// Honor SOURCE_DATE_EPOCH for reproducible builds; otherwise pin to
// the Unix epoch so the notes-ref hash is identical across runs over
// the same source state. The notes commit is bookkeeping — its
// timestamp carries no meaningful information about when the
// underlying SHA1 history was created.
sig := object.Signature{Name: "git-sync", Email: "noreply@entire.io", When: notesCommitTime()}
commit := &object.Commit{
Author: sig,
Committer: sig,