} else if (git_oid_iszero(&nitem->id) && new_is_workdir) {
bool use_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0);
bool use_nanos = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_NANOSECS) != 0);
+ git_index *index;
+ git_iterator_index(&index, info->new_iter);
status = GIT_DELTA_UNMODIFIED;
- /* TODO: add check against index file st_mtime to avoid racy-git */
-
if (S_ISGITLINK(nmode)) {
if ((error = maybe_modified_submodule(&status, &noid, diff, info)) < 0)
return error;
!diff_time_eq(&oitem->ctime, &nitem->ctime, use_nanos)) ||
oitem->ino != nitem->ino ||
oitem->uid != nitem->uid ||
- oitem->gid != nitem->gid)
+ oitem->gid != nitem->gid ||
+ (index && nitem->mtime.seconds >= index->stamp.mtime))
{
status = GIT_DELTA_MODIFIED;
modified_uncertain = true;
return 0;
}
+int git_iterator_index(git_index **out, git_iterator *iter)
+{
+ workdir_iterator *wi = (workdir_iterator *)iter;
+
+ if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
+ *out = NULL;
+
+ *out = wi->index;
+
+ return 0;
+}
+
int git_iterator_advance_over_with_status(
const git_index_entry **entryptr,
git_iterator_status_t *status,
#include "git2/index.h"
#include "vector.h"
#include "buffer.h"
+#include "ignore.h"
typedef struct git_iterator git_iterator;
extern int git_iterator_advance_over_with_status(
const git_index_entry **entry, git_iterator_status_t *status, git_iterator *iter);
+/**
+ * Retrieve the index stored in the iterator.
+ *
+ * Only implemented for the workdir iterator
+ */
+extern int git_iterator_index(git_index **out, git_iterator *iter);
+
#endif
#include "clar_libgit2.h"
+#include "../checkout/checkout_helpers.h"
#include "buffer.h"
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL));
cl_assert_equal_i(1, git_diff_num_deltas(diff));
}
+
+void test_diff_racy__write_index_just_after_file(void)
+{
+ git_index *index;
+ git_diff *diff;
+ git_buf path = GIT_BUF_INIT;
+
+ /* Make sure we do have a timestamp */
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_write(index));
+ /* The timestamp will be one second before we change the file */
+ sleep(1);
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "A"));
+ cl_git_mkfile(path.ptr, "A");
+
+ /*
+ * Put 'A' into the index, the size field will be filled,
+ * because the index' on-disk timestamp does not match the
+ * file's timestamp. Both timestamps will however match after
+ * writing out the index.
+ */
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "A"));
+ cl_git_pass(git_index_write(index));
+
+ /* Change its contents quickly, so we get the same timestamp */
+ cl_git_mkfile(path.ptr, "B");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL));
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+}
/* now if we do it again, we should see fewer OID calculations */
+ /* tick again as the index updating from the previous diff might have reset the timestamp */
+ tick_index(index);
basic_diff_status(&diff, &opts);
cl_git_pass(git_diff_get_perfdata(&perf, diff));
opts.flags &= ~GIT_STATUS_OPT_UPDATE_INDEX;
+ /* tick again as the index updating from the previous diff might have reset the timestamp */
+ tick_index(index);
cl_git_pass(git_status_list_new(&status, repo, &opts));
check_status0(status);
cl_git_pass(git_status_list_get_perfdata(&perf, status));