From ff0a73f5ad300110defffdc2593af129911c0644 Mon Sep 17 00:00:00 2001 From: Tim Hockin Date: Mon, 15 Aug 2022 10:19:29 -0700 Subject: [PATCH] Support shallow sync when the rev is not in-range If I ask for `--depth 1` and a branch, it's fine. If I ask for `--depth 1` and a tag, and that tag is not within 1 commit of the branch that was cloned, it will give an error. Oddly, if the initial `clone` was OK, and subsequent syncs drift, it is OK, because of how we `fetch`. But if it is too far away at the beginning, kaboom. This betrays that the current model of `--branch` and `--rev` is really broken, and should be revamped. For now, I did something simple - if the rev can't be found, try a fetch. A "real" fix is more involved. Also add tests. --- cmd/git-sync/main.go | 42 ++++++++++++++++++++++++++++++++++- test_e2e.sh | 53 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/cmd/git-sync/main.go b/cmd/git-sync/main.go index ea5d376..b1e74c0 100644 --- a/cmd/git-sync/main.go +++ b/cmd/git-sync/main.go @@ -1345,7 +1345,7 @@ func (git *repoSync) SyncRepo(ctx context.Context, refreshCreds func(context.Con if err != nil { return false, "", err } - hash, err = git.LocalHashForRev(ctx, git.rev) + hash, err = git.ensureLocalHashForRev(ctx, git.rev) if err != nil { return false, "", err } @@ -1368,6 +1368,46 @@ func (git *repoSync) SyncRepo(ctx context.Context, refreshCreds func(context.Con return true, hash, git.AddWorktreeAndSwap(ctx, hash) } +func (git *repoSync) ensureLocalHashForRev(ctx context.Context, rev string) (string, error) { + hash, err := git.LocalHashForRev(ctx, rev) + if err == nil { + return hash, nil + } + + // git might return either error, based on flags used internal to + // LocalHashForRev, so we will consider either one to be a "rev not found". + if es := err.Error(); !stringContainsOneOf(es, "unknown revision", "bad revision") { + return "", err + } + + // The rev was not found, try to fetch it. + git.log.V(1).Info("rev was not found, trying fetch", "rev", git.rev) + args := []string{"fetch", "-f", "--tags"} + if git.depth != 0 { + args = append(args, "--depth", strconv.Itoa(git.depth)) + } + args = append(args, git.repo, "--") + if _, err := git.run.Run(ctx, git.root, nil, git.cmd, args...); err != nil { + return "", err + } + + // Try again. + hash, err = git.LocalHashForRev(ctx, git.rev) + if err != nil { + return "", err + } + return hash, nil +} + +func stringContainsOneOf(s string, matches ...string) bool { + for _, m := range matches { + if strings.Contains(s, m) { + return true + } + } + return false +} + // GetRevs returns the local and upstream hashes for rev. func (git *repoSync) GetRevs(ctx context.Context) (string, string, error) { // Ask git what the exact hash is for rev. diff --git a/test_e2e.sh b/test_e2e.sh index e24f105..9dcd8d8 100755 --- a/test_e2e.sh +++ b/test_e2e.sh @@ -934,10 +934,10 @@ function e2e::sync_slow_git_long_timeout() { ############################################## # Test depth syncing ############################################## -function e2e::sync_depth_shallow() { +function e2e::sync_branch_depth_shallow() { # First sync - echo "$FUNCNAME 1" > "$REPO"/file expected_depth="1" + echo "$FUNCNAME 1" > "$REPO"/file git -C "$REPO" commit -qam "$FUNCNAME 1" GIT_SYNC \ @@ -981,6 +981,55 @@ function e2e::sync_depth_shallow() { fi } +############################################## +# Test depth syncing with a tag not within depth +############################################## +function e2e::sync_tag_depth_shallow_out_of_range() { + TAG="e2e-tag" + expected_depth="1" + + # First commits, tag is not within --depth + echo "$FUNCNAME 1" > "$REPO"/file + git -C "$REPO" commit -qam "$FUNCNAME 1" + git -C "$REPO" tag -af "$TAG" -m "$FUNCNAME 1" >/dev/null + echo "$FUNCNAME 2" > "$REPO"/file + git -C "$REPO" commit -qam "$FUNCNAME 2" + + GIT_SYNC \ + --period=100ms \ + --repo="file://$REPO" \ + --branch="$MAIN_BRANCH" \ + --rev="$TAG" \ + --depth="$expected_depth" \ + --root="$ROOT" \ + --link="link" \ + >> "$1" 2>&1 & + sleep 3 + assert_link_exists "$ROOT"/link + assert_file_exists "$ROOT"/link/file + assert_file_eq "$ROOT"/link/file "$FUNCNAME 1" + depth=$(GIT_DIR="$ROOT"/link/.git git log | grep commit | wc -l) + if [[ $expected_depth != $depth ]]; then + fail "initial depth mismatch expected=$expected_depth actual=$depth" + fi + + # Make 2 new commits, and tag the older one, so it's not within --depth + echo "$FUNCNAME 3" > "$REPO"/file + git -C "$REPO" commit -qam "$FUNCNAME 3" + SHA=$(git -C "$REPO" rev-parse HEAD) + echo "$FUNCNAME 4" > "$REPO"/file + git -C "$REPO" commit -qam "$FUNCNAME 4" + git -C "$REPO" tag -af "$TAG" -m "$FUNCNAME 3" "$SHA" >/dev/null + sleep 3 + assert_link_exists "$ROOT"/link + assert_file_exists "$ROOT"/link/file + assert_file_eq "$ROOT"/link/file "$FUNCNAME 3" + depth=$(GIT_DIR="$ROOT"/link/.git git log | grep commit | wc -l) + if [[ $expected_depth != $depth ]]; then + fail "forward depth mismatch expected=$expected_depth actual=$depth" + fi +} + ############################################## # Test fetch skipping commit ##############################################