-
Notifications
You must be signed in to change notification settings - Fork 266
feat: Add ability to configure the git worktree path via the configuration file #121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
68af1c2
030b597
f37f9ed
5908ac6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,12 @@ func (g *GitWorktree) SetupFromExistingBranch() error { | |
return fmt.Errorf("failed to create worktrees directory: %w", err) | ||
} | ||
|
||
// Ensure the parent directory of the worktree path exists | ||
worktreeParent := filepath.Dir(g.worktreePath) | ||
if err := os.MkdirAll(worktreeParent, 0755); err != nil { | ||
return fmt.Errorf("failed to create worktree parent directory: %w", err) | ||
} | ||
|
||
// Clean up any existing worktree first | ||
_, _ = g.runGitCommand(g.repoPath, "worktree", "remove", "-f", g.worktreePath) // Ignore error if worktree doesn't exist | ||
|
||
|
@@ -57,6 +63,12 @@ func (g *GitWorktree) SetupNewWorktree() error { | |
return fmt.Errorf("failed to create worktrees directory: %w", err) | ||
} | ||
|
||
// Ensure the parent directory of the worktree path exists | ||
worktreeParent := filepath.Dir(g.worktreePath) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please explain why this is needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well So this checks for the directory and creates it if it does not exist to then be able to perform the |
||
if err := os.MkdirAll(worktreeParent, 0755); err != nil { | ||
return fmt.Errorf("failed to create worktree parent directory: %w", err) | ||
} | ||
|
||
// Clean up any existing worktree first | ||
_, _ = g.runGitCommand(g.repoPath, "worktree", "remove", "-f", g.worktreePath) // Ignore error if worktree doesn't exist | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package git | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"strings" | ||
"text/template" | ||
"time" | ||
) | ||
|
||
// PatternVariables holds the available variables for worktree pattern substitution | ||
type PatternVariables struct { | ||
RepoRoot string | ||
RepoName string | ||
IssueNumber string | ||
Title string | ||
Timestamp string | ||
} | ||
|
||
// parseWorktreePattern substitutes variables in the pattern with actual values | ||
func parseWorktreePattern(pattern string, vars PatternVariables) string { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah I will update to this approach,I am pretty new to go so still learning alot of things 😅 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated via 5908ac6 |
||
if pattern == "" { | ||
return "" | ||
} | ||
|
||
// If timestamp is empty, generate it | ||
if vars.Timestamp == "" { | ||
vars.Timestamp = fmt.Sprintf("%x", time.Now().UnixNano()) | ||
} | ||
|
||
// Convert the pattern from {variable} format to {{.Variable}} format for text/template | ||
templatePattern := convertToTemplateFormat(pattern) | ||
|
||
// Create and parse the template | ||
tmpl, err := template.New("worktree").Parse(templatePattern) | ||
if err != nil { | ||
// If template parsing fails, fallback to original pattern | ||
return pattern | ||
} | ||
|
||
// Execute the template with the variables | ||
var result strings.Builder | ||
err = tmpl.Execute(&result, vars) | ||
if err != nil { | ||
// If template execution fails, fallback to original pattern | ||
return pattern | ||
} | ||
|
||
output := result.String() | ||
|
||
// Clean up delimiters from empty variables | ||
output = cleanupDelimiters(output) | ||
|
||
// Expand tilde to home directory | ||
if strings.HasPrefix(output, "~/") { | ||
homeDir, err := os.UserHomeDir() | ||
if err == nil { | ||
output = filepath.Join(homeDir, output[2:]) | ||
} | ||
} | ||
|
||
// Clean up the path | ||
output = filepath.Clean(output) | ||
|
||
return output | ||
} | ||
|
||
// convertToTemplateFormat converts {variable} format to {{.Variable}} format for text/template | ||
func convertToTemplateFormat(pattern string) string { | ||
// Map of old format to new format | ||
replacements := map[string]string{ | ||
"{repo_root}": "{{.RepoRoot}}", | ||
"{repo_name}": "{{.RepoName}}", | ||
"{issue_number}": "{{.IssueNumber}}", | ||
"{title}": "{{.Title}}", | ||
"{timestamp}": "{{.Timestamp}}", | ||
} | ||
|
||
result := pattern | ||
for old, new := range replacements { | ||
result = strings.ReplaceAll(result, old, new) | ||
} | ||
|
||
return result | ||
} | ||
|
||
// cleanupDelimiters removes unnecessary delimiters left by empty variables | ||
func cleanupDelimiters(s string) string { | ||
// Common delimiters to clean up | ||
delimiters := "-_.:" | ||
|
||
// Remove leading delimiters | ||
s = strings.TrimLeft(s, delimiters) | ||
|
||
// Remove trailing delimiters | ||
s = strings.TrimRight(s, delimiters) | ||
|
||
// Replace multiple consecutive delimiters with a single one | ||
// We need to handle each delimiter type separately to preserve the original delimiter | ||
for _, delim := range delimiters { | ||
delimStr := string(delim) | ||
multiple := delimStr + delimStr | ||
for strings.Contains(s, multiple) { | ||
s = strings.ReplaceAll(s, multiple, delimStr) | ||
} | ||
} | ||
|
||
// Special case: remove delimiter before or after path separator | ||
// e.g., "/-" -> "/", "-/" -> "/" | ||
for _, delim := range delimiters { | ||
s = strings.ReplaceAll(s, "/"+string(delim), "/") | ||
s = strings.ReplaceAll(s, string(delim)+"/", "/") | ||
} | ||
|
||
return s | ||
} | ||
|
||
// extractIssueNumber attempts to extract an issue number from the session name | ||
func extractIssueNumber(sessionName string) string { | ||
// Look for patterns like "#123", "issue-123", "issue/123", etc. | ||
patterns := []string{ | ||
`#(\d+)`, | ||
`issue[-/](\d+)`, | ||
`(\d+)[-_]`, | ||
`^(\d+)$`, | ||
} | ||
|
||
for _, pattern := range patterns { | ||
re := regexp.MustCompile(pattern) | ||
matches := re.FindStringSubmatch(sessionName) | ||
if len(matches) > 1 { | ||
return matches[1] | ||
} | ||
} | ||
|
||
return "" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be good to make this apply to the branch name too. I think it's good for branch names to be 1:1 with worktree paths.
Maybe we can call it
WorkStreamPattern
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you thinking the branch name would be another pattern we include?