Merge pull request #3234 from ethomson/dont_update_index_unnecessarily Dont update index unnecessarily
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
diff --git a/src/diff.c b/src/diff.c
index 12d34a1..c1adcc6 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -664,8 +664,10 @@ int git_diff__oid_for_entry(
updated_entry.mode = mode;
git_oid_cpy(&updated_entry.id, out);
- if (!(error = git_repository_index__weakptr(&idx, diff->repo)))
+ if (!(error = git_repository_index__weakptr(&idx, diff->repo))) {
error = git_index_add(idx, &updated_entry);
+ diff->index_updated = true;
+ }
}
git_buf_free(&full_path);
@@ -1360,7 +1362,7 @@ int git_diff_index_to_workdir(
&b, repo, index, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
);
- if (!error && DIFF_FLAG_IS_SET(*diff, GIT_DIFF_UPDATE_INDEX))
+ if (!error && DIFF_FLAG_IS_SET(*diff, GIT_DIFF_UPDATE_INDEX) && (*diff)->index_updated)
error = git_index_write(index);
return error;
diff --git a/src/diff.h b/src/diff.h
index a202a08..2dfc2c6 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -64,6 +64,7 @@ struct git_diff {
git_iterator_type_t new_src;
uint32_t diffcaps;
git_diff_perfdata perf;
+ bool index_updated;
int (*strcomp)(const char *, const char *);
int (*strncomp)(const char *, const char *, size_t);
diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c
index 13de6a9..34bad48 100644
--- a/tests/diff/workdir.c
+++ b/tests/diff/workdir.c
@@ -1544,19 +1544,21 @@ void test_diff_workdir__with_stale_index(void)
static int touch_file(void *payload, git_buf *path)
{
- int fd;
- char b;
+ struct stat st;
+ struct timeval times[2];
GIT_UNUSED(payload);
if (git_path_isdir(path->ptr))
return 0;
- cl_assert((fd = p_open(path->ptr, O_RDWR)) >= 0);
- cl_assert_equal_i(1, p_read(fd, &b, 1));
- cl_must_pass(p_lseek(fd, 0, SEEK_SET));
- cl_must_pass(p_write(fd, &b, 1));
- cl_must_pass(p_close(fd));
+ cl_must_pass(p_stat(path->ptr, &st));
+
+ times[0].tv_sec = st.st_mtime + 3;
+ times[0].tv_usec = 0;
+ times[1].tv_sec = st.st_mtime + 3;
+ times[1].tv_usec = 0;
+ cl_must_pass(p_utimes(path->ptr, times));
return 0;
}
@@ -1783,3 +1785,63 @@ void test_diff_workdir__to_index_conflicted(void) {
git_index_free(index);
git_tree_free(a);
}
+
+void test_diff_workdir__only_writes_index_when_necessary(void)
+{
+ git_index *index;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
+ git_reference *head;
+ git_object *head_object;
+ git_oid initial, first, second;
+ git_buf path = GIT_BUF_INIT;
+ struct stat st;
+ struct timeval times[2];
+
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_UPDATE_INDEX;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_repository_head(&head, g_repo));
+ cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJ_COMMIT));
+
+ cl_git_pass(git_reset(g_repo, head_object, GIT_RESET_HARD, NULL));
+
+ git_oid_cpy(&initial, git_index_checksum(index));
+
+ /* update the index timestamp to avoid raciness */
+ cl_must_pass(p_stat("status/.git/index", &st));
+
+ times[0].tv_sec = st.st_mtime + 5;
+ times[0].tv_usec = 0;
+ times[1].tv_sec = st.st_mtime + 5;
+ times[1].tv_usec = 0;
+
+ cl_must_pass(p_utimes("status/.git/index", times));
+
+ /* ensure diff doesn't touch the index */
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ git_diff_free(diff);
+
+ git_oid_cpy(&first, git_index_checksum(index));
+ cl_assert(!git_oid_equal(&initial, &first));
+
+ /* touch all the files so stat times are different */
+ cl_git_pass(git_buf_sets(&path, "status"));
+ cl_git_pass(git_path_direach(&path, 0, touch_file, NULL));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ git_diff_free(diff);
+
+ /* ensure the second diff did update the index */
+ git_oid_cpy(&second, git_index_checksum(index));
+ cl_assert(!git_oid_equal(&first, &second));
+
+ git_buf_free(&path);
+ git_object_free(head_object);
+ git_reference_free(head);
+ git_index_free(index);
+}
+