github.com/khulnasoft/codebase@v0.0.0-20231214144635-a707781cbb24/service/serviceutil/serviceutil.go (about) 1 package serviceutil 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "strings" 8 ) 9 10 // GitRelWorkdir returns git relative workdir of current directory. 11 // 12 // It should return the same output as `git rev-parse --show-prefix`. 13 // It does not execute `git` command to avoid needless git binary dependency. 14 // 15 // An example problem due to `git` command dependency: https://github.com/khulnasoft/codebase/issues/1158 16 func GitRelWorkdir() (string, error) { 17 cwd, err := os.Getwd() 18 if err != nil { 19 return "", err 20 } 21 root, err := findGitRoot(cwd) 22 if err != nil { 23 return "", err 24 } 25 if !strings.HasPrefix(cwd, root) { 26 return "", fmt.Errorf("cannot get GitRelWorkdir: cwd=%q, root=%q", cwd, root) 27 } 28 const separator = string(filepath.Separator) 29 path := strings.Trim(strings.TrimPrefix(cwd, root), separator) 30 if path != "" { 31 path += separator 32 } 33 return path, nil 34 } 35 36 func GetGitRoot() (string, error) { 37 cwd, err := os.Getwd() 38 if err != nil { 39 return "", err 40 } 41 return findGitRoot(cwd) 42 } 43 44 func findGitRoot(path string) (string, error) { 45 gitPath, err := findDotGitPath(path) 46 if err != nil { 47 return "", err 48 } 49 return filepath.Dir(gitPath), nil 50 } 51 52 func findDotGitPath(path string) (string, error) { 53 // normalize the path 54 path, err := filepath.Abs(path) 55 if err != nil { 56 return "", err 57 } 58 59 for { 60 fi, err := os.Stat(filepath.Join(path, ".git")) 61 if err == nil { 62 if !fi.IsDir() { 63 return "", fmt.Errorf(".git exist but is not a directory") 64 } 65 return filepath.Join(path, ".git"), nil 66 } 67 if !os.IsNotExist(err) { 68 // unknown error 69 return "", err 70 } 71 72 // detect bare repo 73 ok, err := isGitDir(path) 74 if err != nil { 75 return "", err 76 } 77 if ok { 78 return path, nil 79 } 80 81 parent := filepath.Dir(path) 82 if parent == path { 83 return "", fmt.Errorf(".git not found") 84 } 85 path = parent 86 } 87 } 88 89 // ref: https://github.com/git/git/blob/3bab5d56259722843359702bc27111475437ad2a/setup.c#L328-L338 90 func isGitDir(path string) (bool, error) { 91 markers := []string{"HEAD", "objects", "refs"} 92 for _, marker := range markers { 93 _, err := os.Stat(filepath.Join(path, marker)) 94 if err == nil { 95 continue 96 } 97 if !os.IsNotExist(err) { 98 // unknown error 99 return false, err 100 } 101 return false, nil 102 } 103 return true, nil 104 }