stash_apply: provide progress callbacks
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 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
diff --git a/include/git2/stash.h b/include/git2/stash.h
index 1f773dc..526db0b 100644
--- a/include/git2/stash.h
+++ b/include/git2/stash.h
@@ -80,6 +80,40 @@ typedef enum {
GIT_STASH_APPLY_REINSTATE_INDEX = (1 << 0),
} git_stash_apply_flags;
+typedef enum {
+ GIT_STASH_APPLY_PROGRESS_NONE = 0,
+
+ /** Loading the stashed data from the object database. */
+ GIT_STASH_APPLY_PROGRESS_LOADING_STASH,
+
+ /** The stored index is being analyzed. */
+ GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX,
+
+ /** The modified files are being analyzed. */
+ GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED,
+
+ /** The untracked and ignored files are being analyzed. */
+ GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED,
+
+ /** The untracked files are being written to disk. */
+ GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED,
+
+ /** The modified files are being written to disk. */
+ GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED,
+
+ /** The stash was applied successfully. */
+ GIT_STASH_APPLY_PROGRESS_DONE,
+} git_stash_apply_progress_t;
+
+/**
+ * Stash application progress notification function.
+ * Return 0 to continue processing, or a negative value to
+ * abort the stash application.
+ */
+typedef int (*git_stash_apply_progress_cb)(
+ git_stash_apply_progress_t progress,
+ void *payload);
+
/** Stash application options structure.
*
* Initialize with the `GIT_STASH_APPLY_OPTIONS_INIT` macro to set
@@ -95,6 +129,10 @@ typedef struct git_stash_apply_options {
/** Options to use when writing files to the working directory. */
git_checkout_options checkout_options;
+
+ /** Optional callback to notify the consumer of application progress. */
+ git_stash_apply_progress_cb progress_cb;
+ void *progress_payload;
} git_stash_apply_options;
#define GIT_STASH_APPLY_OPTIONS_VERSION 1
diff --git a/src/stash.c b/src/stash.c
index dade06f..71ab7b9 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -701,6 +701,11 @@ int git_stash_apply_init_options(git_stash_apply_options *opts, unsigned int ver
return 0;
}
+#define NOTIFY_PROGRESS(opts, progress_type) \
+ if ((opts).progress_cb && \
+ (error = (opts).progress_cb((progress_type), (opts).progress_payload))) \
+ return (error < 0) ? error : -1;
+
int git_stash_apply(
git_repository *repo,
size_t index,
@@ -725,6 +730,8 @@ int git_stash_apply(
normalize_apply_options(&opts, given_opts);
checkout_strategy = opts.checkout_options.checkout_strategy;
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_LOADING_STASH);
+
/* Retrieve commit corresponding to the given stash */
if ((error = retrieve_stash_commit(&stash_commit, repo, index)) < 0)
goto cleanup;
@@ -739,6 +746,8 @@ int git_stash_apply(
if ((error = git_repository_index(&repo_index, repo)) < 0)
goto cleanup;
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX);
+
/* Restore index if required */
if ((opts.flags & GIT_STASH_APPLY_REINSTATE_INDEX) &&
git_oid_cmp(git_tree_id(stash_parent_tree), git_tree_id(index_tree))) {
@@ -753,19 +762,26 @@ int git_stash_apply(
}
}
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED);
+
/* Restore modified files in workdir */
if ((error = merge_index_and_tree(
&modified_index, repo, stash_parent_tree, repo_index, stash_tree)) < 0)
goto cleanup;
/* If applicable, restore untracked / ignored files in workdir */
- if (untracked_tree &&
- (error = merge_index_and_tree(&untracked_index, repo, NULL, repo_index, untracked_tree)) < 0)
- goto cleanup;
+ if (untracked_tree) {
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED);
+
+ if ((error = merge_index_and_tree(&untracked_index, repo, NULL, repo_index, untracked_tree)) < 0)
+ goto cleanup;
+ }
if (untracked_index) {
opts.checkout_options.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED);
+
if ((error = git_checkout_index(repo, untracked_index, &opts.checkout_options)) < 0)
goto cleanup;
@@ -787,6 +803,8 @@ int git_stash_apply(
*/
opts.checkout_options.baseline_index = repo_index;
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED);
+
if ((error = git_checkout_index(repo, modified_index, &opts.checkout_options)) < 0)
goto cleanup;
@@ -795,6 +813,8 @@ int git_stash_apply(
goto cleanup;
}
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_DONE);
+
cleanup:
git_index_free(untracked_index);
git_index_free(modified_index);
diff --git a/tests/stash/apply.c b/tests/stash/apply.c
index de330e9..213945e 100644
--- a/tests/stash/apply.c
+++ b/tests/stash/apply.c
@@ -286,3 +286,48 @@ void test_stash_apply__executes_notify_cb(void)
cl_assert_equal_b(true, seen_paths.who);
cl_assert_equal_b(true, seen_paths.when);
}
+
+int progress_cb(
+ git_stash_apply_progress_t progress,
+ void *payload)
+{
+ git_stash_apply_progress_t *p = (git_stash_apply_progress_t *)payload;
+
+ cl_assert_equal_i((*p)+1, progress);
+
+ *p = progress;
+
+ return 0;
+}
+
+void test_stash_apply__calls_progress_cb(void)
+{
+ git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
+ git_stash_apply_progress_t progress = GIT_STASH_APPLY_PROGRESS_NONE;
+
+ opts.progress_cb = progress_cb;
+ opts.progress_payload = &progress;
+
+ cl_git_pass(git_stash_apply(repo, 0, &opts));
+ cl_assert_equal_i(progress, GIT_STASH_APPLY_PROGRESS_DONE);
+}
+
+int aborting_progress_cb(
+ git_stash_apply_progress_t progress,
+ void *payload)
+{
+ if (progress == GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED)
+ return -44;
+
+ return 0;
+}
+
+void test_stash_apply__progress_cb_can_abort(void)
+{
+ git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
+ git_stash_apply_progress_t progress = GIT_STASH_APPLY_PROGRESS_NONE;
+
+ opts.progress_cb = aborting_progress_cb;
+
+ cl_git_fail_with(-44, git_stash_apply(repo, 0, &opts));
+}