1
1
use super :: { get_head, utils:: repo, CommitId } ;
2
2
use crate :: error:: Result ;
3
3
use git2:: { ErrorCode , ObjectType , Repository , Signature } ;
4
+ use gpgme:: { Context , Protocol } ;
4
5
use scopetime:: scope_time;
5
6
6
7
///
@@ -54,6 +55,7 @@ pub fn commit(repo_path: &str, msg: &str) -> Result<CommitId> {
54
55
scope_time ! ( "commit" ) ;
55
56
56
57
let repo = repo ( repo_path) ?;
58
+ let config = repo. config ( ) ?;
57
59
58
60
let signature = signature_allow_undefined_name ( & repo) ?;
59
61
let mut index = repo. index ( ) ?;
@@ -68,16 +70,74 @@ pub fn commit(repo_path: &str, msg: &str) -> Result<CommitId> {
68
70
69
71
let parents = parents. iter ( ) . collect :: < Vec < _ > > ( ) ;
70
72
71
- Ok ( repo
72
- . commit (
73
+ let commit_oid = if config. get_bool ( "commit.gpgsign" ) ? {
74
+ // Generate commit content
75
+ let commit_bufffer = repo. commit_create_buffer (
76
+ & signature,
77
+ & signature,
78
+ msg,
79
+ & tree,
80
+ parents. as_slice ( ) ,
81
+ ) ?;
82
+ let commit_content = commit_bufffer
83
+ . as_str ( )
84
+ . expect ( "Buffer was not valid UTF-8" ) ;
85
+
86
+ // Prepare to sign using the designated key in the user's git config
87
+ let mut gpg_ctx = Context :: from_protocol ( Protocol :: OpenPgp ) ?;
88
+ let key =
89
+ gpg_ctx. get_key ( config. get_string ( "user.signingkey" ) ?) ?;
90
+ gpg_ctx. add_signer ( & key) ?;
91
+ gpg_ctx. set_armor ( true ) ;
92
+
93
+ // Create GPG signature for commit content
94
+ let mut signature_buffer = Vec :: new ( ) ;
95
+ gpg_ctx
96
+ . sign_detached ( & * commit_bufffer, & mut signature_buffer) ?;
97
+ let gpg_signature =
98
+ std:: str:: from_utf8 ( & signature_buffer) . unwrap ( ) ;
99
+
100
+ let commit_oid = repo. commit_signed (
101
+ & commit_content,
102
+ & gpg_signature,
103
+ None ,
104
+ ) ?;
105
+
106
+ match repo. head ( ) {
107
+ // If HEAD reference is returned, simply update the target.
108
+ Ok ( mut head) => {
109
+ head. set_target ( commit_oid, msg) ?;
110
+ }
111
+ // If there is an error getting HEAD, likely it is a new repo
112
+ // and a reference to a default branch needs to be created.
113
+ Err ( _) => {
114
+ // Default branch name behavior as of git 2.28.
115
+ let default_branch_name = config
116
+ . get_str ( "init.defaultBranch" )
117
+ . unwrap_or ( "master" ) ;
118
+
119
+ repo. reference (
120
+ & format ! ( "refs/heads/{}" , default_branch_name) ,
121
+ commit_oid,
122
+ true ,
123
+ msg,
124
+ ) ?;
125
+ }
126
+ }
127
+
128
+ commit_oid
129
+ } else {
130
+ repo. commit (
73
131
Some ( "HEAD" ) ,
74
132
& signature,
75
133
& signature,
76
134
msg,
77
135
& tree,
78
136
parents. as_slice ( ) ,
79
137
) ?
80
- . into ( ) )
138
+ } ;
139
+
140
+ Ok ( commit_oid. into ( ) )
81
141
}
82
142
83
143
/// Tag a commit.
0 commit comments