github.com/purpleclay/gitz@v0.8.2-0.20240515052600-43f80eea2fe1/commit.go (about) 1 package git 2 3 import ( 4 "fmt" 5 "strings" 6 ) 7 8 // CommitOption provides a way for setting specific options during a commit 9 // operation. Each supported option can customize the way the commit is 10 // created against the current repository (working directory) 11 type CommitOption func(*commitOptions) 12 13 type commitOptions struct { 14 AllowEmpty bool 15 Config []string 16 ForceNoSigned bool 17 Signed bool 18 SigningKey string 19 } 20 21 // WithAllowEmpty allows a commit to be created without having to track 22 // any changes. This bypasses the default protection by git, preventing 23 // a commit from having the exact same tree as its parent 24 func WithAllowEmpty() CommitOption { 25 return func(opts *commitOptions) { 26 opts.AllowEmpty = true 27 } 28 } 29 30 // WithCommitConfig allows temporary git config to be set during the 31 // execution of the commit. Config set using this approach will override 32 // any config defined within existing git config files. Config must be 33 // provided as key value pairs, mismatched config will result in an 34 // [ErrMissingConfigValue] error. Any invalid paths will result in an 35 // [ErrInvalidConfigPath] error 36 func WithCommitConfig(kv ...string) CommitOption { 37 return func(opts *commitOptions) { 38 opts.Config = trim(kv...) 39 } 40 } 41 42 // WithGpgSign will create a GPG-signed commit using the GPG key associated 43 // with the committers email address. Overriding this behavior is possible 44 // through the user.signingkey config setting. This option does not need 45 // to be explicitly called if the commit.gpgSign config setting is set to 46 // true 47 func WithGpgSign() CommitOption { 48 return func(opts *commitOptions) { 49 opts.Signed = true 50 } 51 } 52 53 // WithGpgSigningKey will create a GPG-signed commit using the provided GPG 54 // key ID, overridding any default GPG key set by the user.signingKey git 55 // config setting 56 func WithGpgSigningKey(key string) CommitOption { 57 return func(opts *commitOptions) { 58 opts.Signed = true 59 opts.SigningKey = strings.TrimSpace(key) 60 } 61 } 62 63 // WithNoGpgSign ensures the created commit will not be GPG signed 64 // regardless of the value assigned to the repositories commit.gpgSign 65 // git config setting 66 func WithNoGpgSign() CommitOption { 67 return func(opts *commitOptions) { 68 opts.ForceNoSigned = true 69 } 70 } 71 72 // Commit a snapshot of changes within the current repository (working directory) 73 // and describe those changes with a given log message. Commit behavior can be 74 // customized through the use of options 75 func (c *Client) Commit(msg string, opts ...CommitOption) (string, error) { 76 options := &commitOptions{} 77 for _, opt := range opts { 78 opt(options) 79 } 80 81 cfg, err := ToInlineConfig(options.Config...) 82 if err != nil { 83 return "", err 84 } 85 86 var buf strings.Builder 87 buf.WriteString("git") 88 89 if len(cfg) > 0 { 90 buf.WriteString(" ") 91 buf.WriteString(strings.Join(cfg, " ")) 92 } 93 buf.WriteString(" commit") 94 95 if options.AllowEmpty { 96 buf.WriteString(" --allow-empty") 97 } 98 99 if options.Signed { 100 buf.WriteString(" -S") 101 } 102 103 if options.SigningKey != "" { 104 buf.WriteString(" --gpg-sign=" + options.SigningKey) 105 } 106 107 if options.ForceNoSigned { 108 buf.WriteString(" --no-gpg-sign") 109 } 110 111 buf.WriteString(fmt.Sprintf(" -m '%s'", msg)) 112 return c.exec(buf.String()) 113 } 114 115 // CommitVerification contains details about a GPG signed commit 116 type CommitVerification struct { 117 // Author represents a person who originally created the files 118 // within the repository 119 Author Person 120 121 // Committer represents a person who changed any existing files 122 // within the repository 123 Committer Person 124 125 // Hash contains the unique identifier associated with the commit 126 Hash string 127 128 // Message contains the message associated with the commit 129 Message string 130 131 // Signature contains details of the verified GPG signature 132 Signature *Signature 133 } 134 135 // VerifyCommit validates that a given commit has a valid GPG signature 136 // and returns details about that signature 137 func (c *Client) VerifyCommit(hash string) (*CommitVerification, error) { 138 out, err := c.exec("git verify-commit -v " + hash) 139 if err != nil { 140 return nil, err 141 } 142 143 out, _ = until("author ")(out) 144 out, pair := separatedPair(tag("author "), ws(), until("committer "))(out) 145 author := parsePerson(pair[1]) 146 147 out, pair = separatedPair(tag("committer "), ws(), takeUntil(lineEnding))(out) 148 committer := parsePerson(pair[1]) 149 out, _ = line()(out) 150 151 out, mesage := until("gpg: ")(out) 152 153 return &CommitVerification{ 154 Author: author, 155 Committer: committer, 156 Hash: hash, 157 Message: strings.TrimSpace(mesage), 158 Signature: parseSignature(out), 159 }, nil 160 }