github.com/driusan/dgit@v0.0.0-20221118233547-f39f0c15edbb/git/file.go (about) 1 package git 2 3 import ( 4 "bufio" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "strings" 10 ) 11 12 // A file represents a file (or directory) relative to os.Getwd() 13 type File string 14 15 // Determines if the file exists on the filesystem. 16 func (f File) Exists() bool { 17 if _, err := os.Lstat(string(f)); err == nil { 18 return true 19 } 20 return false 21 } 22 23 func (f File) String() string { 24 return string(f) 25 } 26 27 // Appends the value val to the end of the file f. 28 // Not that f must already exist. 29 func (f File) Append(val string) error { 30 if !f.Exists() { 31 return fmt.Errorf("File %s does not exist", f) 32 } 33 fi, err := os.OpenFile(f.String(), os.O_RDWR|os.O_APPEND, 0660) 34 if err != nil { 35 return err 36 } 37 defer fi.Close() 38 fmt.Fprintf(fi, "%s", val) 39 return nil 40 } 41 42 // Normalizes the file name that's relative to the current working directory 43 // to be relative to the workdir root. Ie. convert it from a file system 44 // path to an index path. 45 func (f File) IndexPath(c *Client) (IndexPath, error) { 46 p, err := filepath.Abs(f.String()) 47 if err != nil { 48 return "", err 49 } 50 // BUG(driusan): This should verify that there is a prefix and return 51 // an error if it's outside of the tree. 52 return IndexPath(strings.TrimPrefix(p, string(c.WorkDir)+"/")), nil 53 } 54 55 // Returns stat information for the given file. 56 func (f File) Stat() (os.FileInfo, error) { 57 return os.Stat(string(f)) 58 } 59 60 // Returns lstat information for the given file. 61 func (f File) Lstat() (os.FileInfo, error) { 62 return os.Lstat(string(f)) 63 } 64 65 func (f File) IsDir() bool { 66 stat, err := f.Lstat() 67 if err != nil { 68 // If we couldn't stat it, it's not a directory.. 69 return false 70 } 71 return stat.IsDir() 72 73 } 74 75 // Returns true if the file is inside a submodule and the submodule file. 76 // FIXME: invert this when submodules are implemented 77 func (f File) IsInSubmodule(c *Client) (bool, File, error) { 78 abs, err := filepath.Abs(f.String()) 79 if err != nil { 80 return false, File(""), err 81 } 82 83 for abs != c.WorkDir.String() { 84 stat, _ := os.Lstat(filepath.Join(abs, ".git")) 85 if stat != nil { 86 submodule, err := File(abs).IndexPath(c) 87 if err != nil { 88 return false, File(""), err 89 } 90 return true, File(string(submodule)), nil 91 } 92 93 abs = filepath.Dir(abs) 94 } 95 96 return false, File(""), nil 97 } 98 99 func (f File) IsInsideSymlink() (bool, error) { 100 abs, err := filepath.Abs(f.String()) 101 if err != nil { 102 return false, err 103 } 104 105 dir := filepath.Dir(abs) 106 107 evalPath, _ := filepath.EvalSymlinks(dir) 108 if evalPath != "" && evalPath != dir { 109 return true, nil 110 } 111 112 return false, nil 113 } 114 115 func (f File) IsSymlink() bool { 116 stat, err := f.Lstat() 117 if err != nil { 118 // If we couldn't stat it, it's not a directory.. 119 return false 120 } 121 // This is probably not robust. It's assuming every OS 122 // uses the same modes as git, but is probably good enough. 123 return stat.Mode()&os.ModeSymlink == os.ModeSymlink 124 } 125 126 func (f File) Create() (*os.File, error) { 127 dir := File(filepath.Dir(f.String())) 128 if !dir.Exists() { 129 if err := os.MkdirAll(dir.String(), 0755); err != nil { 130 return nil, err 131 } 132 } 133 134 return os.Create(f.String()) 135 } 136 137 // Reads the entire contents of file and return as a string. Note 138 // that this should only be used for very small files (like refspecs) 139 // 140 func (f File) ReadAll() (string, error) { 141 val, err := ioutil.ReadFile(f.String()) 142 if err != nil { 143 return "", err 144 } 145 return string(val), nil 146 } 147 148 // Reads the first line of File. (This is primarily to extract commit message 149 // lines for reflogs) 150 func (f File) ReadFirstLine() (string, error) { 151 if !f.Exists() { 152 return "", fmt.Errorf("File %s does not exist", f) 153 } 154 fi, err := os.Open(f.String()) 155 if err != nil { 156 return "", err 157 } 158 defer fi.Close() 159 scanner := bufio.NewScanner(fi) 160 scanner.Scan() 161 if err := scanner.Err(); err != nil { 162 return "", err 163 } 164 return scanner.Text(), nil 165 } 166 167 func (f File) Remove() error { 168 return os.Remove(f.String()) 169 } 170 171 func (f File) Open() (*os.File, error) { 172 return os.Open(f.String()) 173 } 174 175 // Returns true if f matches the filesystem glob pattern pattern. 176 func (f File) MatchGlob(pattern string) bool { 177 m, err := filepath.Match(pattern, string(f)) 178 if err != nil { 179 panic(err) 180 } 181 return m 182 }