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  }