github.com/driusan/dgit@v0.0.0-20221118233547-f39f0c15edbb/git/difffiles.go (about) 1 package git 2 3 import ( 4 "log" 5 "sort" 6 ) 7 8 // Options that are shared between git diff, git diff-files, diff-index, 9 // and diff-tree 10 type DiffCommonOptions struct { 11 // Print a patch, not just the sha differences 12 Patch bool 13 14 // The 0 value implies 3. 15 NumContextLines int 16 17 // Generate the diff in raw format, not a unified diff 18 Raw bool 19 20 // Exit with a exit code of 1 if there are any diffs 21 ExitCode bool 22 } 23 24 // Describes the options that may be specified on the command line for 25 // "git diff-files". Note that only raw mode is currently supported, even 26 // though all the other options are parsed/set in this struct. 27 type DiffFilesOptions struct { 28 DiffCommonOptions 29 } 30 31 // DiffFiles implements the git diff-files command. 32 // It compares the file system to the index. 33 func DiffFiles(c *Client, opt DiffFilesOptions, paths []File) ([]HashDiff, error) { 34 indexentries, err := LsFiles( 35 c, 36 LsFilesOptions{ 37 Cached: true, Deleted: true, Modified: true, 38 }, 39 paths, 40 ) 41 if err != nil { 42 return nil, err 43 } 44 45 var val []HashDiff 46 47 for _, idx := range indexentries { 48 fs := TreeEntry{} 49 idxtree := TreeEntry{idx.Sha1, idx.Mode} 50 51 f, err := idx.PathName.FilePath(c) 52 if err != nil || !f.Exists() { 53 // If there was an error, treat it as a non-existant file 54 // and just use the empty Sha1 55 val = append(val, HashDiff{idx.PathName, idxtree, fs, uint(idx.Fsize), 0}) 56 continue 57 } 58 stat, err := f.Lstat() 59 if err != nil { 60 val = append(val, HashDiff{idx.PathName, idxtree, fs, uint(idx.Fsize), 0}) 61 continue 62 } 63 64 switch { 65 case stat.Mode().IsDir(): 66 // Since we're diffing files in the index (which only holds files) 67 // against a directory, it means that the file was deleted and 68 // replaced by a directory. 69 val = append(val, HashDiff{idx.PathName, idxtree, fs, uint(idx.Fsize), 0}) 70 continue 71 case !stat.Mode().IsRegular(): 72 // FIXME: This doesn't take into account that the file 73 // might be some kind of non-symlink non-regular file. 74 fs.FileMode = ModeSymlink 75 case stat.Mode().Perm()&0100 != 0: 76 fs.FileMode = ModeExec 77 default: 78 fs.FileMode = ModeBlob 79 } 80 size := stat.Size() 81 if err := idx.CompareStat(f); err != nil { 82 log.Printf("Stat information does not match for %v: %v\n", f, err) 83 val = append(val, HashDiff{idx.PathName, idxtree, fs, uint(idx.Fsize), uint(size)}) 84 continue 85 } 86 87 // We couldn't short-circuit by checking the stat info, so fall back on hashing 88 // the file. 89 hash, _, err := HashFile("blob", f.String()) 90 91 if err != nil || hash != idx.Sha1 { 92 val = append(val, HashDiff{idx.PathName, idxtree, fs, uint(idx.Fsize), uint(size)}) 93 } 94 } 95 96 sort.Sort(ByName(val)) 97 98 return val, nil 99 }