diff --git a/cmd/git-sync/main.go b/cmd/git-sync/main.go index d39f83d..07a3f83 100644 --- a/cmd/git-sync/main.go +++ b/cmd/git-sync/main.go @@ -1554,6 +1554,8 @@ func (git *repoSync) cleanup(ctx context.Context, worktree worktree) error { if err := removeDirContents(git.worktreeFor("").Path(), git.log, worktree.Hash()); err != nil { cleanupErrs = append(cleanupErrs, err) } + + // Let git know we don't need those old commits any more. if _, err := git.Run(ctx, git.root, "worktree", "prune", "--verbose"); err != nil { cleanupErrs = append(cleanupErrs, err) } @@ -1678,6 +1680,9 @@ func (git *repoSync) currentWorktree() (worktree, error) { if target == "" { return "", nil } + if filepath.IsAbs(target) { + return worktree(target), nil + } return worktree(git.root.Join(target)), nil } @@ -1736,12 +1741,17 @@ func (git *repoSync) SyncRepo(ctx context.Context, refreshCreds func(context.Con currentHash = "" } } - changed := (currentHash != remoteHash) - if changed || git.syncCount == 0 { - git.log.V(0).Info("update required", "ref", git.ref, "local", currentHash, "remote", remoteHash) - // We have to do at least one fetch, to ensure that parameters like depth - // are set properly. This is cheap when we already have the target hash. + // This catches in-place upgrades from older versions where the worktree + // path was different. + changed := (currentHash != remoteHash) || (currentWorktree != git.worktreeFor(currentHash)) + + // We have to do at least one fetch, to ensure that parameters like depth + // are set properly. This is cheap when we already have the target hash. + if changed || git.syncCount == 0 { + git.log.V(0).Info("update required", "ref", git.ref, "local", currentHash, "remote", remoteHash, "syncCount", git.syncCount) + + // Parameters like depth are set at fetch time. if err := git.fetch(ctx, remoteHash); err != nil { return false, "", err } @@ -1785,6 +1795,11 @@ func (git *repoSync) SyncRepo(ctx context.Context, refreshCreds func(context.Con git.syncCount++ // Clean up old worktree(s) and run GC. + if currentWorktree != git.worktreeFor(currentHash) { + // The old worktree might have come from a prior version, and so + // not get caught by the normal cleanup. + os.RemoveAll(currentWorktree.Path().String()) + } if err := git.cleanup(ctx, newWorktree); err != nil { git.log.Error(err, "git cleanup failed", "newWorktree", newWorktree) } diff --git a/test_e2e.sh b/test_e2e.sh index 07851af..155a1ba 100755 --- a/test_e2e.sh +++ b/test_e2e.sh @@ -591,6 +591,50 @@ function e2e::worktree_cleanup() { assert_file_absent "$ROOT/.worktrees/not_a_directory" } +############################################## +# Test v3->v4 upgrade +############################################## +function e2e::v3_v4_upgrade_in_place() { + echo "$FUNCNAME 1" > "$REPO"/file + git -C "$REPO" commit -qam "$FUNCNAME" + + # sync once + GIT_SYNC \ + --one-time \ + --repo="file://$REPO" \ + --root="$ROOT" \ + --link="link" + + assert_link_exists "$ROOT"/link + assert_file_exists "$ROOT"/link/file + assert_file_eq "$ROOT"/link/file "$FUNCNAME 1" + + # simulate v3's worktrees + WT="$(readlink "$ROOT/link")" + SHA="$(basename "$WT")" + mv -f "$ROOT/$WT" "$ROOT/$SHA" + ln -sf "$SHA" "$ROOT/link" + + # make a second commit + echo "$FUNCNAME 2" > "$REPO"/file2 + git -C "$REPO" add file2 + git -C "$REPO" commit -qam "$FUNCNAME new file" + + # sync again + GIT_SYNC \ + --one-time \ + --repo="file://$REPO" \ + --root="$ROOT" \ + --link="link" + + assert_link_exists "$ROOT"/link + assert_file_exists "$ROOT"/link/file + assert_file_eq "$ROOT"/link/file "$FUNCNAME 1" + assert_file_exists "$ROOT"/link/file2 + assert_file_eq "$ROOT"/link/file2 "$FUNCNAME 2" + assert_file_absent "$ROOT/$SHA" +} + ############################################## # Test readlink ############################################## diff --git a/v3-to-v4.md b/v3-to-v4.md index c430bb0..07ff1e9 100644 --- a/v3-to-v4.md +++ b/v3-to-v4.md @@ -4,6 +4,11 @@ Git-sync v4 is a significant change from v3. It includes several flag changes (though many of the old flags are kept for backwards compatibility), but more importantly it fundamentally changes the way the internal sync-loop works. +It should be possible to upgrade a synced repo (e.g. in a volume) from git-sync +v3 to git-sync v4, but appropriate caution should be used for critical +deployments. We have a test which covers this, but there are many degrees of +config which we simply can't predict. + ## The v3 loop The way git-sync v3.x works is sort of like how a human might work: