| 1 | package boot |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "os" |
| 6 | "path/filepath" |
| 7 | "time" |
| 8 | |
| 9 | "reasonix/internal/gitcmd" |
| 10 | ) |
| 11 | |
| 12 | // workspaceRepoTimeout bounds the one git call a session open makes. |
| 13 | const workspaceRepoTimeout = 10 * time.Second |
| 14 | |
| 15 | // workspaceRepo is root's git identity for the session being built, settled |
| 16 | // before the session runs any command. A rebuild of the same workspace keeps |
| 17 | // the identity its session opened with instead of rediscovering it from files |
| 18 | // that session could since have written. |
| 19 | func workspaceRepo(ctx context.Context, carried gitcmd.Repo, root string) gitcmd.Repo { |
| 20 | abs, err := filepath.Abs(root) |
| 21 | if err != nil { |
| 22 | return gitcmd.Repo{Dir: root} |
| 23 | } |
| 24 | if carried.Dir != "" && filepath.Clean(carried.Dir) == abs { |
| 25 | return carried |
| 26 | } |
| 27 | if !underGitMarker(abs) { |
| 28 | return gitcmd.Repo{Dir: abs} |
| 29 | } |
| 30 | ctx, cancel := context.WithTimeout(ctx, workspaceRepoTimeout) |
| 31 | defer cancel() |
| 32 | repo, err := gitcmd.Open(ctx, abs) |
| 33 | if err != nil { |
| 34 | return gitcmd.Repo{Dir: abs} |
| 35 | } |
| 36 | return repo |
| 37 | } |
| 38 | |
| 39 | // underGitMarker reports whether dir or an ancestor holds a .git entry, so a |
| 40 | // workspace outside any repository never starts git (on macOS a missing git is |
| 41 | // an installer prompt, not an error). |
| 42 | func underGitMarker(dir string) bool { |
| 43 | for { |
| 44 | if _, err := os.Lstat(filepath.Join(dir, ".git")); err == nil { |
| 45 | return true |
| 46 | } |
| 47 | parent := filepath.Dir(dir) |
| 48 | if parent == dir { |
| 49 | return false |
| 50 | } |
| 51 | dir = parent |
| 52 | } |
| 53 | } |
| 54 |