github.com/purpleclay/gitz@v0.8.2-0.20240515052600-43f80eea2fe1/status.go (about)

     1  package git
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"strings"
     7  )
     8  
     9  // FileStatusIndicator contains a single character that represents
    10  // a files status within a git repository. Based on the git
    11  // specification: https://git-scm.com/docs/git-status#_output
    12  type FileStatusIndicator byte
    13  
    14  const (
    15  	Added       FileStatusIndicator = 'A'
    16  	Copied      FileStatusIndicator = 'C'
    17  	Deleted     FileStatusIndicator = 'D'
    18  	Ignored     FileStatusIndicator = '!'
    19  	Modified    FileStatusIndicator = 'M'
    20  	Renamed     FileStatusIndicator = 'R'
    21  	TypeChanged FileStatusIndicator = 'T'
    22  	Updated     FileStatusIndicator = 'U'
    23  	Unmodified  FileStatusIndicator = ' '
    24  	Untracked   FileStatusIndicator = '?'
    25  )
    26  
    27  // FileStatus represents the status of a file within a repository
    28  type FileStatus struct {
    29  	// Indicators is a two character array that contains
    30  	// the current status of a file within both the current index
    31  	// and the working repository tree.
    32  	//
    33  	// Examples:
    34  	//
    35  	// 	'??' - a file that is not tracked
    36  	//	' A' - a file that has been added to the working tree
    37  	//	'M ' - a file that has been modified within the index
    38  	Indicators [2]FileStatusIndicator
    39  
    40  	// Path of the file relative to the root of the
    41  	// current repository
    42  	Path string
    43  }
    44  
    45  // String representation of a file status that adheres to the
    46  // porcelain v1 format
    47  func (f FileStatus) String() string {
    48  	return fmt.Sprintf("%c%c %s", f.Indicators[0], f.Indicators[1], f.Path)
    49  }
    50  
    51  // PorcelainStatus identifies if there are any changes within the current
    52  // repository (working directory) and returns them in the parseable
    53  // porcelain v1 format
    54  func (c *Client) PorcelainStatus() ([]FileStatus, error) {
    55  	log, err := c.exec("git status --porcelain")
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	return parsePorcelainV1(log), nil
    61  }
    62  
    63  // Clean determines if the current repository (working directory) is in
    64  // a clean state. A repository is deemed clean, if it contains no changes
    65  func (c *Client) Clean() (bool, error) {
    66  	statuses, err := c.PorcelainStatus()
    67  	return len(statuses) == 0, err
    68  }
    69  
    70  func parsePorcelainV1(log string) []FileStatus {
    71  	var statuses []FileStatus
    72  
    73  	scanner := bufio.NewScanner(strings.NewReader(log))
    74  	scanner.Split(bufio.ScanLines)
    75  
    76  	for scanner.Scan() {
    77  		line := scanner.Text()
    78  		statuses = append(statuses, FileStatus{
    79  			Indicators: [2]FileStatusIndicator{
    80  				FileStatusIndicator(line[0]),
    81  				FileStatusIndicator(line[1]),
    82  			},
    83  			Path: line[3:],
    84  		})
    85  	}
    86  
    87  	return statuses
    88  }