summaryrefslogtreecommitdiff
path: root/merge-ort.c
diff options
context:
space:
mode:
Diffstat (limited to 'merge-ort.c')
-rw-r--r--merge-ort.c120
1 files changed, 86 insertions, 34 deletions
diff --git a/merge-ort.c b/merge-ort.c
index 47b3d1730e..9e85a5e60a 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -39,7 +39,7 @@
#include "mem-pool.h"
#include "object-file.h"
#include "object-name.h"
-#include "object-store.h"
+#include "odb.h"
#include "oid-array.h"
#include "path.h"
#include "promisor-remote.h"
@@ -316,9 +316,14 @@ struct merge_options_internal {
* (e.g. "drivers/firmware/raspberrypi.c").
* * store all relevant paths in the repo, both directories and
* files (e.g. drivers, drivers/firmware would also be included)
- * * these keys serve to intern all the path strings, which allows
- * us to do pointer comparison on directory names instead of
- * strcmp; we just have to be careful to use the interned strings.
+ * * these keys serve to intern *all* path strings, which allows us
+ * to do pointer comparisons on file & directory names instead of
+ * using strcmp; however, for this pointer-comparison optimization
+ * to work, any code path that independently computes a path needs
+ * to check for it existing in this strmap, and if so, point to
+ * the path in this strmap instead of their computed copy. See
+ * the "reuse known pointer" comment in
+ * apply_directory_rename_modifications() for an example.
*
* The values of paths:
* * either a pointer to a merged_info, or a conflict_info struct
@@ -2163,7 +2168,7 @@ static int handle_content_merge(struct merge_options *opt,
/*
* FIXME: If opt->priv->call_depth && !clean, then we really
* should not make result->mode match either a->mode or
- * b->mode; that causes t6036 "check conflicting mode for
+ * b->mode; that causes t6416 "check conflicting mode for
* regular file" to fail. It would be best to use some other
* mode, but we'll confuse all kinds of stuff if we use one
* where S_ISREG(result->mode) isn't true, and if we use
@@ -2216,8 +2221,8 @@ static int handle_content_merge(struct merge_options *opt,
}
if (!ret && record_object &&
- write_object_file(result_buf.ptr, result_buf.size,
- OBJ_BLOB, &result->oid)) {
+ odb_write_object(the_repository->objects, result_buf.ptr, result_buf.size,
+ OBJ_BLOB, &result->oid)) {
path_msg(opt, ERROR_OBJECT_WRITE_FAILED, 0,
pathnames[0], pathnames[1], pathnames[2], NULL,
_("error: unable to add %s to database"), path);
@@ -2313,14 +2318,20 @@ static char *apply_dir_rename(struct strmap_entry *rename_info,
return strbuf_detach(&new_path, NULL);
}
-static int path_in_way(struct strmap *paths, const char *path, unsigned side_mask)
+static int path_in_way(struct strmap *paths,
+ const char *path,
+ unsigned side_mask,
+ struct diff_filepair *p)
{
struct merged_info *mi = strmap_get(paths, path);
struct conflict_info *ci;
if (!mi)
return 0;
INITIALIZE_CI(ci, mi);
- return mi->clean || (side_mask & (ci->filemask | ci->dirmask));
+ return mi->clean || (side_mask & (ci->filemask | ci->dirmask))
+ /* See testcases 12[npq] of t6423 for this next condition */
+ || ((ci->filemask & 0x01) &&
+ strcmp(p->one->path, path));
}
/*
@@ -2332,6 +2343,7 @@ static int path_in_way(struct strmap *paths, const char *path, unsigned side_mas
static char *handle_path_level_conflicts(struct merge_options *opt,
const char *path,
unsigned side_index,
+ struct diff_filepair *p,
struct strmap_entry *rename_info,
struct strmap *collisions)
{
@@ -2366,7 +2378,7 @@ static char *handle_path_level_conflicts(struct merge_options *opt,
*/
if (c_info->reported_already) {
clean = 0;
- } else if (path_in_way(&opt->priv->paths, new_path, 1 << side_index)) {
+ } else if (path_in_way(&opt->priv->paths, new_path, 1 << side_index, p)) {
c_info->reported_already = 1;
strbuf_add_separated_string_list(&collision_paths, ", ",
&c_info->source_files);
@@ -2520,7 +2532,7 @@ static void compute_collisions(struct strmap *collisions,
* happening, and fall back to no-directory-rename detection
* behavior for those paths.
*
- * See testcases 9e and all of section 5 from t6043 for examples.
+ * See testcases 9e and all of section 5 from t6423 for examples.
*/
for (i = 0; i < pairs->nr; ++i) {
struct strmap_entry *rename_info;
@@ -2573,6 +2585,7 @@ static void free_collisions(struct strmap *collisions)
static char *check_for_directory_rename(struct merge_options *opt,
const char *path,
unsigned side_index,
+ struct diff_filepair *p,
struct strmap *dir_renames,
struct strmap *dir_rename_exclusions,
struct strmap *collisions,
@@ -2580,7 +2593,6 @@ static char *check_for_directory_rename(struct merge_options *opt,
{
char *new_path;
struct strmap_entry *rename_info;
- struct strmap_entry *otherinfo;
const char *new_dir;
int other_side = 3 - side_index;
@@ -2615,14 +2627,13 @@ static char *check_for_directory_rename(struct merge_options *opt,
* to not let Side1 do the rename to dumbdir, since we know that is
* the source of one of our directory renames.
*
- * That's why otherinfo and dir_rename_exclusions is here.
+ * That's why dir_rename_exclusions is here.
*
* As it turns out, this also prevents N-way transient rename
- * confusion; See testcases 9c and 9d of t6043.
+ * confusion; See testcases 9c and 9d of t6423.
*/
new_dir = rename_info->value; /* old_dir = rename_info->key; */
- otherinfo = strmap_get_entry(dir_rename_exclusions, new_dir);
- if (otherinfo) {
+ if (strmap_contains(dir_rename_exclusions, new_dir)) {
path_msg(opt, INFO_DIR_RENAME_SKIPPED_DUE_TO_RERENAME, 1,
rename_info->key, path, new_dir, NULL,
_("WARNING: Avoiding applying %s -> %s rename "
@@ -2631,7 +2642,7 @@ static char *check_for_directory_rename(struct merge_options *opt,
return NULL;
}
- new_path = handle_path_level_conflicts(opt, path, side_index,
+ new_path = handle_path_level_conflicts(opt, path, side_index, p,
rename_info,
&collisions[side_index]);
*clean_merge &= (new_path != NULL);
@@ -2876,6 +2887,20 @@ static int process_renames(struct merge_options *opt,
}
/*
+ * Directory renames can result in rename-to-self; the code
+ * below assumes we have A->B with different A & B, and tries
+ * to move all entries to path B. If A & B are the same path,
+ * the logic can get confused, so skip further processing when
+ * A & B are already the same path.
+ *
+ * As a reminder, we can avoid strcmp here because all paths
+ * are interned in opt->priv->paths; see the comment above
+ * "paths" in struct merge_options_internal.
+ */
+ if (oldpath == newpath)
+ continue;
+
+ /*
* If pair->one->path isn't in opt->priv->paths, that means
* that either directory rename detection removed that
* path, or a parent directory of oldpath was resolved and
@@ -2888,6 +2913,32 @@ static int process_renames(struct merge_options *opt,
continue;
/*
+ * Rename caching from a previous commit might give us an
+ * irrelevant rename for the current commit.
+ *
+ * Imagine:
+ * foo/A -> bar/A
+ * was a cached rename for the upstream side from the
+ * previous commit (without the directories being renamed),
+ * but the next commit being replayed
+ * * does NOT add or delete files
+ * * does NOT have directory renames
+ * * does NOT modify any files under bar/
+ * * does NOT modify foo/A
+ * * DOES modify other files under foo/ (otherwise the
+ * !oldinfo check above would have already exited for
+ * us)
+ * In such a case, our trivial directory resolution will
+ * have already merged bar/, and our attempt to process
+ * the cached
+ * foo/A -> bar/A
+ * would be counterproductive, and lack the necessary
+ * information anyway. Skip such renames.
+ */
+ if (!newinfo)
+ continue;
+
+ /*
* diff_filepairs have copies of pathnames, thus we have to
* use standard 'strcmp()' (negated) instead of '=='.
*/
@@ -3413,13 +3464,13 @@ static int collect_renames(struct merge_options *opt,
continue;
}
if (opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_NONE &&
- p->status == 'R' && 1) {
+ p->status == 'R') {
possibly_cache_new_pair(renames, p, side_index, NULL);
goto skip_directory_renames;
}
new_path = check_for_directory_rename(opt, p->two->path,
- side_index,
+ side_index, p,
dir_renames_for_side,
rename_exclusions,
collisions,
@@ -3629,7 +3680,7 @@ static int read_oid_strbuf(struct merge_options *opt,
void *buf;
enum object_type type;
unsigned long size;
- buf = repo_read_object_file(the_repository, oid, &type, &size);
+ buf = odb_read_object(the_repository->objects, oid, &type, &size);
if (!buf) {
path_msg(opt, ERROR_OBJECT_READ_FAILED, 0,
path, NULL, NULL, NULL,
@@ -3772,7 +3823,8 @@ static int write_tree(struct object_id *result_oid,
}
/* Write this object file out, and record in result_oid */
- if (write_object_file(buf.buf, buf.len, OBJ_TREE, result_oid))
+ if (odb_write_object(the_repository->objects, buf.buf,
+ buf.len, OBJ_TREE, result_oid))
ret = -1;
strbuf_release(&buf);
return ret;
@@ -4385,8 +4437,8 @@ static void prefetch_for_content_merges(struct merge_options *opt,
if ((ci->filemask & side_mask) &&
S_ISREG(vi->mode) &&
- oid_object_info_extended(opt->repo, &vi->oid, NULL,
- OBJECT_INFO_FOR_PREFETCH))
+ odb_read_object_info_extended(opt->repo->objects, &vi->oid, NULL,
+ OBJECT_INFO_FOR_PREFETCH))
oid_array_append(&to_fetch, &vi->oid);
}
}
@@ -5092,7 +5144,8 @@ static void merge_check_renames_reusable(struct merge_options *opt,
* optimization" comment near that case).
*
* This could be revisited in the future; see the commit message
- * where this comment was added for some possible pointers.
+ * where this comment was added for some possible pointers, or the
+ * later commit where this comment was added.
*/
if (opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_NONE) {
renames->cached_pairs_valid_side = 0; /* neither side valid */
@@ -5353,20 +5406,20 @@ static void merge_recursive_config(struct merge_options *opt, int ui)
{
char *value = NULL;
int renormalize = 0;
- git_config_get_int("merge.verbosity", &opt->verbosity);
- git_config_get_int("diff.renamelimit", &opt->rename_limit);
- git_config_get_int("merge.renamelimit", &opt->rename_limit);
- git_config_get_bool("merge.renormalize", &renormalize);
+ repo_config_get_int(the_repository, "merge.verbosity", &opt->verbosity);
+ repo_config_get_int(the_repository, "diff.renamelimit", &opt->rename_limit);
+ repo_config_get_int(the_repository, "merge.renamelimit", &opt->rename_limit);
+ repo_config_get_bool(the_repository, "merge.renormalize", &renormalize);
opt->renormalize = renormalize;
- if (!git_config_get_string("diff.renames", &value)) {
+ if (!repo_config_get_string(the_repository, "diff.renames", &value)) {
opt->detect_renames = git_config_rename("diff.renames", value);
free(value);
}
- if (!git_config_get_string("merge.renames", &value)) {
+ if (!repo_config_get_string(the_repository, "merge.renames", &value)) {
opt->detect_renames = git_config_rename("merge.renames", value);
free(value);
}
- if (!git_config_get_string("merge.directoryrenames", &value)) {
+ if (!repo_config_get_string(the_repository, "merge.directoryrenames", &value)) {
int boolval = git_parse_maybe_bool(value);
if (0 <= boolval) {
opt->detect_directory_renames = boolval ?
@@ -5379,7 +5432,7 @@ static void merge_recursive_config(struct merge_options *opt, int ui)
free(value);
}
if (ui) {
- if (!git_config_get_string("diff.algorithm", &value)) {
+ if (!repo_config_get_string(the_repository, "diff.algorithm", &value)) {
long diff_algorithm = parse_algorithm_value(value);
if (diff_algorithm < 0)
die(_("unknown value for config '%s': %s"), "diff.algorithm", value);
@@ -5387,7 +5440,7 @@ static void merge_recursive_config(struct merge_options *opt, int ui)
free(value);
}
}
- git_config(git_xmerge_config, NULL);
+ repo_config(the_repository, git_xmerge_config, NULL);
}
static void init_merge_options(struct merge_options *opt,
@@ -5470,7 +5523,6 @@ int parse_merge_opt(struct merge_options *opt, const char *s)
if (value < 0)
return -1;
/* clear out previous settings */
- DIFF_XDL_CLR(opt, NEED_MINIMAL);
opt->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
opt->xdl_opts |= value;
}