Skip to content

Commit 546e40c

Browse files
committed
Add signing callbacks for git_rebase_commit in git_rebase_options
2 callbacks have been added to git_rebase_options, git_rebase_commit_signature_cb and git_rebase_commit_signature_field_cb. When git_rebase_commit_signature_cb is present in git_rebase_options, it will be called whenever git_rebase_commit is performed, giving an opportunity to sign the commit. The signing procedure can be skipped if the callback specifies passthrough as the error. The git_rebase_commit_signature_field_cb will only be called if the other callback is present or did not passthrough, and it provides means to specify which field a signature is for. Git_rebase_options was chosen as the home for these callbacks as it keeps backwards compatibility with the current rebase api.
1 parent 635693d commit 546e40c

File tree

2 files changed

+94
-5
lines changed

2 files changed

+94
-5
lines changed

include/git2/rebase.h

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,36 @@
2323
*/
2424
GIT_BEGIN_DECL
2525

26+
/**
27+
* Rebase commit signature callback.
28+
*
29+
* The callback will be called with the commit content, giving a user an
30+
* opportunity to sign the commit content in a rebase.
31+
* The signature parameter will be owned by LibGit2 after this callback returns.
32+
*
33+
* When the callback:
34+
* - returns GIT_PASSTHROUGH, no signature will be added to the commit.
35+
* - returns < 0, git_rebase_commit will be aborted.
36+
* - returns GIT_OK, the signature parameter is expected to be filled.
37+
*/
38+
typedef int (*git_rebase_commit_signature_cb)(
39+
char **signature, const char *commit_content, void *payload);
40+
41+
/**
42+
* Rebase commit signature field callback.
43+
*
44+
* The callback will be called if a signature_cb was called and successful.
45+
* This callback will provide the field that a user is signing in a git_rebase_commit.
46+
* The signature_field parameter will be owned by LibGit2 after this callback returns.
47+
*
48+
* When the callback:
49+
* - returns GIT_PASSTHROUGH, signature_field is expected to remain null.
50+
* - returns < 0, git_rebase_commit will be aborted.
51+
* - returns GIT_OK, the signature_field parameter is expected to be filled.
52+
*/
53+
typedef int (*git_rebase_commit_signature_field_cb)(
54+
char **signature_field, void *payload);
55+
2656
/**
2757
* Rebase options
2858
*
@@ -72,6 +102,28 @@ typedef struct {
72102
* `abort` to match git semantics.
73103
*/
74104
git_checkout_options checkout_options;
105+
106+
/**
107+
* If provided, this will be called with the commit content, allowing
108+
* a signature to be added to the rebase commit. Can be skipped with
109+
* GIT_PASSTHROUGH. If GIT_PASSTHROUGH is returned, a commit will be made
110+
* without a signature.
111+
* This field is only used when performing git_rebase_commit.
112+
*/
113+
git_rebase_commit_signature_cb signature_cb;
114+
115+
/**
116+
* If provided and the signature_cb is provided, this will be called asking
117+
* for the field to write the signature to. Can be skipped with GIT_PASSTHROUGH.
118+
* This field is only used when performing git_rebase_commit.
119+
*/
120+
git_rebase_commit_signature_field_cb signature_field_cb;
121+
122+
/**
123+
* This will be passed to each of the callbacks in this struct
124+
* as the last parameter.
125+
*/
126+
void *payload;
75127
} git_rebase_options;
76128

77129
/**
@@ -118,7 +170,7 @@ typedef enum {
118170
#define GIT_REBASE_OPTIONS_VERSION 1
119171
#define GIT_REBASE_OPTIONS_INIT \
120172
{ GIT_REBASE_OPTIONS_VERSION, 0, 0, NULL, GIT_MERGE_OPTIONS_INIT, \
121-
GIT_CHECKOUT_OPTIONS_INIT}
173+
GIT_CHECKOUT_OPTIONS_INIT, 0, 0, NULL }
122174

123175
/** Indicates that a rebase operation is not (yet) in progress. */
124176
#define GIT_REBASE_NO_OPERATION SIZE_MAX

src/rebase.c

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,8 @@ static int rebase_commit__create(
945945
git_commit *current_commit = NULL, *commit = NULL;
946946
git_tree *parent_tree = NULL, *tree = NULL;
947947
git_oid tree_id, commit_id;
948+
git_buf commit_content = GIT_BUF_INIT;
949+
char *signature = NULL, *signature_field = NULL;
948950
int error;
949951

950952
operation = git_array_get(rebase->operations, rebase->current);
@@ -975,10 +977,40 @@ static int rebase_commit__create(
975977
message = git_commit_message(current_commit);
976978
}
977979

978-
if ((error = git_commit_create(&commit_id, rebase->repo, NULL, author,
979-
committer, message_encoding, message, tree, 1,
980-
(const git_commit **)&parent_commit)) < 0 ||
981-
(error = git_commit_lookup(&commit, rebase->repo, &commit_id)) < 0)
980+
/* this error will be cleared by the signing process, but should be set
981+
* to signal the unsigned commit create process if we are not going to sign */
982+
error = GIT_PASSTHROUGH;
983+
if (rebase->options.signature_cb) {
984+
if ((error = git_commit_create_buffer(&commit_content, rebase->repo, author, committer,
985+
message_encoding, message, tree, 1, (const git_commit **)&parent_commit)) < 0)
986+
goto done;
987+
988+
if ((error = rebase->options.signature_cb(&signature, git_buf_cstr(&commit_content),
989+
rebase->options.payload)) < 0 && error != GIT_PASSTHROUGH)
990+
goto done;
991+
992+
if (error != GIT_PASSTHROUGH) {
993+
if (rebase->options.signature_field_cb &&
994+
(error = rebase->options.signature_field_cb(&signature_field, rebase->options.payload)) < 0) {
995+
if (error == GIT_PASSTHROUGH)
996+
assert(signature_field == NULL);
997+
else
998+
goto done;
999+
}
1000+
1001+
if ((error = git_commit_create_with_signature(&commit_id, rebase->repo,
1002+
git_buf_cstr(&commit_content), signature, signature_field)) < 0)
1003+
goto done;
1004+
}
1005+
}
1006+
1007+
/* if we skipped signing, create the commit normally */
1008+
if (error == GIT_PASSTHROUGH &&
1009+
(error = git_commit_create(&commit_id, rebase->repo, NULL, author, committer,
1010+
message_encoding, message, tree, 1, (const git_commit **)&parent_commit)) < 0)
1011+
goto done;
1012+
1013+
if ((error = git_commit_lookup(&commit, rebase->repo, &commit_id)) < 0)
9821014
goto done;
9831015

9841016
*out = commit;
@@ -987,6 +1019,11 @@ static int rebase_commit__create(
9871019
if (error < 0)
9881020
git_commit_free(commit);
9891021

1022+
if (signature)
1023+
free(signature);
1024+
if (signature_field)
1025+
free(signature_field);
1026+
git_buf_dispose(&commit_content);
9901027
git_commit_free(current_commit);
9911028
git_tree_free(parent_tree);
9921029
git_tree_free(tree);

0 commit comments

Comments
 (0)