Deref tags on ls-remote

A previous commit (2f7335868e) introduced
a quiet bug which results in the "update needed" condition triggering
every loop.  e2e passed for me by sheer luck of winning races.  Until it
didn't.  Walking thru with the debugger to figure it out, I realized
this issue.

The short story: `ls-remote` for a tag gets us the SHA of the tag, but
`rev-parse HEAD` gets us the SHA of the commit to which that tag is
attached.  Those are never equal, so we detect "update needed" every
loop.

Now we ask `ls-remote` for the rev and the dereferenced rev.  If that
rev is a branch, the deref does nothing.  If that rev is a tag it
produces both results.  ls-remote does its own sort, so the deref (if
found) comes after the non-deref.  This means that, in both cases, the
last line is the one we want.
This commit is contained in:
Tim Hockin 2023-02-10 13:25:14 -08:00
parent 1253f82cd9
commit 139352ecee
No known key found for this signature in database
1 changed files with 17 additions and 2 deletions

View File

@ -1393,14 +1393,29 @@ func (git *repoSync) LocalHashForRev(ctx context.Context, rev string) (string, e
// RemoteHashForRef returns the upstream hash for a given ref. // RemoteHashForRef returns the upstream hash for a given ref.
func (git *repoSync) RemoteHashForRef(ctx context.Context, ref string) (string, error) { func (git *repoSync) RemoteHashForRef(ctx context.Context, ref string) (string, error) {
output, err := git.run.Run(ctx, git.root, nil, git.cmd, "ls-remote", "-q", git.repo, ref) // Fetch both the bare and dereferenced rev. git sorts the results and
// prints the dereferenced result, if present, after the bare result, so we
// always want the last result it produces.
output, err := git.run.Run(ctx, git.root, nil, git.cmd, "ls-remote", "-q", git.repo, ref, ref+"^{}")
if err != nil { if err != nil {
return "", err return "", err
} }
parts := strings.Split(string(output), "\t") lines := strings.Split(string(output), "\n") // guaranteed to have at least 1 element
line := lastNonEmpty(lines)
parts := strings.Split(line, "\t") // guaranteed to have at least 1 element
return parts[0], nil return parts[0], nil
} }
func lastNonEmpty(lines []string) string {
last := ""
for _, line := range lines {
if line != "" {
last = line
}
}
return last
}
func (git *repoSync) RevIsHash(ctx context.Context) (bool, error) { func (git *repoSync) RevIsHash(ctx context.Context) (bool, error) {
hash, err := git.ResolveRef(ctx, git.rev) hash, err := git.ResolveRef(ctx, git.rev)
if err != nil { if err != nil {