summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2015-06-20 23:17:42 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2015-06-20 23:17:42 +0200
commite96a97f18e8f961c434e4fa4fc2c7d950480b9e9 (patch)
tree12a051e50a7ceb8a5f1f0de948a46890028432ed
parent9018529b4ed190484aade5971fc1c9f1bcccfc15 (diff)
parent96dd171e34fbb89d5ff5eb320c00db82b9ed3942 (diff)
downloadlibgit2-e96a97f18e8f961c434e4fa4fc2c7d950480b9e9.tar.gz
Merge pull request #3233 from ethomson/status_typechange
Don't propagate workdir's mode to the index during diff's update index
-rw-r--r--src/checkout.c2
-rw-r--r--src/diff.c24
-rw-r--r--src/diff.h2
-rw-r--r--tests/status/worktree.c48
4 files changed, 64 insertions, 12 deletions
diff --git a/src/checkout.c b/src/checkout.c
index fd2b19a95..2893c63de 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -211,7 +211,7 @@ static bool checkout_is_workdir_modified(
if (baseitem->size && wditem->file_size != baseitem->size)
return true;
- if (git_diff__oid_for_entry(&oid, data->diff, wditem, NULL) < 0)
+ if (git_diff__oid_for_entry(&oid, data->diff, wditem, wditem->mode, NULL) < 0)
return false;
/* Allow the checkout if the workdir is not modified *or* if the checkout
diff --git a/src/diff.c b/src/diff.c
index c9d1d8730..d7365ef77 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -570,7 +570,7 @@ int git_diff__oid_for_file(
git_oid *out,
git_diff *diff,
const char *path,
- uint16_t mode,
+ uint16_t mode,
git_off_t size)
{
git_index_entry entry;
@@ -580,13 +580,14 @@ int git_diff__oid_for_file(
entry.file_size = size;
entry.path = (char *)path;
- return git_diff__oid_for_entry(out, diff, &entry, NULL);
+ return git_diff__oid_for_entry(out, diff, &entry, mode, NULL);
}
int git_diff__oid_for_entry(
git_oid *out,
git_diff *diff,
const git_index_entry *src,
+ uint16_t mode,
const git_oid *update_match)
{
int error = 0;
@@ -600,7 +601,7 @@ int git_diff__oid_for_entry(
&full_path, git_repository_workdir(diff->repo), entry.path) < 0)
return -1;
- if (!entry.mode) {
+ if (!mode) {
struct stat st;
diff->perf.stat_calls++;
@@ -616,7 +617,7 @@ int git_diff__oid_for_entry(
}
/* calculate OID for file if possible */
- if (S_ISGITLINK(entry.mode)) {
+ if (S_ISGITLINK(mode)) {
git_submodule *sm;
if (!git_submodule_lookup(&sm, diff->repo, entry.path)) {
@@ -630,7 +631,7 @@ int git_diff__oid_for_entry(
*/
giterr_clear();
}
- } else if (S_ISLNK(entry.mode)) {
+ } else if (S_ISLNK(mode)) {
error = git_odb__hashlink(out, full_path.ptr);
diff->perf.oid_calculations++;
} else if (!git__is_sizet(entry.file_size)) {
@@ -657,11 +658,14 @@ int git_diff__oid_for_entry(
/* update index for entry if requested */
if (!error && update_match && git_oid_equal(out, update_match)) {
git_index *idx;
+ git_index_entry updated_entry;
- if (!(error = git_repository_index__weakptr(&idx, diff->repo))) {
- git_oid_cpy(&entry.id, out);
- error = git_index_add(idx, &entry);
- }
+ memcpy(&updated_entry, &entry, sizeof(git_index_entry));
+ updated_entry.mode = mode;
+ git_oid_cpy(&updated_entry.id, out);
+
+ if (!(error = git_repository_index__weakptr(&idx, diff->repo)))
+ error = git_index_add(idx, &updated_entry);
}
git_buf_free(&full_path);
@@ -856,7 +860,7 @@ static int maybe_modified(
&oitem->id : NULL;
if ((error = git_diff__oid_for_entry(
- &noid, diff, nitem, update_check)) < 0)
+ &noid, diff, nitem, nmode, update_check)) < 0)
return error;
/* if oid matches, then mark unmodified (except submodules, where
diff --git a/src/diff.h b/src/diff.h
index 3305238d0..2a35fd9ac 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -94,7 +94,7 @@ extern int git_diff_delta__format_file_header(
extern int git_diff__oid_for_file(
git_oid *out, git_diff *, const char *, uint16_t, git_off_t);
extern int git_diff__oid_for_entry(
- git_oid *out, git_diff *, const git_index_entry *, const git_oid *update);
+ git_oid *out, git_diff *, const git_index_entry *, uint16_t, const git_oid *update);
extern int git_diff__from_iterators(
git_diff **diff_ptr,
diff --git a/tests/status/worktree.c b/tests/status/worktree.c
index f8d1f7f54..56f98a882 100644
--- a/tests/status/worktree.c
+++ b/tests/status/worktree.c
@@ -1096,3 +1096,51 @@ void test_status_worktree__unreadable_as_untracked(void)
cl_assert_equal_i(0, counts.wrong_sorted_path);
}
+void test_status_worktree__update_index_with_symlink_doesnt_change_mode(void)
+{
+ git_repository *repo = cl_git_sandbox_init("testrepo");
+ git_reference *head;
+ git_object *head_object;
+ git_index *index;
+ const git_index_entry *idx_entry;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts = {0};
+ const char *expected_paths[] = { "README" };
+ const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW};
+
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_UPDATE_INDEX;
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJ_COMMIT));
+
+ cl_git_pass(git_reset(repo, head_object, GIT_RESET_HARD, NULL));
+
+ cl_git_rewritefile("testrepo/README", "This was rewritten.");
+
+ /* this status rewrites the index because we have changed the
+ * contents of a tracked file
+ */
+ counts.expected_entry_count = 1;
+ counts.expected_paths = expected_paths;
+ counts.expected_statuses = expected_statuses;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
+ cl_assert_equal_i(1, counts.entry_count);
+
+ /* now ensure that the status's rewrite of the index did not screw
+ * up the mode of the symlink `link_to_new.txt`, particularly
+ * on platforms that don't support symlinks
+ */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_read(index, true));
+
+ cl_assert(idx_entry = git_index_get_bypath(index, "link_to_new.txt", 0));
+ cl_assert(S_ISLNK(idx_entry->mode));
+
+ git_index_free(index);
+ git_object_free(head_object);
+ git_reference_free(head);
+}
+