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  }