Skip to content

Commit 01a33bd

Browse files
aluzzarditiborvass
andauthored
cu diff (#135)
* cu diff - Add support for `cu diff` - Only show logs and diffs for changes that happened in the environment - Move `cu log` code into Repository - Add `cu log -p` and don't show patches by default - Show notes in `cu log` Signed-off-by: Andrea Luzzardi <[email protected]> * cu log/diff: add tests Signed-off-by: Andrea Luzzardi <[email protected]> * Update environment/integration/repository_test.go Co-authored-by: Tibor Vass <[email protected]> Signed-off-by: Andrea Luzzardi <[email protected]> * Update environment/integration/repository_test.go Co-authored-by: Tibor Vass <[email protected]> Signed-off-by: Andrea Luzzardi <[email protected]> * Update environment/integration/repository_test.go Co-authored-by: Tibor Vass <[email protected]> Signed-off-by: Andrea Luzzardi <[email protected]> * cu log/diff: include stderr Signed-off-by: Andrea Luzzardi <[email protected]> --------- Signed-off-by: Andrea Luzzardi <[email protected]> Signed-off-by: Andrea Luzzardi <[email protected]> Co-authored-by: Tibor Vass <[email protected]>
1 parent 8691757 commit 01a33bd

File tree

6 files changed

+194
-11
lines changed

6 files changed

+194
-11
lines changed

cmd/cu/diff.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package main
2+
3+
import (
4+
"os"
5+
6+
"github.com/dagger/container-use/repository"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
var diffCmd = &cobra.Command{
11+
Use: "diff <env>",
12+
Short: "Show changes between the environment and the local branch",
13+
Args: cobra.ExactArgs(1),
14+
ValidArgsFunction: suggestEnvironments,
15+
RunE: func(app *cobra.Command, args []string) error {
16+
ctx := app.Context()
17+
18+
// Ensure we're in a git repository
19+
repo, err := repository.Open(ctx, ".")
20+
if err != nil {
21+
return err
22+
}
23+
24+
return repo.Diff(ctx, args[0], os.Stdout)
25+
},
26+
}
27+
28+
func init() {
29+
rootCmd.AddCommand(diffCmd)
30+
}

cmd/cu/log.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package main
22

33
import (
44
"os"
5-
"os/exec"
6-
"strings"
75

86
"github.com/dagger/container-use/repository"
97
"github.com/spf13/cobra"
@@ -18,22 +16,18 @@ var logCmd = &cobra.Command{
1816
ctx := app.Context()
1917

2018
// Ensure we're in a git repository
21-
if _, err := repository.Open(ctx, "."); err != nil {
19+
repo, err := repository.Open(ctx, ".")
20+
if err != nil {
2221
return err
2322
}
2423

25-
env := args[0]
26-
// prevent accidental single quotes to mess up command
27-
env = strings.Trim(env, "'")
28-
cmd := exec.CommandContext(app.Context(), "git", "log", "--patch", "container-use/"+env)
29-
cmd.Stderr = os.Stderr
30-
cmd.Stdin = os.Stdin
31-
cmd.Stdout = os.Stdout
24+
patch, _ := app.Flags().GetBool("patch")
3225

33-
return cmd.Run()
26+
return repo.Log(ctx, args[0], patch, os.Stdout)
3427
},
3528
}
3629

3730
func init() {
31+
logCmd.Flags().BoolP("patch", "p", false, "Generate patch")
3832
rootCmd.AddCommand(logCmd)
3933
}

environment/integration/repository_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package integration
22

33
import (
4+
"bytes"
45
"context"
56
"os"
67
"strings"
@@ -126,3 +127,72 @@ func TestRepositoryCheckout(t *testing.T) {
126127
"Expected branch to be %s or cu-%s, got %s", env.ID, env.ID, actualBranch)
127128
})
128129
}
130+
131+
// TestRepositoryLog tests retrieving commit history for an environment
132+
func TestRepositoryLog(t *testing.T) {
133+
t.Parallel()
134+
WithRepository(t, "repository-log", SetupEmptyRepo, func(t *testing.T, repo *repository.Repository, user *UserActions) {
135+
ctx := context.Background()
136+
137+
// Create an environment and add some commits
138+
env := user.CreateEnvironment("Test Log", "Testing repository log")
139+
user.FileWrite(env.ID, "file1.txt", "initial content", "Initial commit")
140+
user.FileWrite(env.ID, "file1.txt", "updated content", "Update file")
141+
user.FileWrite(env.ID, "file2.txt", "new file", "Add second file")
142+
143+
// Get commit log without patches
144+
var logBuf bytes.Buffer
145+
err := repo.Log(ctx, env.ID, false, &logBuf)
146+
logOutput := logBuf.String()
147+
require.NoError(t, err, logOutput)
148+
149+
// Verify commit messages are present
150+
assert.Contains(t, logOutput, "Add second file")
151+
assert.Contains(t, logOutput, "Update file")
152+
assert.Contains(t, logOutput, "Initial commit")
153+
154+
// Get commit log with patches
155+
logBuf.Reset()
156+
err = repo.Log(ctx, env.ID, true, &logBuf)
157+
logWithPatchOutput := logBuf.String()
158+
require.NoError(t, err, logWithPatchOutput)
159+
160+
// Verify patch information is included
161+
assert.Contains(t, logWithPatchOutput, "diff --git")
162+
assert.Contains(t, logWithPatchOutput, "+updated content")
163+
164+
// Test log for non-existent environment
165+
err = repo.Log(ctx, "non-existent-env", false, &logBuf)
166+
assert.Error(t, err)
167+
})
168+
}
169+
170+
// TestRepositoryDiff tests retrieving changes between commits
171+
func TestRepositoryDiff(t *testing.T) {
172+
t.Parallel()
173+
WithRepository(t, "repository-diff", SetupEmptyRepo, func(t *testing.T, repo *repository.Repository, user *UserActions) {
174+
ctx := context.Background()
175+
176+
// Create an environment and make some changes
177+
env := user.CreateEnvironment("Test Diff", "Testing repository diff")
178+
179+
// First commit - add a file
180+
user.FileWrite(env.ID, "test.txt", "initial content\n", "Initial commit")
181+
182+
// Make changes to the file
183+
user.FileWrite(env.ID, "test.txt", "initial content\nupdated content\n", "Update file")
184+
185+
// Get diff output
186+
var diffBuf bytes.Buffer
187+
err := repo.Diff(ctx, env.ID, &diffBuf)
188+
diffOutput := diffBuf.String()
189+
require.NoError(t, err, diffOutput)
190+
191+
// Verify diff contains expected changes
192+
assert.Contains(t, diffOutput, "+updated content")
193+
194+
// Test diff with non-existent environment
195+
err = repo.Diff(ctx, "non-existent-env", &diffBuf)
196+
assert.Error(t, err)
197+
})
198+
}

mcpserver/tools.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ type EnvironmentResponse struct {
128128
RemoteRef string `json:"remote_ref"`
129129
CheckoutCommand string `json:"checkout_command_to_share_with_user"`
130130
LogCommand string `json:"log_command_to_share_with_user"`
131+
DiffCommand string `json:"diff_command_to_share_with_user"`
131132
Services []*environment.Service `json:"services,omitempty"`
132133
}
133134

@@ -142,6 +143,7 @@ func marshalEnvironment(env *environment.Environment) (string, error) {
142143
RemoteRef: fmt.Sprintf("container-use/%s", env.ID),
143144
CheckoutCommand: fmt.Sprintf("cu checkout %s", env.ID),
144145
LogCommand: fmt.Sprintf("cu log %s", env.ID),
146+
DiffCommand: fmt.Sprintf("cu diff %s", env.ID),
145147
Services: env.Services,
146148
}
147149
out, err := json.Marshal(resp)

repository/git.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,33 @@ func (r *Repository) addGitNote(ctx context.Context, env *environment.Environmen
235235
return r.propagateGitNotes(ctx, gitNotesLogRef)
236236
}
237237

238+
func (r *Repository) currentUserBranch(ctx context.Context) (string, error) {
239+
return RunGitCommand(ctx, r.userRepoPath, "branch", "--show-current")
240+
}
241+
242+
func (r *Repository) mergeBase(ctx context.Context, env *environment.EnvironmentInfo) (string, error) {
243+
currentBranch, err := r.currentUserBranch(ctx)
244+
if err != nil {
245+
return "", err
246+
}
247+
currentBranch = strings.TrimSpace(currentBranch)
248+
envGitRef := fmt.Sprintf("%s/%s", containerUseRemote, env.ID)
249+
mergeBase, err := RunGitCommand(ctx, r.userRepoPath, "merge-base", currentBranch, envGitRef)
250+
if err != nil {
251+
return "", err
252+
}
253+
return strings.TrimSpace(mergeBase), nil
254+
}
255+
256+
func (r *Repository) revisionRange(ctx context.Context, env *environment.EnvironmentInfo) (string, error) {
257+
mergeBase, err := r.mergeBase(ctx, env)
258+
if err != nil {
259+
return "", err
260+
}
261+
envGitRef := fmt.Sprintf("%s/%s", containerUseRemote, env.ID)
262+
return fmt.Sprintf("%s..%s", mergeBase, envGitRef), nil
263+
}
264+
238265
func (r *Repository) commitWorktreeChanges(ctx context.Context, worktreePath, name, explanation string) error {
239266
status, err := RunGitCommand(ctx, worktreePath, "status", "--porcelain")
240267
if err != nil {

repository/repository.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"io"
78
"log/slog"
89
"os"
910
"os/exec"
@@ -354,3 +355,62 @@ func (r *Repository) Checkout(ctx context.Context, id, branch string) (string, e
354355

355356
return branch, err
356357
}
358+
359+
func (r *Repository) Log(ctx context.Context, id string, patch bool, w io.Writer) error {
360+
envInfo, err := r.Info(ctx, id)
361+
if err != nil {
362+
return err
363+
}
364+
365+
logArgs := []string{
366+
"git",
367+
"log",
368+
fmt.Sprintf("--notes=%s", gitNotesLogRef),
369+
}
370+
371+
if patch {
372+
logArgs = append(logArgs, "--patch")
373+
}
374+
375+
revisionRange, err := r.revisionRange(ctx, envInfo)
376+
if err != nil {
377+
return err
378+
}
379+
380+
logArgs = append(logArgs, revisionRange)
381+
382+
cmd := exec.CommandContext(ctx, "git")
383+
cmd.Dir = r.userRepoPath
384+
cmd.Args = logArgs
385+
cmd.Stdout = w
386+
cmd.Stderr = w
387+
388+
return cmd.Run()
389+
}
390+
391+
func (r *Repository) Diff(ctx context.Context, id string, w io.Writer) error {
392+
envInfo, err := r.Info(ctx, id)
393+
if err != nil {
394+
return err
395+
}
396+
397+
diffArgs := []string{
398+
"git",
399+
"diff",
400+
}
401+
402+
revisionRange, err := r.revisionRange(ctx, envInfo)
403+
if err != nil {
404+
return err
405+
}
406+
407+
diffArgs = append(diffArgs, revisionRange)
408+
409+
cmd := exec.CommandContext(ctx, "git")
410+
cmd.Dir = r.userRepoPath
411+
cmd.Args = diffArgs
412+
cmd.Stdout = w
413+
cmd.Stderr = w
414+
415+
return cmd.Run()
416+
}

0 commit comments

Comments
 (0)