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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
diff --git a/include/git2/rebase.h b/include/git2/rebase.h
index f6b2e20..81d194e 100644
--- a/include/git2/rebase.h
+++ b/include/git2/rebase.h
@@ -24,6 +24,36 @@
GIT_BEGIN_DECL
/**
+ * Rebase commit signature callback.
+ *
+ * The callback will be called with the commit content, giving a user an
+ * opportunity to sign the commit content in a rebase.
+ * The signature parameter will be owned by LibGit2 after this callback returns.
+ *
+ * When the callback:
+ * - returns GIT_PASSTHROUGH, no signature will be added to the commit.
+ * - returns < 0, git_rebase_commit will be aborted.
+ * - returns GIT_OK, the signature parameter is expected to be filled.
+ */
+typedef int (*git_rebase_commit_signature_cb)(
+ char **signature, const char *commit_content, void *payload);
+
+/**
+ * Rebase commit signature field callback.
+ *
+ * The callback will be called if a signature_cb was called and successful.
+ * This callback will provide the field that a user is signing in a git_rebase_commit.
+ * The signature_field parameter will be owned by LibGit2 after this callback returns.
+ *
+ * When the callback:
+ * - returns GIT_PASSTHROUGH, signature_field is expected to remain null.
+ * - returns < 0, git_rebase_commit will be aborted.
+ * - returns GIT_OK, the signature_field parameter is expected to be filled.
+ */
+typedef int (*git_rebase_commit_signature_field_cb)(
+ char **signature_field, void *payload);
+
+/**
* Rebase options
*
* Use to tell the rebase machinery how to operate.
@@ -72,6 +102,28 @@ typedef struct {
* `abort` to match git semantics.
*/
git_checkout_options checkout_options;
+
+ /**
+ * If provided, this will be called with the commit content, allowing
+ * a signature to be added to the rebase commit. Can be skipped with
+ * GIT_PASSTHROUGH. If GIT_PASSTHROUGH is returned, a commit will be made
+ * without a signature.
+ * This field is only used when performing git_rebase_commit.
+ */
+ git_rebase_commit_signature_cb signature_cb;
+
+ /**
+ * If provided and the signature_cb is provided, this will be called asking
+ * for the field to write the signature to. Can be skipped with GIT_PASSTHROUGH.
+ * This field is only used when performing git_rebase_commit.
+ */
+ git_rebase_commit_signature_field_cb signature_field_cb;
+
+ /**
+ * This will be passed to each of the callbacks in this struct
+ * as the last parameter.
+ */
+ void *payload;
} git_rebase_options;
/**
@@ -118,7 +170,7 @@ typedef enum {
#define GIT_REBASE_OPTIONS_VERSION 1
#define GIT_REBASE_OPTIONS_INIT \
{ GIT_REBASE_OPTIONS_VERSION, 0, 0, NULL, GIT_MERGE_OPTIONS_INIT, \
- GIT_CHECKOUT_OPTIONS_INIT}
+ GIT_CHECKOUT_OPTIONS_INIT, 0, 0, NULL }
/** Indicates that a rebase operation is not (yet) in progress. */
#define GIT_REBASE_NO_OPERATION SIZE_MAX
diff --git a/src/rebase.c b/src/rebase.c
index 4546036..8ead1cf 100644
--- a/src/rebase.c
+++ b/src/rebase.c
@@ -945,6 +945,8 @@ static int rebase_commit__create(
git_commit *current_commit = NULL, *commit = NULL;
git_tree *parent_tree = NULL, *tree = NULL;
git_oid tree_id, commit_id;
+ git_buf commit_content = GIT_BUF_INIT;
+ char *signature = NULL, *signature_field = NULL;
int error;
operation = git_array_get(rebase->operations, rebase->current);
@@ -975,10 +977,40 @@ static int rebase_commit__create(
message = git_commit_message(current_commit);
}
- if ((error = git_commit_create(&commit_id, rebase->repo, NULL, author,
- committer, message_encoding, message, tree, 1,
- (const git_commit **)&parent_commit)) < 0 ||
- (error = git_commit_lookup(&commit, rebase->repo, &commit_id)) < 0)
+ /* this error will be cleared by the signing process, but should be set
+ * to signal the unsigned commit create process if we are not going to sign */
+ error = GIT_PASSTHROUGH;
+ if (rebase->options.signature_cb) {
+ if ((error = git_commit_create_buffer(&commit_content, rebase->repo, author, committer,
+ message_encoding, message, tree, 1, (const git_commit **)&parent_commit)) < 0)
+ goto done;
+
+ if ((error = rebase->options.signature_cb(&signature, git_buf_cstr(&commit_content),
+ rebase->options.payload)) < 0 && error != GIT_PASSTHROUGH)
+ goto done;
+
+ if (error != GIT_PASSTHROUGH) {
+ if (rebase->options.signature_field_cb &&
+ (error = rebase->options.signature_field_cb(&signature_field, rebase->options.payload)) < 0) {
+ if (error == GIT_PASSTHROUGH)
+ assert(signature_field == NULL);
+ else
+ goto done;
+ }
+
+ if ((error = git_commit_create_with_signature(&commit_id, rebase->repo,
+ git_buf_cstr(&commit_content), signature, signature_field)) < 0)
+ goto done;
+ }
+ }
+
+ /* if we skipped signing, create the commit normally */
+ if (error == GIT_PASSTHROUGH &&
+ (error = git_commit_create(&commit_id, rebase->repo, NULL, author, committer,
+ message_encoding, message, tree, 1, (const git_commit **)&parent_commit)) < 0)
+ goto done;
+
+ if ((error = git_commit_lookup(&commit, rebase->repo, &commit_id)) < 0)
goto done;
*out = commit;
@@ -987,6 +1019,11 @@ done:
if (error < 0)
git_commit_free(commit);
+ if (signature)
+ free(signature);
+ if (signature_field)
+ free(signature_field);
+ git_buf_dispose(&commit_content);
git_commit_free(current_commit);
git_tree_free(parent_tree);
git_tree_free(tree);