summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2015-10-15 12:22:10 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2015-10-15 12:22:10 +0200
commit8321596a49ee0a2e09d97538aee0882bd684cb2d (patch)
treeda794f6e75ce68ee4e7e843bff187c85bc57718a
parentd5f7aad810b4232da433b94e3ecf658a75bd145c (diff)
parent21515f228b739a3ecd2237bafbba50e8d219d8dd (diff)
downloadlibgit2-8321596a49ee0a2e09d97538aee0882bd684cb2d.tar.gz
Merge pull request #3444 from ethomson/add_preserves_conflict_mode
Preserve modes from a conflict in `git_index_insert`
-rw-r--r--include/git2/index.h28
-rw-r--r--src/index.c77
-rw-r--r--tests/index/bypath.c88
3 files changed, 171 insertions, 22 deletions
diff --git a/include/git2/index.h b/include/git2/index.h
index 176ba301e..466765be3 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -154,13 +154,27 @@ typedef enum {
GIT_INDEX_ADD_CHECK_PATHSPEC = (1u << 2),
} git_index_add_option_t;
-/**
- * Match any index stage.
- *
- * Some index APIs take a stage to match; pass this value to match
- * any entry matching the path regardless of stage.
- */
-#define GIT_INDEX_STAGE_ANY -1
+typedef enum {
+ /**
+ * Match any index stage.
+ *
+ * Some index APIs take a stage to match; pass this value to match
+ * any entry matching the path regardless of stage.
+ */
+ GIT_INDEX_STAGE_ANY = -1,
+
+ /** A normal staged file in the index. */
+ GIT_INDEX_STAGE_NORMAL = 0,
+
+ /** The ancestor side of a conflict. */
+ GIT_INDEX_STAGE_ANCESTOR = 1,
+
+ /** The "ours" side of a conflict. */
+ GIT_INDEX_STAGE_OURS = 2,
+
+ /** The "theirs" side of a conflict. */
+ GIT_INDEX_STAGE_THEIRS = 3,
+} git_index_stage_t;
/** @name Index File Functions
*
diff --git a/src/index.c b/src/index.c
index 2e8934780..c0be5b90d 100644
--- a/src/index.c
+++ b/src/index.c
@@ -1114,7 +1114,9 @@ static int check_file_directory_collision(git_index *index,
}
static int canonicalize_directory_path(
- git_index *index, git_index_entry *entry)
+ git_index *index,
+ git_index_entry *entry,
+ git_index_entry *existing)
{
const git_index_entry *match, *best = NULL;
char *search, *sep;
@@ -1124,8 +1126,8 @@ static int canonicalize_directory_path(
return 0;
/* item already exists in the index, simply re-use the existing case */
- if ((match = git_index_get_bypath(index, entry->path, 0)) != NULL) {
- memcpy((char *)entry->path, match->path, strlen(entry->path));
+ if (existing) {
+ memcpy((char *)entry->path, existing->path, strlen(existing->path));
return 0;
}
@@ -1190,6 +1192,52 @@ static int index_no_dups(void **old, void *new)
return GIT_EEXISTS;
}
+static void index_existing_and_best(
+ const git_index_entry **existing,
+ size_t *existing_position,
+ const git_index_entry **best,
+ git_index *index,
+ const git_index_entry *entry)
+{
+ const git_index_entry *e;
+ size_t pos;
+ int error;
+
+ error = index_find(&pos,
+ index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false);
+
+ if (error == 0) {
+ *existing = index->entries.contents[pos];
+ *existing_position = pos;
+ *best = index->entries.contents[pos];
+ return;
+ }
+
+ *existing = NULL;
+ *existing_position = 0;
+ *best = NULL;
+
+ if (GIT_IDXENTRY_STAGE(entry) == 0) {
+ for (; pos < index->entries.length; pos++) {
+ int (*strcomp)(const char *a, const char *b) =
+ index->ignore_case ? git__strcasecmp : git__strcmp;
+
+ e = index->entries.contents[pos];
+
+ if (strcomp(entry->path, e->path) != 0)
+ break;
+
+ if (GIT_IDXENTRY_STAGE(e) == GIT_INDEX_STAGE_ANCESTOR) {
+ *best = e;
+ continue;
+ } else {
+ *best = e;
+ break;
+ }
+ }
+ }
+}
+
/* index_insert takes ownership of the new entry - if it can't insert
* it, then it will return an error **and also free the entry**. When
* it replaces an existing entry, it will update the entry_ptr with the
@@ -1208,7 +1256,7 @@ static int index_insert(
{
int error = 0;
size_t path_length, position;
- git_index_entry *existing = NULL, *entry;
+ git_index_entry *existing, *best, *entry;
assert(index && entry_ptr);
@@ -1231,20 +1279,19 @@ static int index_insert(
git_vector_sort(&index->entries);
- /* look if an entry with this path already exists */
- if (!index_find(
- &position, index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false)) {
- existing = index->entries.contents[position];
- /* update filemode to existing values if stat is not trusted */
- if (trust_mode)
- entry->mode = git_index__create_mode(entry->mode);
- else
- entry->mode = index_merge_mode(index, existing, entry->mode);
- }
+ /* look if an entry with this path already exists, either staged, or (if
+ * this entry is a regular staged item) as the "ours" side of a conflict.
+ */
+ index_existing_and_best(&existing, &position, &best, index, entry);
+
+ /* update the file mode */
+ entry->mode = trust_mode ?
+ git_index__create_mode(entry->mode) :
+ index_merge_mode(index, best, entry->mode);
/* canonicalize the directory name */
if (!trust_path)
- error = canonicalize_directory_path(index, entry);
+ error = canonicalize_directory_path(index, entry, best);
/* look for tree / blob name collisions, removing conflicts if requested */
if (!error)
diff --git a/tests/index/bypath.c b/tests/index/bypath.c
index b152b0917..0c10cfe4c 100644
--- a/tests/index/bypath.c
+++ b/tests/index/bypath.c
@@ -240,3 +240,91 @@ void test_index_bypath__add_honors_existing_case_4(void)
cl_assert_equal_s("just_a_dir/a/b/Z/y/X/foo.txt", entry->path);
}
+void test_index_bypath__add_honors_mode(void)
+{
+ const git_index_entry *entry;
+ git_index_entry new_entry;
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+
+ memcpy(&new_entry, entry, sizeof(git_index_entry));
+ new_entry.path = "README.txt";
+ new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE));
+
+ cl_git_pass(git_index_add(g_idx, &new_entry));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_git_rewritefile("submod2/README.txt", "Modified but still executable");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "README.txt"));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+ cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode);
+}
+
+void test_index_bypath__add_honors_conflict_mode(void)
+{
+ const git_index_entry *entry;
+ git_index_entry new_entry;
+ int stage = 0;
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+
+ memcpy(&new_entry, entry, sizeof(git_index_entry));
+ new_entry.path = "README.txt";
+ new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE));
+
+ cl_git_pass(git_index_remove_bypath(g_idx, "README.txt"));
+
+ for (stage = 1; stage <= 3; stage++) {
+ new_entry.flags = stage << GIT_IDXENTRY_STAGESHIFT;
+ cl_git_pass(git_index_add(g_idx, &new_entry));
+ }
+
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_git_rewritefile("submod2/README.txt", "Modified but still executable");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "README.txt"));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+ cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode);
+}
+
+void test_index_bypath__add_honors_conflict_case(void)
+{
+ const git_index_entry *entry;
+ git_index_entry new_entry;
+ int stage = 0;
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+
+ memcpy(&new_entry, entry, sizeof(git_index_entry));
+ new_entry.path = "README.txt";
+ new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE));
+
+ cl_git_pass(git_index_remove_bypath(g_idx, "README.txt"));
+
+ for (stage = 1; stage <= 3; stage++) {
+ new_entry.flags = stage << GIT_IDXENTRY_STAGESHIFT;
+ cl_git_pass(git_index_add(g_idx, &new_entry));
+ }
+
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_git_rewritefile("submod2/README.txt", "Modified but still executable");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "README.txt"));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+ cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode);
+}