✨ feat(worktree): add support for shared resources across worktrees#35
✨ feat(worktree): add support for shared resources across worktrees#35
Conversation
Signed-off-by: samzong <samzong.lu@gmail.com>
Summary of ChangesHello @samzong, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a robust mechanism for managing shared resources across Git worktrees. It enables users to define specific files or directories that should be automatically synchronized to all worktrees, either by copying them or creating symbolic links. This feature streamlines development workflows by ensuring consistent environments and simplifying the management of common assets like configuration files or large data directories, reducing manual setup and potential inconsistencies. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
Signed-off-by: samzong <samzong.lu@gmail.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a valuable feature for sharing resources across worktrees, complete with CLI commands and an interactive mode. The implementation is well-structured, but I've identified a few areas for improvement to enhance its robustness and maintainability. My feedback includes fixing a critical bug that could cause a panic, improving error handling in the interactive prompts, addressing code duplication, and ensuring tests are platform-independent. I've also suggested a minor change to follow logging conventions. Overall, great work on this new functionality.
| info, err := os.Stat(srcPath) | ||
| if os.IsNotExist(err) { | ||
| if c.verbose { | ||
| fmt.Printf("Shared resource source not found: %s\n", srcPath) | ||
| } | ||
| return nil | ||
| } |
There was a problem hiding this comment.
There's a potential nil pointer dereference here. os.Stat(srcPath) can return an error other than os.IsNotExist. In that case, the error is not handled, and execution continues. Later, on line 130, info.IsDir() would cause a panic because info would be nil. You should handle all non-nil errors from os.Stat.
info, err := os.Stat(srcPath)
if err != nil {
if os.IsNotExist(err) {
if c.verbose {
fmt.Printf("Shared resource source not found: %s\n", srcPath)
}
return nil // Not an error, just nothing to sync
}
return fmt.Errorf("failed to stat source resource %s: %w", srcPath, err)
}| } | ||
|
|
||
| func promptAddResource(c *worktree.Client, reader *bufio.Reader) { | ||
| root, _ := c.GetWorktreeRoot() |
There was a problem hiding this comment.
The error returned by c.GetWorktreeRoot() is ignored. If this call fails, root will be an empty string. This will cause strings.HasPrefix(cwd, root) on line 183 to always evaluate to true, leading to incorrect path manipulation logic later on. You should handle this error to prevent unexpected behavior.
root, err := c.GetWorktreeRoot()
if err != nil {
fmt.Printf("Error: could not determine project root: %v\n", err)
return
}| if !cmd.Flags().Changed("strategy") { | ||
| reader := bufio.NewReader(os.Stdin) | ||
| fmt.Println("Strategy:") | ||
| fmt.Println(" 1. copy - each worktree gets its own copy") | ||
| fmt.Println(" 2. link - symlink to shared source") | ||
| fmt.Print("Select [1/2, default: 1]: ") | ||
| strategyStr, _ := reader.ReadString('\n') | ||
| strategyStr = strings.TrimSpace(strings.ToLower(strategyStr)) | ||
| if strategyStr == "2" || strategyStr == "link" || strategyStr == "l" { | ||
| strategy = worktree.StrategySymlink | ||
| } else { | ||
| strategy = worktree.StrategyCopy | ||
| } | ||
| } |
There was a problem hiding this comment.
The logic for selecting a strategy interactively is also present in promptAddResource (lines 208-220). To avoid duplication and improve maintainability, you could extract this logic into a helper function, for example: func promptForStrategy(reader *bufio.Reader) worktree.ResourceStrategy. This would centralize the prompt text and input parsing.
| fmt.Println(" 1. copy - each worktree gets its own copy") | ||
| fmt.Println(" 2. link - symlink to shared source") | ||
| fmt.Print("Select [1/2, default: 1]: ") | ||
| strategyStr, _ := reader.ReadString('\n') |
There was a problem hiding this comment.
The error from reader.ReadString('\n') is ignored. If an error occurs (e.g., io.EOF if the user presses Ctrl+D), the program will continue with a potentially empty or partial strategyStr, leading to incorrect behavior. This pattern of ignoring errors from ReadString is repeated throughout the file (e.g., lines 154, 196, 212, 228, 241). It's best practice to handle these errors.
strategyStr, err := reader.ReadString('\n')
if err != nil {
return fmt.Errorf("failed to read strategy input: %w", err)
}| target, err := os.Readlink(filepath.Join(wtPath, "models")) | ||
| require.NoError(t, err) | ||
| // On Darwin/Linux it should be relative | ||
| assert.Equal(t, "../models", target) |
There was a problem hiding this comment.
The test hardcodes the expected symlink target as "../models". This will fail on Windows, where the path separator is \ and the relative path would be ..\models. To make this test platform-independent, you should use a helper from the path/filepath package to construct the expected path.
| assert.Equal(t, "../models", target) | |
| assert.Equal(t, filepath.FromSlash("../models"), target) |
|
|
||
| // Sync shared resources | ||
| if err := c.SyncSharedResources(name); err != nil { | ||
| fmt.Printf("Warning: failed to sync shared resources: %v\n", err) |
There was a problem hiding this comment.
The warning message is printed to standard output using fmt.Printf. It's conventional for warnings and errors to be printed to standard error (stderr). This allows users to redirect normal output (stdout) without losing visibility of warnings. You should use fmt.Fprintf(os.Stderr, ...) instead.
| fmt.Printf("Warning: failed to sync shared resources: %v\n", err) | |
| fmt.Fprintf(os.Stderr, "Warning: failed to sync shared resources: %v\n", err) |
|
|
||
| // Sync shared resources | ||
| if err := c.SyncSharedResources(dirName); err != nil { | ||
| fmt.Printf("Warning: failed to sync shared resources for %s: %v\n", dirName, err) |
There was a problem hiding this comment.
The warning message is printed to standard output using fmt.Printf. It's conventional for warnings and errors to be printed to standard error (stderr). This allows users to redirect normal output (stdout) without losing visibility of warnings. You should use fmt.Fprintf(os.Stderr, ...) instead.
| fmt.Printf("Warning: failed to sync shared resources for %s: %v\n", dirName, err) | |
| fmt.Fprintf(os.Stderr, "Warning: failed to sync shared resources for %s: %v\n", dirName, err) |
No description provided.