Skip to content

Commit 083d8dd

Browse files
committed
Simplify inner loop: just fetch $ref
Old way: - ls-remote $ref $ref^{} and parse - compare to current - if changed, fetch - update worktree New way: - fetch $ref - compare to current - if change, update worktree
1 parent ad92ba6 commit 083d8dd

File tree

3 files changed

+29
-76
lines changed

3 files changed

+29
-76
lines changed

main.go

Lines changed: 19 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -836,10 +836,7 @@ func main() {
836836
os.Exit(exitCode)
837837
}
838838

839-
if isHash, err := git.IsKnownHash(ctx, git.ref); err != nil {
840-
log.Error(err, "can't tell if ref is a git hash, exiting", "ref", git.ref)
841-
os.Exit(1)
842-
} else if isHash {
839+
if hash == git.ref {
843840
log.V(0).Info("ref appears to be a git hash, no further sync needed", "ref", git.ref)
844841
log.DeleteErrorFile()
845842
sleepForever()
@@ -1493,49 +1490,6 @@ func (m multiError) Error() string {
14931490
return strings.Join(strs, "; ")
14941491
}
14951492

1496-
// remoteHashForRef returns the upstream hash for a given ref.
1497-
func (git *repoSync) remoteHashForRef(ctx context.Context, ref string) (string, error) {
1498-
// Fetch both the bare and dereferenced ref. git sorts the results and
1499-
// prints the dereferenced result, if present, after the bare result, so we
1500-
// always want the last result it produces.
1501-
output, _, err := git.Run(ctx, git.root, "ls-remote", "-q", git.repo, ref, ref+"^{}")
1502-
if err != nil {
1503-
return "", err
1504-
}
1505-
line := lastNonEmptyLine(output)
1506-
parts := strings.Split(line, "\t") // guaranteed to have at least 1 element
1507-
return parts[0], nil
1508-
}
1509-
1510-
func lastNonEmptyLine(text string) string {
1511-
lines := strings.Split(text, "\n") // guaranteed to have at least 1 element
1512-
for i := len(lines) - 1; i >= 0; i-- {
1513-
line := strings.TrimSpace(lines[i])
1514-
if line != "" {
1515-
return line
1516-
}
1517-
}
1518-
return ""
1519-
}
1520-
1521-
// IsKnownHash returns true if ref is the hash of a commit which is known to this
1522-
// repo. In the event that ref is an abbreviated hash (e.g. "abcd" which
1523-
// resolves to "abcdef1234567890"), this will return true by prefix-matching.
1524-
// If ref is ambiguous, it will consider whatever result git returns. If ref
1525-
// is not a hash or is not known to this repo, even if it appears to be a hash,
1526-
// this will return false.
1527-
func (git *repoSync) IsKnownHash(ctx context.Context, ref string) (bool, error) {
1528-
stdout, stderr, err := git.Run(ctx, git.root, "rev-parse", ref+"^{commit}")
1529-
if err != nil {
1530-
if strings.Contains(stderr, "unknown revision") {
1531-
return false, nil
1532-
}
1533-
return false, err
1534-
}
1535-
line := lastNonEmptyLine(stdout)
1536-
return strings.HasPrefix(line, ref), nil
1537-
}
1538-
15391493
// worktree represents a git worktree (which may or may not exist on disk).
15401494
type worktree absPath
15411495

@@ -1592,26 +1546,6 @@ func (git *repoSync) SyncRepo(ctx context.Context, refreshCreds func(context.Con
15921546
return false, "", err
15931547
}
15941548

1595-
// Figure out what hash the remote resolves to.
1596-
remoteHash, err := git.remoteHashForRef(ctx, git.ref)
1597-
if err != nil {
1598-
return false, "", err
1599-
}
1600-
1601-
// If we couldn't find a remote commit, it might have been a hash literal.
1602-
if remoteHash == "" {
1603-
// If git thinks it tastes like a hash, we just use that and if it
1604-
// is wrong, we will fail later.
1605-
output, _, err := git.Run(ctx, git.root, "rev-parse", git.ref)
1606-
if err != nil {
1607-
return false, "", err
1608-
}
1609-
result := strings.Trim(output, "\n")
1610-
if result == git.ref {
1611-
remoteHash = git.ref
1612-
}
1613-
}
1614-
16151549
// Find out what we currently have synced, if anything.
16161550
var currentWorktree worktree
16171551
if wt, err := git.currentWorktree(); err != nil {
@@ -1622,6 +1556,22 @@ func (git *repoSync) SyncRepo(ctx context.Context, refreshCreds func(context.Con
16221556
currentHash := currentWorktree.Hash()
16231557
git.log.V(3).Info("current state", "hash", currentHash, "worktree", currentWorktree)
16241558

1559+
// This should be very fast if we already have the hash we need. Parameters
1560+
// like depth are set at fetch time.
1561+
if err := git.fetch(ctx, git.ref); err != nil {
1562+
return false, "", err
1563+
}
1564+
1565+
// Figure out what we got. The ^{} syntax "peels" annotated tags to
1566+
// their underlying commit hashes, but has no effect if we fetched a
1567+
// branch, plain tag, or hash.
1568+
remoteHash := ""
1569+
if output, _, err := git.Run(ctx, git.root, "rev-parse", "FETCH_HEAD^{}"); err != nil {
1570+
return false, "", err
1571+
} else {
1572+
remoteHash = strings.Trim(output, "\n")
1573+
}
1574+
16251575
if currentHash == remoteHash {
16261576
// We seem to have the right hash already. Let's be sure it's good.
16271577
git.log.V(3).Info("current hash is same as remote", "hash", currentHash)
@@ -1643,17 +1593,12 @@ func (git *repoSync) SyncRepo(ctx context.Context, refreshCreds func(context.Con
16431593
// are set properly. This is cheap when we already have the target hash.
16441594
if changed || git.syncCount == 0 {
16451595
git.log.V(0).Info("update required", "ref", git.ref, "local", currentHash, "remote", remoteHash, "syncCount", git.syncCount)
1646-
1647-
// Parameters like depth are set at fetch time.
1648-
if err := git.fetch(ctx, remoteHash); err != nil {
1649-
return false, "", err
1650-
}
16511596
metricFetchCount.Inc()
16521597

16531598
// Reset the repo (note: not the worktree - that happens later) to the new
16541599
// ref. This makes subsequent fetches much less expensive. It uses --soft
16551600
// so no files are checked out.
1656-
if _, _, err := git.Run(ctx, git.root, "reset", "--soft", "FETCH_HEAD"); err != nil {
1601+
if _, _, err := git.Run(ctx, git.root, "reset", "--soft", remoteHash); err != nil {
16571602
return false, "", err
16581603
}
16591604

@@ -1715,7 +1660,7 @@ func (git *repoSync) SyncRepo(ctx context.Context, refreshCreds func(context.Con
17151660

17161661
// fetch retrieves the specified ref from the upstream repo.
17171662
func (git *repoSync) fetch(ctx context.Context, ref string) error {
1718-
git.log.V(1).Info("fetching", "ref", ref, "repo", git.repo)
1663+
git.log.V(2).Info("fetching", "ref", ref, "repo", git.repo)
17191664

17201665
// Fetch the ref and do some cleanup, setting or un-setting the repo's
17211666
// shallow flag as appropriate.

test_e2e.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3021,7 +3021,7 @@ function e2e::export_error() {
30213021
--error-file="error.json"
30223022
assert_file_absent "$ROOT/link"
30233023
assert_file_absent "$ROOT/link/file"
3024-
assert_file_contains "$ROOT/error.json" "unknown revision"
3024+
assert_file_contains "$ROOT/error.json" "couldn't find remote ref"
30253025

30263026
# the error.json file should be removed if sync succeeds.
30273027
GIT_SYNC \
@@ -3049,7 +3049,7 @@ function e2e::export_error_abs_path() {
30493049
--error-file="$ROOT/dir/error.json"
30503050
assert_file_absent "$ROOT/link"
30513051
assert_file_absent "$ROOT/link/file"
3052-
assert_file_contains "$ROOT/dir/error.json" "unknown revision"
3052+
assert_file_contains "$ROOT/dir/error.json" "couldn't find remote ref"
30533053
}
30543054

30553055
##############################################

v3-to-v4.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ commit (by SHA) it needs. This transfers less data and closes the race
3131
condition where a symbolic name can change after `git ls-remote` but before
3232
`git fetch`.
3333

34+
### The v4.2+ loop
35+
36+
The v4.2 loop refines the v4 loop even further. Instead of using ls-remote to
37+
see what the upstream has and then fetching it, git-sync sill just fetch it by
38+
ref. If the local sync already has the corresponding hash, nothing more will
39+
be synced. If it did not have that hash before, then it does now and can
40+
update the worktree.
41+
3442
## Flags
3543

3644
The flag syntax parsing has changed in v4. git-sync v3 accept flags in Go's

0 commit comments

Comments
 (0)