Merge pull request #942 from nulltoken/topic/checkout-notify-skipped checkout: add notification callback for skipped files
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 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index 42d4700..ef3badb 100644
--- a/include/git2/checkout.h
+++ b/include/git2/checkout.h
@@ -36,6 +36,21 @@ typedef struct git_checkout_opts {
int file_mode; /* default is 0644 */
int file_open_flags; /* default is O_CREAT | O_TRUNC | O_WRONLY */
+ /* Optional callback to notify the consumer of files that
+ * haven't be checked out because a modified version of them
+ * exist in the working directory.
+ *
+ * When provided, this callback will be invoked when the flag
+ * GIT_CHECKOUT_OVERWRITE_MODIFIED isn't part of the checkout strategy.
+ */
+ int (* skipped_notify_cb)(
+ const char *skipped_file,
+ const git_oid *blob_oid,
+ int file_mode,
+ void *payload);
+
+ void *notify_payload;
+
/* when not NULL, arrays of fnmatch pattern specifying
* which paths should be taken into account
*/
diff --git a/src/checkout.c b/src/checkout.c
index b20bd57..ea5e79a 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -25,7 +25,7 @@
struct checkout_diff_data
{
git_buf *path;
- int workdir_len;
+ size_t workdir_len;
git_checkout_opts *checkout_opts;
git_indexer_stats *stats;
git_repository *owner;
@@ -35,7 +35,7 @@ struct checkout_diff_data
static int buffer_to_file(
git_buf *buffer,
const char *path,
- int dir_mode,
+ mode_t dir_mode,
int file_open_flags,
mode_t file_mode)
{
@@ -56,10 +56,11 @@ static int buffer_to_file(
static int blob_content_to_file(
git_blob *blob,
const char *path,
- unsigned int entry_filemode,
+ mode_t entry_filemode,
git_checkout_opts *opts)
{
- int error, nb_filters = 0, file_mode = opts->file_mode;
+ int error, nb_filters = 0;
+ mode_t file_mode = opts->file_mode;
bool dont_free_filtered = false;
git_buf unfiltered = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
git_vector filters = GIT_VECTOR_INIT;
@@ -127,7 +128,7 @@ static int checkout_blob(
git_repository *repo,
git_oid *blob_oid,
const char *path,
- unsigned int filemode,
+ mode_t filemode,
bool can_symlink,
git_checkout_opts *opts)
{
@@ -154,6 +155,7 @@ static int checkout_diff_fn(
{
struct checkout_diff_data *data;
int error = -1;
+ git_checkout_opts *opts;
data = (struct checkout_diff_data *)cb_data;
@@ -163,9 +165,11 @@ static int checkout_diff_fn(
if (git_buf_joinpath(data->path, git_buf_cstr(data->path), delta->new_file.path) < 0)
return -1;
+ opts = data->checkout_opts;
+
switch (delta->status) {
case GIT_DELTA_UNTRACKED:
- if (!(data->checkout_opts->checkout_strategy & GIT_CHECKOUT_REMOVE_UNTRACKED))
+ if (!(opts->checkout_strategy & GIT_CHECKOUT_REMOVE_UNTRACKED))
return 0;
if (!git__suffixcmp(delta->new_file.path, "/"))
@@ -175,8 +179,20 @@ static int checkout_diff_fn(
break;
case GIT_DELTA_MODIFIED:
- if (!(data->checkout_opts->checkout_strategy & GIT_CHECKOUT_OVERWRITE_MODIFIED))
+ if (!(opts->checkout_strategy & GIT_CHECKOUT_OVERWRITE_MODIFIED)) {
+
+ if ((opts->skipped_notify_cb != NULL)
+ && (opts->skipped_notify_cb(
+ delta->new_file.path,
+ &delta->old_file.oid,
+ delta->old_file.mode,
+ opts->notify_payload))) {
+ giterr_clear();
+ return GIT_EUSER;
+ }
+
return 0;
+ }
if (checkout_blob(
data->owner,
@@ -184,13 +200,13 @@ static int checkout_diff_fn(
git_buf_cstr(data->path),
delta->old_file.mode,
data->can_symlink,
- data->checkout_opts) < 0)
+ opts) < 0)
goto cleanup;
break;
case GIT_DELTA_DELETED:
- if (!(data->checkout_opts->checkout_strategy & GIT_CHECKOUT_CREATE_MISSING))
+ if (!(opts->checkout_strategy & GIT_CHECKOUT_CREATE_MISSING))
return 0;
if (checkout_blob(
@@ -199,7 +215,7 @@ static int checkout_diff_fn(
git_buf_cstr(data->path),
delta->old_file.mode,
data->can_symlink,
- data->checkout_opts) < 0)
+ opts) < 0)
goto cleanup;
break;
@@ -377,4 +393,3 @@ int git_checkout_head(
return error;
}
-
diff --git a/src/diff_output.c b/src/diff_output.c
index 37cceff..58a1a35 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -1354,9 +1354,9 @@ int git_diff_iterator_num_lines_in_hunk(git_diff_iterator *iter)
return error;
if (iter->hunk_curr)
- return iter->hunk_curr->line_count;
+ return (int)iter->hunk_curr->line_count;
if (iter->hunk_head)
- return iter->hunk_head->line_count;
+ return (int)iter->hunk_head->line_count;
return 0;
}
diff --git a/src/transports/http.c b/src/transports/http.c
index 456b85e..d5015f5 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -166,7 +166,7 @@ static int send_request(transport_http *t, const char *service, void *data, ssiz
}
if (WinHttpSendRequest(t->request, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
- data, content_length, content_length, 0) == FALSE) {
+ data, (DWORD)content_length, (DWORD)content_length, 0) == FALSE) {
giterr_set(GITERR_OS, "Failed to send request");
goto on_error;
}
diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c
index 88a8414..396af7c 100644
--- a/src/win32/utf-conv.c
+++ b/src/win32/utf-conv.c
@@ -72,7 +72,7 @@ void git__utf8_to_16(wchar_t *dest, size_t length, const char *src)
void git__utf8_to_16(wchar_t *dest, size_t length, const char *src)
{
- MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, length);
+ MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, (int)length);
}
void git__utf16_to_8(char *out, const wchar_t *input)
diff --git a/tests-clar/checkout/index.c b/tests-clar/checkout/index.c
index d1c59e3..f017a0f 100644
--- a/tests-clar/checkout/index.c
+++ b/tests-clar/checkout/index.c
@@ -287,3 +287,76 @@ void test_checkout_index__options_open_flags(void)
test_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
}
+
+struct notify_data {
+ const char *file;
+ const char *sha;
+};
+
+static int notify_cb(
+ const char *skipped_file,
+ const git_oid *blob_oid,
+ int file_mode,
+ void *payload)
+{
+ struct notify_data *expectations = (struct notify_data *)payload;
+
+ GIT_UNUSED(file_mode);
+
+ cl_assert_equal_s(expectations->file, skipped_file);
+ cl_assert_equal_i(0, git_oid_streq(blob_oid, expectations->sha));
+
+ return 0;
+}
+
+void test_checkout_index__can_notify_of_skipped_files(void)
+{
+ struct notify_data data;
+
+ cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
+
+ /*
+ * $ git ls-tree HEAD
+ * 100644 blob a8233120f6ad708f843d861ce2b7228ec4e3dec6 README
+ * 100644 blob 3697d64be941a53d4ae8f6a271e4e3fa56b022cc branch_file.txt
+ * 100644 blob a71586c1dfe8a71c6cbf6c129f404c5642ff31bd new.txt
+ */
+ data.file = "new.txt";
+ data.sha = "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd";
+
+ g_opts.checkout_strategy = GIT_CHECKOUT_CREATE_MISSING;
+ g_opts.skipped_notify_cb = notify_cb;
+ g_opts.notify_payload = &data;
+
+ cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+}
+
+static int dont_notify_cb(
+ const char *skipped_file,
+ const git_oid *blob_oid,
+ int file_mode,
+ void *payload)
+{
+ GIT_UNUSED(skipped_file);
+ GIT_UNUSED(blob_oid);
+ GIT_UNUSED(file_mode);
+ GIT_UNUSED(payload);
+
+ cl_assert(false);
+
+ return 0;
+}
+
+void test_checkout_index__wont_notify_of_expected_line_ending_changes(void)
+{
+ cl_git_pass(p_unlink("./testrepo/.gitattributes"));
+ set_core_autocrlf_to(true);
+
+ cl_git_mkfile("./testrepo/new.txt", "my new file\r\n");
+
+ g_opts.checkout_strategy = GIT_CHECKOUT_CREATE_MISSING;
+ g_opts.skipped_notify_cb = dont_notify_cb;
+ g_opts.notify_payload = NULL;
+
+ cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+}