diff --git a/repository/git.go b/repository/git.go index 55dacfbb..5aa353c6 100644 --- a/repository/git.go +++ b/repository/git.go @@ -67,6 +67,30 @@ func RunInteractiveGitCommand(ctx context.Context, dir string, w io.Writer, args return cmd.Run() } +func isShallowRepository(ctx context.Context, repo string) (bool, error) { + isShallow, err := RunGitCommand(ctx, repo, "rev-parse", "--is-shallow-repository") + if err != nil { + return false, err + } + + return strings.TrimSpace(isShallow) == "true", nil +} + +func ensureRepositoryNotShallow(ctx context.Context, repo string) error { + isShallow, err := isShallowRepository(ctx, repo) + if err != nil { + return err + } + + if !isShallow { + return nil + } + + slog.Info("Repository is shallow, fetching full history", "repository", repo) + _, err = RunGitCommand(ctx, repo, "fetch", "--unshallow") + return err +} + func getContainerUseRemote(ctx context.Context, repo string) (string, error) { // Check if we already have a container-use remote cuRemote, err := RunGitCommand(ctx, repo, "remote", "get-url", "container-use") @@ -139,6 +163,10 @@ func (r *Repository) initializeWorktree(ctx context.Context, id, gitRef string) } resolvedRef = strings.TrimSpace(resolvedRef) + if err := ensureRepositoryNotShallow(ctx, r.userRepoPath); err != nil { + return err + } + _, err = RunGitCommand(ctx, r.userRepoPath, "push", containerUseRemote, fmt.Sprintf("%s:refs/heads/%s", resolvedRef, id)) if err != nil { // Retry once on failure diff --git a/repository/repository_test.go b/repository/repository_test.go index 2d2924b4..8fd2dbb0 100644 --- a/repository/repository_test.go +++ b/repository/repository_test.go @@ -66,3 +66,55 @@ func TestRepositoryOpen(t *testing.T) { assert.Equal(t, repo.forkRepoPath, strings.TrimSpace(remote)) }) } + +func TestInitializeWorktreeUnshallowOnShallowClone(t *testing.T) { + ctx := context.Background() + tempDir := t.TempDir() + + originRepo := filepath.Join(tempDir, "origin") + shallowRepo := filepath.Join(tempDir, "shallow") + bareRemote := filepath.Join(tempDir, "remote.git") + base := filepath.Join(tempDir, "base") + + err := os.MkdirAll(originRepo, 0755) + require.NoError(t, err) + _, err = RunGitCommand(ctx, originRepo, "init") + require.NoError(t, err) + _, err = RunGitCommand(ctx, originRepo, "config", "user.email", "test@example.com") + require.NoError(t, err) + _, err = RunGitCommand(ctx, originRepo, "config", "user.name", "Test User") + require.NoError(t, err) + + err = os.WriteFile(filepath.Join(originRepo, "README.md"), []byte("initial"), 0644) + require.NoError(t, err) + _, err = RunGitCommand(ctx, originRepo, "add", "README.md") + require.NoError(t, err) + _, err = RunGitCommand(ctx, originRepo, "commit", "-m", "initial") + require.NoError(t, err) + err = os.WriteFile(filepath.Join(originRepo, "README.md"), []byte("updated"), 0644) + require.NoError(t, err) + _, err = RunGitCommand(ctx, originRepo, "commit", "-am", "update") + require.NoError(t, err) + + _, err = RunGitCommand(ctx, tempDir, "clone", "--bare", originRepo, bareRemote) + require.NoError(t, err) + _, err = RunGitCommand(ctx, tempDir, "clone", "--depth=1", "file://"+bareRemote, shallowRepo) + require.NoError(t, err) + isShallow, err := isShallowRepository(ctx, shallowRepo) + require.NoError(t, err) + require.True(t, isShallow, "test setup should create a shallow repository") + + repo, err := OpenWithBasePath(ctx, shallowRepo, base) + require.NoError(t, err) + defer repo.deleteWorktree("issue-248-shallow") + + _, _, err = repo.initializeWorktree(ctx, "issue-248-shallow", "HEAD") + require.NoError(t, err) + + isShallow, err = isShallowRepository(ctx, shallowRepo) + require.NoError(t, err) + assert.False(t, isShallow, "shallow repository should be unshallowed during environment creation") + + _, err = RunGitCommand(ctx, repo.forkRepoPath, "rev-parse", "--verify", "issue-248-shallow") + require.NoError(t, err) +}