2008-08-13 00:45:14 +08:00
|
|
|
/*
|
|
|
|
* Recursive Merge algorithm stolen from git-merge-recursive.py by
|
|
|
|
* Fredrik Kuivinen.
|
|
|
|
* The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
|
|
|
|
*/
|
|
|
|
#include "cache.h"
|
2017-06-15 02:07:36 +08:00
|
|
|
#include "config.h"
|
2014-09-14 15:40:45 +08:00
|
|
|
#include "advice.h"
|
2014-10-01 18:28:42 +08:00
|
|
|
#include "lockfile.h"
|
2008-08-13 00:45:14 +08:00
|
|
|
#include "cache-tree.h"
|
|
|
|
#include "commit.h"
|
|
|
|
#include "blob.h"
|
|
|
|
#include "builtin.h"
|
|
|
|
#include "tree-walk.h"
|
|
|
|
#include "diff.h"
|
|
|
|
#include "diffcore.h"
|
|
|
|
#include "tag.h"
|
|
|
|
#include "unpack-trees.h"
|
|
|
|
#include "string-list.h"
|
|
|
|
#include "xdiff-interface.h"
|
|
|
|
#include "ll-merge.h"
|
|
|
|
#include "attr.h"
|
|
|
|
#include "merge-recursive.h"
|
2008-09-30 02:04:20 +08:00
|
|
|
#include "dir.h"
|
2010-07-07 21:39:13 +08:00
|
|
|
#include "submodule.h"
|
2018-05-16 04:00:28 +08:00
|
|
|
#include "revision.h"
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2017-09-08 00:25:56 +08:00
|
|
|
struct path_hashmap_entry {
|
|
|
|
struct hashmap_entry e;
|
|
|
|
char path[FLEX_ARRAY];
|
|
|
|
};
|
|
|
|
|
|
|
|
static int path_hashmap_cmp(const void *cmp_data,
|
|
|
|
const void *entry,
|
|
|
|
const void *entry_or_key,
|
|
|
|
const void *keydata)
|
|
|
|
{
|
|
|
|
const struct path_hashmap_entry *a = entry;
|
|
|
|
const struct path_hashmap_entry *b = entry_or_key;
|
|
|
|
const char *key = keydata;
|
|
|
|
|
|
|
|
if (ignore_case)
|
|
|
|
return strcasecmp(a->path, key ? key : b->path);
|
|
|
|
else
|
|
|
|
return strcmp(a->path, key ? key : b->path);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int path_hash(const char *path)
|
|
|
|
{
|
|
|
|
return ignore_case ? strihash(path) : strhash(path);
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:05 +08:00
|
|
|
static struct dir_rename_entry *dir_rename_find_entry(struct hashmap *hashmap,
|
|
|
|
char *dir)
|
|
|
|
{
|
|
|
|
struct dir_rename_entry key;
|
|
|
|
|
|
|
|
if (dir == NULL)
|
|
|
|
return NULL;
|
|
|
|
hashmap_entry_init(&key, strhash(dir));
|
|
|
|
key.dir = dir;
|
|
|
|
return hashmap_get(hashmap, &key, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dir_rename_cmp(const void *unused_cmp_data,
|
|
|
|
const void *entry,
|
|
|
|
const void *entry_or_key,
|
|
|
|
const void *unused_keydata)
|
|
|
|
{
|
|
|
|
const struct dir_rename_entry *e1 = entry;
|
|
|
|
const struct dir_rename_entry *e2 = entry_or_key;
|
|
|
|
|
|
|
|
return strcmp(e1->dir, e2->dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dir_rename_init(struct hashmap *map)
|
|
|
|
{
|
|
|
|
hashmap_init(map, dir_rename_cmp, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dir_rename_entry_init(struct dir_rename_entry *entry,
|
|
|
|
char *directory)
|
|
|
|
{
|
|
|
|
hashmap_entry_init(entry, strhash(directory));
|
|
|
|
entry->dir = directory;
|
|
|
|
entry->non_unique_new_dir = 0;
|
|
|
|
strbuf_init(&entry->new_dir, 0);
|
|
|
|
string_list_init(&entry->possible_new_dirs, 0);
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:07 +08:00
|
|
|
static struct collision_entry *collision_find_entry(struct hashmap *hashmap,
|
|
|
|
char *target_file)
|
|
|
|
{
|
|
|
|
struct collision_entry key;
|
|
|
|
|
|
|
|
hashmap_entry_init(&key, strhash(target_file));
|
|
|
|
key.target_file = target_file;
|
|
|
|
return hashmap_get(hashmap, &key, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int collision_cmp(void *unused_cmp_data,
|
|
|
|
const struct collision_entry *e1,
|
|
|
|
const struct collision_entry *e2,
|
|
|
|
const void *unused_keydata)
|
|
|
|
{
|
|
|
|
return strcmp(e1->target_file, e2->target_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void collision_init(struct hashmap *map)
|
|
|
|
{
|
|
|
|
hashmap_init(map, (hashmap_cmp_fn) collision_cmp, NULL, 0);
|
|
|
|
}
|
|
|
|
|
2016-08-01 19:44:37 +08:00
|
|
|
static void flush_output(struct merge_options *o)
|
|
|
|
{
|
2016-08-01 19:44:50 +08:00
|
|
|
if (o->buffer_output < 2 && o->obuf.len) {
|
2016-08-01 19:44:37 +08:00
|
|
|
fputs(o->obuf.buf, stdout);
|
|
|
|
strbuf_reset(&o->obuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int err(struct merge_options *o, const char *err, ...)
|
|
|
|
{
|
|
|
|
va_list params;
|
|
|
|
|
2016-08-01 19:44:50 +08:00
|
|
|
if (o->buffer_output < 2)
|
|
|
|
flush_output(o);
|
|
|
|
else {
|
|
|
|
strbuf_complete(&o->obuf, '\n');
|
|
|
|
strbuf_addstr(&o->obuf, "error: ");
|
|
|
|
}
|
2016-08-01 19:44:37 +08:00
|
|
|
va_start(params, err);
|
|
|
|
strbuf_vaddf(&o->obuf, err, params);
|
|
|
|
va_end(params);
|
2016-08-01 19:44:50 +08:00
|
|
|
if (o->buffer_output > 1)
|
|
|
|
strbuf_addch(&o->obuf, '\n');
|
|
|
|
else {
|
|
|
|
error("%s", o->obuf.buf);
|
|
|
|
strbuf_reset(&o->obuf);
|
|
|
|
}
|
2016-08-01 19:44:37 +08:00
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-07-01 13:18:57 +08:00
|
|
|
static struct tree *shift_tree_object(struct tree *one, struct tree *two,
|
|
|
|
const char *subtree_shift)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2015-11-10 10:22:28 +08:00
|
|
|
struct object_id shifted;
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2008-07-01 13:18:57 +08:00
|
|
|
if (!*subtree_shift) {
|
2016-04-18 07:10:38 +08:00
|
|
|
shift_tree(&one->object.oid, &two->object.oid, &shifted, 0);
|
2008-07-01 13:18:57 +08:00
|
|
|
} else {
|
2016-04-18 07:10:38 +08:00
|
|
|
shift_tree_by(&one->object.oid, &two->object.oid, &shifted,
|
2008-07-01 13:18:57 +08:00
|
|
|
subtree_shift);
|
|
|
|
}
|
2015-11-10 10:22:28 +08:00
|
|
|
if (!oidcmp(&two->object.oid, &shifted))
|
2008-08-13 00:45:14 +08:00
|
|
|
return two;
|
2017-05-07 06:10:17 +08:00
|
|
|
return lookup_tree(&shifted);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2009-06-19 01:28:43 +08:00
|
|
|
static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2014-06-11 05:39:11 +08:00
|
|
|
struct commit *commit = alloc_commit_node();
|
2011-11-08 05:26:22 +08:00
|
|
|
|
2016-08-13 20:16:04 +08:00
|
|
|
set_merge_remote_desc(commit, comment, (struct object *)commit);
|
2018-04-07 03:09:32 +08:00
|
|
|
commit->maybe_tree = tree;
|
2008-08-13 00:45:14 +08:00
|
|
|
commit->object.parsed = 1;
|
|
|
|
return commit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since we use get_tree_entry(), which does not put the read object into
|
|
|
|
* the object pool, we cannot rely on a == b.
|
|
|
|
*/
|
2016-06-25 07:09:27 +08:00
|
|
|
static int oid_eq(const struct object_id *a, const struct object_id *b)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
|
|
|
if (!a && !b)
|
|
|
|
return 2;
|
2016-06-25 07:09:27 +08:00
|
|
|
return a && b && oidcmp(a, b) == 0;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2010-09-20 16:28:53 +08:00
|
|
|
enum rename_type {
|
|
|
|
RENAME_NORMAL = 0,
|
2018-04-20 01:58:10 +08:00
|
|
|
RENAME_DIR,
|
2010-09-20 16:28:53 +08:00
|
|
|
RENAME_DELETE,
|
2011-08-12 13:20:08 +08:00
|
|
|
RENAME_ONE_FILE_TO_ONE,
|
2011-08-12 13:20:15 +08:00
|
|
|
RENAME_ONE_FILE_TO_TWO,
|
|
|
|
RENAME_TWO_FILES_TO_ONE
|
2010-09-20 16:28:53 +08:00
|
|
|
};
|
|
|
|
|
2011-08-12 13:20:08 +08:00
|
|
|
struct rename_conflict_info {
|
2010-09-20 16:28:53 +08:00
|
|
|
enum rename_type rename_type;
|
|
|
|
struct diff_filepair *pair1;
|
|
|
|
struct diff_filepair *pair2;
|
|
|
|
const char *branch1;
|
|
|
|
const char *branch2;
|
|
|
|
struct stage_data *dst_entry1;
|
|
|
|
struct stage_data *dst_entry2;
|
2011-08-12 13:20:16 +08:00
|
|
|
struct diff_filespec ren1_other;
|
|
|
|
struct diff_filespec ren2_other;
|
2010-09-20 16:28:53 +08:00
|
|
|
};
|
|
|
|
|
2008-08-13 00:45:14 +08:00
|
|
|
/*
|
|
|
|
* Since we want to write the index eventually, we cannot reuse the index
|
|
|
|
* for these (temporary) data.
|
|
|
|
*/
|
2011-03-16 15:08:34 +08:00
|
|
|
struct stage_data {
|
|
|
|
struct {
|
2008-08-13 00:45:14 +08:00
|
|
|
unsigned mode;
|
2016-06-25 07:09:25 +08:00
|
|
|
struct object_id oid;
|
2008-08-13 00:45:14 +08:00
|
|
|
} stages[4];
|
2011-08-12 13:20:08 +08:00
|
|
|
struct rename_conflict_info *rename_conflict_info;
|
2008-08-13 00:45:14 +08:00
|
|
|
unsigned processed:1;
|
|
|
|
};
|
|
|
|
|
2011-08-12 13:20:08 +08:00
|
|
|
static inline void setup_rename_conflict_info(enum rename_type rename_type,
|
|
|
|
struct diff_filepair *pair1,
|
|
|
|
struct diff_filepair *pair2,
|
|
|
|
const char *branch1,
|
|
|
|
const char *branch2,
|
|
|
|
struct stage_data *dst_entry1,
|
2011-08-12 13:20:16 +08:00
|
|
|
struct stage_data *dst_entry2,
|
|
|
|
struct merge_options *o,
|
|
|
|
struct stage_data *src_entry1,
|
|
|
|
struct stage_data *src_entry2)
|
2010-09-20 16:28:53 +08:00
|
|
|
{
|
2011-08-12 13:20:08 +08:00
|
|
|
struct rename_conflict_info *ci = xcalloc(1, sizeof(struct rename_conflict_info));
|
2010-09-20 16:28:53 +08:00
|
|
|
ci->rename_type = rename_type;
|
|
|
|
ci->pair1 = pair1;
|
|
|
|
ci->branch1 = branch1;
|
|
|
|
ci->branch2 = branch2;
|
|
|
|
|
|
|
|
ci->dst_entry1 = dst_entry1;
|
2011-08-12 13:20:08 +08:00
|
|
|
dst_entry1->rename_conflict_info = ci;
|
2010-09-20 16:28:53 +08:00
|
|
|
dst_entry1->processed = 0;
|
|
|
|
|
|
|
|
assert(!pair2 == !dst_entry2);
|
|
|
|
if (dst_entry2) {
|
|
|
|
ci->dst_entry2 = dst_entry2;
|
|
|
|
ci->pair2 = pair2;
|
2011-08-12 13:20:08 +08:00
|
|
|
dst_entry2->rename_conflict_info = ci;
|
2010-09-20 16:28:53 +08:00
|
|
|
}
|
2011-08-12 13:20:16 +08:00
|
|
|
|
|
|
|
if (rename_type == RENAME_TWO_FILES_TO_ONE) {
|
|
|
|
/*
|
|
|
|
* For each rename, there could have been
|
|
|
|
* modifications on the side of history where that
|
|
|
|
* file was not renamed.
|
|
|
|
*/
|
|
|
|
int ostage1 = o->branch1 == branch1 ? 3 : 2;
|
|
|
|
int ostage2 = ostage1 ^ 1;
|
|
|
|
|
|
|
|
ci->ren1_other.path = pair1->one->path;
|
2016-06-25 07:09:25 +08:00
|
|
|
oidcpy(&ci->ren1_other.oid, &src_entry1->stages[ostage1].oid);
|
2011-08-12 13:20:16 +08:00
|
|
|
ci->ren1_other.mode = src_entry1->stages[ostage1].mode;
|
|
|
|
|
|
|
|
ci->ren2_other.path = pair2->one->path;
|
2016-06-25 07:09:25 +08:00
|
|
|
oidcpy(&ci->ren2_other.oid, &src_entry2->stages[ostage2].oid);
|
2011-08-12 13:20:16 +08:00
|
|
|
ci->ren2_other.mode = src_entry2->stages[ostage2].mode;
|
2010-09-20 16:28:53 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
static int show(struct merge_options *o, int v)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2008-09-03 05:30:09 +08:00
|
|
|
return (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2009-11-15 05:33:13 +08:00
|
|
|
__attribute__((format (printf, 3, 4)))
|
2008-08-25 22:25:57 +08:00
|
|
|
static void output(struct merge_options *o, int v, const char *fmt, ...)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
if (!show(o, v))
|
2008-08-13 00:45:14 +08:00
|
|
|
return;
|
|
|
|
|
2014-09-07 15:06:42 +08:00
|
|
|
strbuf_addchars(&o->obuf, ' ', o->call_depth * 2);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
va_start(ap, fmt);
|
2011-02-26 13:08:53 +08:00
|
|
|
strbuf_vaddf(&o->obuf, fmt, ap);
|
2008-08-13 00:45:14 +08:00
|
|
|
va_end(ap);
|
|
|
|
|
2014-07-10 16:54:24 +08:00
|
|
|
strbuf_addch(&o->obuf, '\n');
|
2008-08-25 22:25:57 +08:00
|
|
|
if (!o->buffer_output)
|
2008-09-03 08:30:03 +08:00
|
|
|
flush_output(o);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2008-09-03 05:30:09 +08:00
|
|
|
static void output_commit_title(struct merge_options *o, struct commit *commit)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2016-08-01 19:44:45 +08:00
|
|
|
strbuf_addchars(&o->obuf, ' ', o->call_depth * 2);
|
2008-08-13 00:45:14 +08:00
|
|
|
if (commit->util)
|
2016-08-01 19:44:45 +08:00
|
|
|
strbuf_addf(&o->obuf, "virtual %s\n",
|
|
|
|
merge_remote_util(commit)->name);
|
2008-08-13 00:45:14 +08:00
|
|
|
else {
|
strbuf: convert strbuf_add_unique_abbrev to use struct object_id
Convert the declaration and definition of strbuf_add_unique_abbrev to
make it take a pointer to struct object_id. Predeclare the struct in
strbuf.h, as cache.h includes strbuf.h before it declares the struct,
and otherwise the struct declaration would have the wrong scope.
Apply the following semantic patch, along with the standard object_id
transforms, to adjust the callers:
@@
expression E1, E2, E3;
@@
- strbuf_add_unique_abbrev(E1, E2.hash, E3);
+ strbuf_add_unique_abbrev(E1, &E2, E3);
@@
expression E1, E2, E3;
@@
- strbuf_add_unique_abbrev(E1, E2->hash, E3);
+ strbuf_add_unique_abbrev(E1, E2, E3);
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-12 10:27:28 +08:00
|
|
|
strbuf_add_unique_abbrev(&o->obuf, &commit->object.oid,
|
2016-10-08 23:38:47 +08:00
|
|
|
DEFAULT_ABBREV);
|
|
|
|
strbuf_addch(&o->obuf, ' ');
|
2008-08-13 00:45:14 +08:00
|
|
|
if (parse_commit(commit) != 0)
|
2016-09-16 02:31:00 +08:00
|
|
|
strbuf_addstr(&o->obuf, _("(bad commit)\n"));
|
2008-08-13 00:45:14 +08:00
|
|
|
else {
|
2010-07-22 21:18:34 +08:00
|
|
|
const char *title;
|
2014-06-11 05:44:13 +08:00
|
|
|
const char *msg = get_commit_buffer(commit, NULL);
|
2014-06-11 05:41:51 +08:00
|
|
|
int len = find_commit_subject(msg, &title);
|
2010-07-22 21:18:34 +08:00
|
|
|
if (len)
|
2016-08-01 19:44:45 +08:00
|
|
|
strbuf_addf(&o->obuf, "%.*s\n", len, title);
|
2014-06-11 05:41:51 +08:00
|
|
|
unuse_commit_buffer(commit, msg);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
}
|
2016-08-01 19:44:45 +08:00
|
|
|
flush_output(o);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2016-08-01 19:44:37 +08:00
|
|
|
static int add_cacheinfo(struct merge_options *o,
|
|
|
|
unsigned int mode, const struct object_id *oid,
|
2008-08-13 00:45:14 +08:00
|
|
|
const char *path, int stage, int refresh, int options)
|
|
|
|
{
|
|
|
|
struct cache_entry *ce;
|
merge: avoid "safer crlf" during recording of merge results
When merge_recursive() decides what the correct blob object merge
result for a path should be, it uses update_file_flags() helper
function to write it out to a working tree file and then calls
add_cacheinfo(). The add_cacheinfo() function in turn calls
make_cache_entry() to create a new cache entry to replace the
higher-stage entries for the path that represents the conflict.
The make_cache_entry() function calls refresh_cache_entry() to fill
in the cached stat information. To mark a cache entry as
up-to-date, the data is re-read from the file in the working tree,
and goes through convert_to_git() conversion to be compared with the
blob object name the new cache entry records.
It is important to note that this happens while the higher-stage
entries, which are going to be replaced with the new entry, are
still in the index. Unfortunately, the convert_to_git() conversion
has a misguided "safer crlf" mechanism baked in, and looks at the
existing cache entry for the path to decide how to convert the
contents in the working tree file. If our side (i.e. stage#2)
records a text blob with CRLF in it, even when the system is
configured to record LF in blobs and convert them to CRLF upon
checkout (and back to LF upon checkin), the "safer crlf" mechanism
stops us doing so.
This especially poses a problem during a renormalizing merge, where
the merge result for the path is computed by first "normalizing" the
blobs involved in the merge by using convert_to_working_tree()
followed by convert_to_git() with "safer crlf" disabled. The merge
result that is computed correctly and fed to add_cacheinfo() via
update_file_flags() does _not_ match what refresh_cache_entry() sees
by converting the working tree file via convert_to_git().
We can work this around by not refreshing the new cache entry in
make_cache_entry() called by add_cacheinfo(). After add_cacheinfo()
adds the new entry, we can call refresh_cache_entry() on that,
knowing that addition of this new cache entry would have removed the
stale cache entries that had CRLF in stage #2 that were carried over
before the renormalizing merge started and will not interfere with
the correct recording of the result.
The test update was taken from a series by Torsten Bögershausen
that attempted to fix this with a different approach.
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Reviewed-by: Torsten Bögershausen <tboegi@web.de>
2016-07-09 01:59:15 +08:00
|
|
|
int ret;
|
|
|
|
|
2016-07-26 05:13:38 +08:00
|
|
|
ce = make_cache_entry(mode, oid ? oid->hash : null_sha1, path, stage, 0);
|
2008-08-13 00:45:14 +08:00
|
|
|
if (!ce)
|
merge-recursive: improve add_cacheinfo error handling
Four closely related changes all with the purpose of fixing error handling
in this function:
- fix reported function name in add_cacheinfo error messages
- differentiate between the two error messages
- abort early when we hit the error (stop ignoring return code)
- mark a test which was hitting this error as failing until we get the
right fix
In more detail...
In commit 0424138d5715 ("Fix bogus error message from merge-recursive
error path", 2007-04-01), it was noted that the name of the function which
the error message claimed it was reported from did not match the actual
function name. This was changed to something closer to the real function
name, but it still didn't match the actual function name. Fix the
reported name to match.
Second, the two errors in this function had identical messages, preventing
us from knowing which error had been triggered. Add a couple words to the
second error message to differentiate the two.
Next, make sure callers do not ignore the return code so that it will stop
processing further entries (processing further entries could result in
more output which could cause the error to scroll off the screen, or at
least be missed by the user) and make it clear the error is the cause of
the early abort. These errors should never be triggered in production; if
either one is, it represents a bug in the calling path somewhere and is
likely to have resulted in mis-merged content. The combination of
ignoring of the return code and continuing to print other standard
messages after hitting the error resulted in the following bug report from
Junio: "...the command pretends that everything went well and merged
cleanly in that path...[Behaving] in a buggy and unexplainable way is bad
enough, doing so silently is unexcusable." Fix this.
Finally, there was one test in the testsuite that did hit this error path,
but was passing anyway. This would have been easy to miss since it had a
test_must_fail and thus could have failed for the wrong reason, but in a
separate testing step I added an intentional NULL-dereference to the
codepath where these error messages are printed in order to flush out such
cases. I could modify that test to explicitly check for this error and
fail the test if it is hit, but since this test operates in a bit of a
gray area and needed other changes, I went for a different fix. The gray
area this test operates in is the following: If the merge of a certain
file results in the same version of the file that existed in HEAD, but
there are dirty modifications to the file, is that an error with a
"Refusing to overwrite existing file" expected, or a case where the merge
should succeed since we shouldn't have to touch the dirty file anyway?
Recent discussion on the list leaned towards saying it should be a
success. Therefore, change the expected behavior of this test to match.
As a side effect, this makes the failed-due-to-hitting-add_cacheinfo-error
very clear, and we can mark the test as test_expect_failure. A subsequent
commit will implement the necessary changes to get this test to pass
again.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-20 01:58:16 +08:00
|
|
|
return err(o, _("add_cacheinfo failed for path '%s'; merge aborting."), path);
|
merge: avoid "safer crlf" during recording of merge results
When merge_recursive() decides what the correct blob object merge
result for a path should be, it uses update_file_flags() helper
function to write it out to a working tree file and then calls
add_cacheinfo(). The add_cacheinfo() function in turn calls
make_cache_entry() to create a new cache entry to replace the
higher-stage entries for the path that represents the conflict.
The make_cache_entry() function calls refresh_cache_entry() to fill
in the cached stat information. To mark a cache entry as
up-to-date, the data is re-read from the file in the working tree,
and goes through convert_to_git() conversion to be compared with the
blob object name the new cache entry records.
It is important to note that this happens while the higher-stage
entries, which are going to be replaced with the new entry, are
still in the index. Unfortunately, the convert_to_git() conversion
has a misguided "safer crlf" mechanism baked in, and looks at the
existing cache entry for the path to decide how to convert the
contents in the working tree file. If our side (i.e. stage#2)
records a text blob with CRLF in it, even when the system is
configured to record LF in blobs and convert them to CRLF upon
checkout (and back to LF upon checkin), the "safer crlf" mechanism
stops us doing so.
This especially poses a problem during a renormalizing merge, where
the merge result for the path is computed by first "normalizing" the
blobs involved in the merge by using convert_to_working_tree()
followed by convert_to_git() with "safer crlf" disabled. The merge
result that is computed correctly and fed to add_cacheinfo() via
update_file_flags() does _not_ match what refresh_cache_entry() sees
by converting the working tree file via convert_to_git().
We can work this around by not refreshing the new cache entry in
make_cache_entry() called by add_cacheinfo(). After add_cacheinfo()
adds the new entry, we can call refresh_cache_entry() on that,
knowing that addition of this new cache entry would have removed the
stale cache entries that had CRLF in stage #2 that were carried over
before the renormalizing merge started and will not interfere with
the correct recording of the result.
The test update was taken from a series by Torsten Bögershausen
that attempted to fix this with a different approach.
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Reviewed-by: Torsten Bögershausen <tboegi@web.de>
2016-07-09 01:59:15 +08:00
|
|
|
|
|
|
|
ret = add_cache_entry(ce, options);
|
|
|
|
if (refresh) {
|
|
|
|
struct cache_entry *nce;
|
|
|
|
|
|
|
|
nce = refresh_cache_entry(ce, CE_MATCH_REFRESH | CE_MATCH_IGNORE_MISSING);
|
2016-11-26 20:48:06 +08:00
|
|
|
if (!nce)
|
merge-recursive: improve add_cacheinfo error handling
Four closely related changes all with the purpose of fixing error handling
in this function:
- fix reported function name in add_cacheinfo error messages
- differentiate between the two error messages
- abort early when we hit the error (stop ignoring return code)
- mark a test which was hitting this error as failing until we get the
right fix
In more detail...
In commit 0424138d5715 ("Fix bogus error message from merge-recursive
error path", 2007-04-01), it was noted that the name of the function which
the error message claimed it was reported from did not match the actual
function name. This was changed to something closer to the real function
name, but it still didn't match the actual function name. Fix the
reported name to match.
Second, the two errors in this function had identical messages, preventing
us from knowing which error had been triggered. Add a couple words to the
second error message to differentiate the two.
Next, make sure callers do not ignore the return code so that it will stop
processing further entries (processing further entries could result in
more output which could cause the error to scroll off the screen, or at
least be missed by the user) and make it clear the error is the cause of
the early abort. These errors should never be triggered in production; if
either one is, it represents a bug in the calling path somewhere and is
likely to have resulted in mis-merged content. The combination of
ignoring of the return code and continuing to print other standard
messages after hitting the error resulted in the following bug report from
Junio: "...the command pretends that everything went well and merged
cleanly in that path...[Behaving] in a buggy and unexplainable way is bad
enough, doing so silently is unexcusable." Fix this.
Finally, there was one test in the testsuite that did hit this error path,
but was passing anyway. This would have been easy to miss since it had a
test_must_fail and thus could have failed for the wrong reason, but in a
separate testing step I added an intentional NULL-dereference to the
codepath where these error messages are printed in order to flush out such
cases. I could modify that test to explicitly check for this error and
fail the test if it is hit, but since this test operates in a bit of a
gray area and needed other changes, I went for a different fix. The gray
area this test operates in is the following: If the merge of a certain
file results in the same version of the file that existed in HEAD, but
there are dirty modifications to the file, is that an error with a
"Refusing to overwrite existing file" expected, or a case where the merge
should succeed since we shouldn't have to touch the dirty file anyway?
Recent discussion on the list leaned towards saying it should be a
success. Therefore, change the expected behavior of this test to match.
As a side effect, this makes the failed-due-to-hitting-add_cacheinfo-error
very clear, and we can mark the test as test_expect_failure. A subsequent
commit will implement the necessary changes to get this test to pass
again.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-20 01:58:16 +08:00
|
|
|
return err(o, _("add_cacheinfo failed to refresh for path '%s'; merge aborting."), path);
|
merge: avoid "safer crlf" during recording of merge results
When merge_recursive() decides what the correct blob object merge
result for a path should be, it uses update_file_flags() helper
function to write it out to a working tree file and then calls
add_cacheinfo(). The add_cacheinfo() function in turn calls
make_cache_entry() to create a new cache entry to replace the
higher-stage entries for the path that represents the conflict.
The make_cache_entry() function calls refresh_cache_entry() to fill
in the cached stat information. To mark a cache entry as
up-to-date, the data is re-read from the file in the working tree,
and goes through convert_to_git() conversion to be compared with the
blob object name the new cache entry records.
It is important to note that this happens while the higher-stage
entries, which are going to be replaced with the new entry, are
still in the index. Unfortunately, the convert_to_git() conversion
has a misguided "safer crlf" mechanism baked in, and looks at the
existing cache entry for the path to decide how to convert the
contents in the working tree file. If our side (i.e. stage#2)
records a text blob with CRLF in it, even when the system is
configured to record LF in blobs and convert them to CRLF upon
checkout (and back to LF upon checkin), the "safer crlf" mechanism
stops us doing so.
This especially poses a problem during a renormalizing merge, where
the merge result for the path is computed by first "normalizing" the
blobs involved in the merge by using convert_to_working_tree()
followed by convert_to_git() with "safer crlf" disabled. The merge
result that is computed correctly and fed to add_cacheinfo() via
update_file_flags() does _not_ match what refresh_cache_entry() sees
by converting the working tree file via convert_to_git().
We can work this around by not refreshing the new cache entry in
make_cache_entry() called by add_cacheinfo(). After add_cacheinfo()
adds the new entry, we can call refresh_cache_entry() on that,
knowing that addition of this new cache entry would have removed the
stale cache entries that had CRLF in stage #2 that were carried over
before the renormalizing merge started and will not interfere with
the correct recording of the result.
The test update was taken from a series by Torsten Bögershausen
that attempted to fix this with a different approach.
Signed-off-by: Torsten Bögershausen <tboegi@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Reviewed-by: Torsten Bögershausen <tboegi@web.de>
2016-07-09 01:59:15 +08:00
|
|
|
if (nce != ce)
|
|
|
|
ret = add_cache_entry(nce, options);
|
|
|
|
}
|
|
|
|
return ret;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
|
|
|
|
{
|
|
|
|
parse_tree(tree);
|
|
|
|
init_tree_desc(desc, tree->buffer, tree->size);
|
|
|
|
}
|
|
|
|
|
2018-05-20 18:17:35 +08:00
|
|
|
static int unpack_trees_start(struct merge_options *o,
|
|
|
|
struct tree *common,
|
|
|
|
struct tree *head,
|
|
|
|
struct tree *merge)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
struct tree_desc t[3];
|
2018-04-20 01:58:20 +08:00
|
|
|
struct index_state tmp_index = { NULL };
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2018-04-20 01:58:12 +08:00
|
|
|
memset(&o->unpack_opts, 0, sizeof(o->unpack_opts));
|
|
|
|
if (o->call_depth)
|
|
|
|
o->unpack_opts.index_only = 1;
|
2008-08-13 00:45:14 +08:00
|
|
|
else
|
2018-04-20 01:58:12 +08:00
|
|
|
o->unpack_opts.update = 1;
|
|
|
|
o->unpack_opts.merge = 1;
|
|
|
|
o->unpack_opts.head_idx = 2;
|
|
|
|
o->unpack_opts.fn = threeway_merge;
|
|
|
|
o->unpack_opts.src_index = &the_index;
|
2018-04-20 01:58:20 +08:00
|
|
|
o->unpack_opts.dst_index = &tmp_index;
|
2018-05-03 00:01:16 +08:00
|
|
|
o->unpack_opts.aggressive = !merge_detect_rename(o);
|
2018-04-20 01:58:12 +08:00
|
|
|
setup_unpack_trees_porcelain(&o->unpack_opts, "merge");
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
init_tree_desc_from_tree(t+0, common);
|
|
|
|
init_tree_desc_from_tree(t+1, head);
|
|
|
|
init_tree_desc_from_tree(t+2, merge);
|
|
|
|
|
2018-04-20 01:58:12 +08:00
|
|
|
rc = unpack_trees(3, t, &o->unpack_opts);
|
2008-08-13 00:45:14 +08:00
|
|
|
cache_tree_free(&active_cache_tree);
|
2018-04-20 01:58:20 +08:00
|
|
|
|
2018-04-20 01:58:12 +08:00
|
|
|
/*
|
2018-04-20 01:58:20 +08:00
|
|
|
* Update the_index to match the new results, AFTER saving a copy
|
|
|
|
* in o->orig_index. Update src_index to point to the saved copy.
|
|
|
|
* (verify_uptodate() checks src_index, and the original index is
|
|
|
|
* the one that had the necessary modification timestamps.)
|
2018-04-20 01:58:12 +08:00
|
|
|
*/
|
2018-04-20 01:58:20 +08:00
|
|
|
o->orig_index = the_index;
|
|
|
|
the_index = tmp_index;
|
|
|
|
o->unpack_opts.src_index = &o->orig_index;
|
|
|
|
|
2008-08-13 00:45:14 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2018-05-20 18:17:35 +08:00
|
|
|
static void unpack_trees_finish(struct merge_options *o)
|
|
|
|
{
|
|
|
|
discard_index(&o->orig_index);
|
2018-05-21 22:54:28 +08:00
|
|
|
clear_unpack_trees_porcelain(&o->unpack_opts);
|
2018-05-20 18:17:35 +08:00
|
|
|
}
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
struct tree *write_tree_from_memory(struct merge_options *o)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
|
|
|
struct tree *result = NULL;
|
|
|
|
|
|
|
|
if (unmerged_cache()) {
|
|
|
|
int i;
|
2010-01-22 08:38:56 +08:00
|
|
|
fprintf(stderr, "BUG: There are unmerged index entries:\n");
|
2008-08-13 00:45:14 +08:00
|
|
|
for (i = 0; i < active_nr; i++) {
|
Convert "struct cache_entry *" to "const ..." wherever possible
I attempted to make index_state->cache[] a "const struct cache_entry **"
to find out how existing entries in index are modified and where. The
question I have is what do we do if we really need to keep track of on-disk
changes in the index. The result is
- diff-lib.c: setting CE_UPTODATE
- name-hash.c: setting CE_HASHED
- preload-index.c, read-cache.c, unpack-trees.c and
builtin/update-index: obvious
- entry.c: write_entry() may refresh the checked out entry via
fill_stat_cache_info(). This causes "non-const struct cache_entry
*" in builtin/apply.c, builtin/checkout-index.c and
builtin/checkout.c
- builtin/ls-files.c: --with-tree changes stagemask and may set
CE_UPDATE
Of these, write_entry() and its call sites are probably most
interesting because it modifies on-disk info. But this is stat info
and can be retrieved via refresh, at least for porcelain
commands. Other just uses ce_flags for local purposes.
So, keeping track of "dirty" entries is just a matter of setting a
flag in index modification functions exposed by read-cache.c. Except
unpack-trees, the rest of the code base does not do anything funny
behind read-cache's back.
The actual patch is less valueable than the summary above. But if
anyone wants to re-identify the above sites. Applying this patch, then
this:
diff --git a/cache.h b/cache.h
index 430d021..1692891 100644
--- a/cache.h
+++ b/cache.h
@@ -267,7 +267,7 @@ static inline unsigned int canon_mode(unsigned int mode)
#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
struct index_state {
- struct cache_entry **cache;
+ const struct cache_entry **cache;
unsigned int version;
unsigned int cache_nr, cache_alloc, cache_changed;
struct string_list *resolve_undo;
will help quickly identify them without bogus warnings.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-09 23:29:00 +08:00
|
|
|
const struct cache_entry *ce = active_cache[i];
|
2008-08-13 00:45:14 +08:00
|
|
|
if (ce_stage(ce))
|
2011-08-12 13:19:49 +08:00
|
|
|
fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce),
|
2010-01-22 08:38:56 +08:00
|
|
|
(int)ce_namelen(ce), ce->name);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("unmerged index entries in merge-recursive.c");
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!active_cache_tree)
|
|
|
|
active_cache_tree = cache_tree();
|
|
|
|
|
|
|
|
if (!cache_tree_fully_valid(active_cache_tree) &&
|
2016-07-27 00:06:26 +08:00
|
|
|
cache_tree_update(&the_index, 0) < 0) {
|
2016-08-01 19:44:37 +08:00
|
|
|
err(o, _("error building trees"));
|
2016-07-27 00:06:26 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2017-05-07 06:10:17 +08:00
|
|
|
result = lookup_tree(&active_cache_tree->oid);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-03-12 10:27:26 +08:00
|
|
|
static int save_files_dirs(const struct object_id *oid,
|
2014-11-30 17:05:00 +08:00
|
|
|
struct strbuf *base, const char *path,
|
2008-08-13 00:45:14 +08:00
|
|
|
unsigned int mode, int stage, void *context)
|
|
|
|
{
|
2017-09-08 00:25:56 +08:00
|
|
|
struct path_hashmap_entry *entry;
|
2014-11-30 17:05:00 +08:00
|
|
|
int baselen = base->len;
|
2008-09-04 01:08:56 +08:00
|
|
|
struct merge_options *o = context;
|
|
|
|
|
2014-11-30 17:05:00 +08:00
|
|
|
strbuf_addstr(base, path);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2017-09-08 00:25:56 +08:00
|
|
|
FLEX_ALLOC_MEM(entry, path, base->buf, base->len);
|
|
|
|
hashmap_entry_init(entry, path_hash(entry->path));
|
|
|
|
hashmap_add(&o->current_file_dir_set, entry);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2014-11-30 17:05:00 +08:00
|
|
|
strbuf_setlen(base, baselen);
|
2009-01-25 08:52:05 +08:00
|
|
|
return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2017-08-29 04:28:28 +08:00
|
|
|
static void get_files_dirs(struct merge_options *o, struct tree *tree)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2011-03-25 17:34:19 +08:00
|
|
|
struct pathspec match_all;
|
2013-07-14 16:35:59 +08:00
|
|
|
memset(&match_all, 0, sizeof(match_all));
|
2017-08-29 04:28:28 +08:00
|
|
|
read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:09 +08:00
|
|
|
static int get_tree_entry_if_blob(const struct object_id *tree,
|
|
|
|
const char *path,
|
|
|
|
struct object_id *hashy,
|
|
|
|
unsigned int *mode_o)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = get_tree_entry(tree, path, hashy, mode_o);
|
|
|
|
if (S_ISDIR(*mode_o)) {
|
|
|
|
oidcpy(hashy, &null_oid);
|
|
|
|
*mode_o = 0;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-08-13 00:45:14 +08:00
|
|
|
/*
|
|
|
|
* Returns an index_entry instance which doesn't have to correspond to
|
|
|
|
* a real cache entry in Git's index.
|
|
|
|
*/
|
|
|
|
static struct stage_data *insert_stage_data(const char *path,
|
|
|
|
struct tree *o, struct tree *a, struct tree *b,
|
|
|
|
struct string_list *entries)
|
|
|
|
{
|
|
|
|
struct string_list_item *item;
|
|
|
|
struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
|
2018-04-20 01:58:09 +08:00
|
|
|
get_tree_entry_if_blob(&o->object.oid, path,
|
|
|
|
&e->stages[1].oid, &e->stages[1].mode);
|
|
|
|
get_tree_entry_if_blob(&a->object.oid, path,
|
|
|
|
&e->stages[2].oid, &e->stages[2].mode);
|
|
|
|
get_tree_entry_if_blob(&b->object.oid, path,
|
|
|
|
&e->stages[3].oid, &e->stages[3].mode);
|
2010-06-26 07:41:35 +08:00
|
|
|
item = string_list_insert(entries, path);
|
2008-08-13 00:45:14 +08:00
|
|
|
item->util = e;
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a dictionary mapping file names to stage_data objects. The
|
|
|
|
* dictionary contains one entry for every path with a non-zero stage entry.
|
|
|
|
*/
|
|
|
|
static struct string_list *get_unmerged(void)
|
|
|
|
{
|
|
|
|
struct string_list *unmerged = xcalloc(1, sizeof(struct string_list));
|
|
|
|
int i;
|
|
|
|
|
|
|
|
unmerged->strdup_strings = 1;
|
|
|
|
|
|
|
|
for (i = 0; i < active_nr; i++) {
|
|
|
|
struct string_list_item *item;
|
|
|
|
struct stage_data *e;
|
Convert "struct cache_entry *" to "const ..." wherever possible
I attempted to make index_state->cache[] a "const struct cache_entry **"
to find out how existing entries in index are modified and where. The
question I have is what do we do if we really need to keep track of on-disk
changes in the index. The result is
- diff-lib.c: setting CE_UPTODATE
- name-hash.c: setting CE_HASHED
- preload-index.c, read-cache.c, unpack-trees.c and
builtin/update-index: obvious
- entry.c: write_entry() may refresh the checked out entry via
fill_stat_cache_info(). This causes "non-const struct cache_entry
*" in builtin/apply.c, builtin/checkout-index.c and
builtin/checkout.c
- builtin/ls-files.c: --with-tree changes stagemask and may set
CE_UPDATE
Of these, write_entry() and its call sites are probably most
interesting because it modifies on-disk info. But this is stat info
and can be retrieved via refresh, at least for porcelain
commands. Other just uses ce_flags for local purposes.
So, keeping track of "dirty" entries is just a matter of setting a
flag in index modification functions exposed by read-cache.c. Except
unpack-trees, the rest of the code base does not do anything funny
behind read-cache's back.
The actual patch is less valueable than the summary above. But if
anyone wants to re-identify the above sites. Applying this patch, then
this:
diff --git a/cache.h b/cache.h
index 430d021..1692891 100644
--- a/cache.h
+++ b/cache.h
@@ -267,7 +267,7 @@ static inline unsigned int canon_mode(unsigned int mode)
#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
struct index_state {
- struct cache_entry **cache;
+ const struct cache_entry **cache;
unsigned int version;
unsigned int cache_nr, cache_alloc, cache_changed;
struct string_list *resolve_undo;
will help quickly identify them without bogus warnings.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-09 23:29:00 +08:00
|
|
|
const struct cache_entry *ce = active_cache[i];
|
2008-08-13 00:45:14 +08:00
|
|
|
if (!ce_stage(ce))
|
|
|
|
continue;
|
|
|
|
|
2010-06-26 07:41:37 +08:00
|
|
|
item = string_list_lookup(unmerged, ce->name);
|
2008-08-13 00:45:14 +08:00
|
|
|
if (!item) {
|
2010-06-26 07:41:35 +08:00
|
|
|
item = string_list_insert(unmerged, ce->name);
|
2008-08-13 00:45:14 +08:00
|
|
|
item->util = xcalloc(1, sizeof(struct stage_data));
|
|
|
|
}
|
|
|
|
e = item->util;
|
|
|
|
e->stages[ce_stage(ce)].mode = ce->ce_mode;
|
2016-09-06 04:07:52 +08:00
|
|
|
oidcpy(&e->stages[ce_stage(ce)].oid, &ce->oid);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return unmerged;
|
|
|
|
}
|
|
|
|
|
2016-11-24 19:45:36 +08:00
|
|
|
static int string_list_df_name_compare(const char *one, const char *two)
|
2010-09-20 16:29:09 +08:00
|
|
|
{
|
2016-11-24 19:45:36 +08:00
|
|
|
int onelen = strlen(one);
|
|
|
|
int twolen = strlen(two);
|
2011-08-12 13:19:56 +08:00
|
|
|
/*
|
|
|
|
* Here we only care that entries for D/F conflicts are
|
|
|
|
* adjacent, in particular with the file of the D/F conflict
|
|
|
|
* appearing before files below the corresponding directory.
|
|
|
|
* The order of the rest of the list is irrelevant for us.
|
2010-09-20 16:29:09 +08:00
|
|
|
*
|
2011-08-12 13:19:56 +08:00
|
|
|
* To achieve this, we sort with df_name_compare and provide
|
|
|
|
* the mode S_IFDIR so that D/F conflicts will sort correctly.
|
|
|
|
* We use the mode S_IFDIR for everything else for simplicity,
|
|
|
|
* since in other cases any changes in their order due to
|
|
|
|
* sorting cause no problems for us.
|
|
|
|
*/
|
2016-11-24 19:45:36 +08:00
|
|
|
int cmp = df_name_compare(one, onelen, S_IFDIR,
|
|
|
|
two, twolen, S_IFDIR);
|
2011-08-12 13:19:56 +08:00
|
|
|
/*
|
|
|
|
* Now that 'foo' and 'foo/bar' compare equal, we have to make sure
|
|
|
|
* that 'foo' comes before 'foo/bar'.
|
2010-09-20 16:29:09 +08:00
|
|
|
*/
|
2011-08-12 13:19:56 +08:00
|
|
|
if (cmp)
|
|
|
|
return cmp;
|
|
|
|
return onelen - twolen;
|
|
|
|
}
|
|
|
|
|
2011-08-12 13:19:58 +08:00
|
|
|
static void record_df_conflict_files(struct merge_options *o,
|
|
|
|
struct string_list *entries)
|
2010-09-20 16:29:09 +08:00
|
|
|
{
|
2011-08-12 13:19:58 +08:00
|
|
|
/* If there is a D/F conflict and the file for such a conflict
|
2018-06-10 12:16:11 +08:00
|
|
|
* currently exists in the working tree, we want to allow it to be
|
2011-08-12 13:20:07 +08:00
|
|
|
* removed to make room for the corresponding directory if needed.
|
|
|
|
* The files underneath the directories of such D/F conflicts will
|
|
|
|
* be processed before the corresponding file involved in the D/F
|
|
|
|
* conflict. If the D/F directory ends up being removed by the
|
|
|
|
* merge, then we won't have to touch the D/F file. If the D/F
|
|
|
|
* directory needs to be written to the working copy, then the D/F
|
|
|
|
* file will simply be removed (in make_room_for_path()) to make
|
|
|
|
* room for the necessary paths. Note that if both the directory
|
|
|
|
* and the file need to be present, then the D/F file will be
|
|
|
|
* reinstated with a new unique name at the time it is processed.
|
2010-09-20 16:29:09 +08:00
|
|
|
*/
|
2016-08-06 04:42:12 +08:00
|
|
|
struct string_list df_sorted_entries = STRING_LIST_INIT_NODUP;
|
2010-09-20 16:29:09 +08:00
|
|
|
const char *last_file = NULL;
|
2010-10-21 22:34:33 +08:00
|
|
|
int last_len = 0;
|
2010-09-20 16:29:09 +08:00
|
|
|
int i;
|
|
|
|
|
2011-08-12 13:19:54 +08:00
|
|
|
/*
|
|
|
|
* If we're merging merge-bases, we don't want to bother with
|
|
|
|
* any working directory changes.
|
|
|
|
*/
|
|
|
|
if (o->call_depth)
|
|
|
|
return;
|
|
|
|
|
2011-08-12 13:19:56 +08:00
|
|
|
/* Ensure D/F conflicts are adjacent in the entries list. */
|
2010-09-20 16:29:09 +08:00
|
|
|
for (i = 0; i < entries->nr; i++) {
|
2011-08-13 10:23:51 +08:00
|
|
|
struct string_list_item *next = &entries->items[i];
|
|
|
|
string_list_append(&df_sorted_entries, next->string)->util =
|
|
|
|
next->util;
|
|
|
|
}
|
2016-11-24 19:45:36 +08:00
|
|
|
df_sorted_entries.cmp = string_list_df_name_compare;
|
|
|
|
string_list_sort(&df_sorted_entries);
|
2011-08-12 13:19:56 +08:00
|
|
|
|
2011-08-12 13:19:58 +08:00
|
|
|
string_list_clear(&o->df_conflict_file_set, 1);
|
2011-08-13 10:23:51 +08:00
|
|
|
for (i = 0; i < df_sorted_entries.nr; i++) {
|
|
|
|
const char *path = df_sorted_entries.items[i].string;
|
2010-09-20 16:29:09 +08:00
|
|
|
int len = strlen(path);
|
2011-08-13 10:23:51 +08:00
|
|
|
struct stage_data *e = df_sorted_entries.items[i].util;
|
2010-09-20 16:29:09 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if last_file & path correspond to a D/F conflict;
|
|
|
|
* i.e. whether path is last_file+'/'+<something>.
|
2011-08-12 13:19:58 +08:00
|
|
|
* If so, record that it's okay to remove last_file to make
|
|
|
|
* room for path and friends if needed.
|
2010-09-20 16:29:09 +08:00
|
|
|
*/
|
|
|
|
if (last_file &&
|
|
|
|
len > last_len &&
|
|
|
|
memcmp(path, last_file, last_len) == 0 &&
|
|
|
|
path[last_len] == '/') {
|
2011-08-12 13:19:58 +08:00
|
|
|
string_list_insert(&o->df_conflict_file_set, last_file);
|
2010-09-20 16:29:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine whether path could exist as a file in the
|
|
|
|
* working directory as a possible D/F conflict. This
|
|
|
|
* will only occur when it exists in stage 2 as a
|
|
|
|
* file.
|
|
|
|
*/
|
|
|
|
if (S_ISREG(e->stages[2].mode) || S_ISLNK(e->stages[2].mode)) {
|
|
|
|
last_file = path;
|
|
|
|
last_len = len;
|
|
|
|
} else {
|
|
|
|
last_file = NULL;
|
|
|
|
}
|
|
|
|
}
|
2011-08-13 10:23:51 +08:00
|
|
|
string_list_clear(&df_sorted_entries, 0);
|
2010-09-20 16:29:09 +08:00
|
|
|
}
|
|
|
|
|
2011-03-16 15:08:34 +08:00
|
|
|
struct rename {
|
2008-08-13 00:45:14 +08:00
|
|
|
struct diff_filepair *pair;
|
2018-01-06 04:20:01 +08:00
|
|
|
/*
|
|
|
|
* Purpose of src_entry and dst_entry:
|
|
|
|
*
|
|
|
|
* If 'before' is renamed to 'after' then src_entry will contain
|
|
|
|
* the versions of 'before' from the merge_base, HEAD, and MERGE in
|
|
|
|
* stages 1, 2, and 3; dst_entry will contain the respective
|
|
|
|
* versions of 'after' in corresponding locations. Thus, we have a
|
|
|
|
* total of six modes and oids, though some will be null. (Stage 0
|
|
|
|
* is ignored; we're interested in handling conflicts.)
|
|
|
|
*
|
|
|
|
* Since we don't turn on break-rewrites by default, neither
|
|
|
|
* src_entry nor dst_entry can have all three of their stages have
|
|
|
|
* non-null oids, meaning at most four of the six will be non-null.
|
|
|
|
* Also, since this is a rename, both src_entry and dst_entry will
|
|
|
|
* have at least one non-null oid, meaning at least two will be
|
|
|
|
* non-null. Of the six oids, a typical rename will have three be
|
|
|
|
* non-null. Only two implies a rename/delete, and four implies a
|
|
|
|
* rename/add.
|
|
|
|
*/
|
2008-08-13 00:45:14 +08:00
|
|
|
struct stage_data *src_entry;
|
|
|
|
struct stage_data *dst_entry;
|
2018-04-20 01:58:10 +08:00
|
|
|
unsigned add_turned_into_rename:1;
|
2008-08-13 00:45:14 +08:00
|
|
|
unsigned processed:1;
|
|
|
|
};
|
|
|
|
|
2016-08-01 19:44:37 +08:00
|
|
|
static int update_stages(struct merge_options *opt, const char *path,
|
|
|
|
const struct diff_filespec *o,
|
2011-08-12 13:19:52 +08:00
|
|
|
const struct diff_filespec *a,
|
|
|
|
const struct diff_filespec *b)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2011-08-12 13:20:25 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE: It is usually a bad idea to call update_stages on a path
|
|
|
|
* before calling update_file on that same path, since it can
|
|
|
|
* sometimes lead to spurious "refusing to lose untracked file..."
|
|
|
|
* messages from update_file (via make_room_for path via
|
|
|
|
* would_lose_untracked). Instead, reverse the order of the calls
|
|
|
|
* (executing update_file first and then update_stages).
|
|
|
|
*/
|
2011-08-12 13:19:52 +08:00
|
|
|
int clear = 1;
|
|
|
|
int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_SKIP_DFCHECK;
|
2008-08-13 00:45:14 +08:00
|
|
|
if (clear)
|
|
|
|
if (remove_file_from_cache(path))
|
|
|
|
return -1;
|
|
|
|
if (o)
|
2016-08-01 19:44:37 +08:00
|
|
|
if (add_cacheinfo(opt, o->mode, &o->oid, path, 1, 0, options))
|
2008-08-13 00:45:14 +08:00
|
|
|
return -1;
|
|
|
|
if (a)
|
2016-08-01 19:44:37 +08:00
|
|
|
if (add_cacheinfo(opt, a->mode, &a->oid, path, 2, 0, options))
|
2008-08-13 00:45:14 +08:00
|
|
|
return -1;
|
|
|
|
if (b)
|
2016-08-01 19:44:37 +08:00
|
|
|
if (add_cacheinfo(opt, b->mode, &b->oid, path, 3, 0, options))
|
2008-08-13 00:45:14 +08:00
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:10 +08:00
|
|
|
static int update_stages_for_stage_data(struct merge_options *opt,
|
|
|
|
const char *path,
|
|
|
|
const struct stage_data *stage_data)
|
|
|
|
{
|
|
|
|
struct diff_filespec o, a, b;
|
|
|
|
|
|
|
|
o.mode = stage_data->stages[1].mode;
|
|
|
|
oidcpy(&o.oid, &stage_data->stages[1].oid);
|
|
|
|
|
|
|
|
a.mode = stage_data->stages[2].mode;
|
|
|
|
oidcpy(&a.oid, &stage_data->stages[2].oid);
|
|
|
|
|
|
|
|
b.mode = stage_data->stages[3].mode;
|
|
|
|
oidcpy(&b.oid, &stage_data->stages[3].oid);
|
|
|
|
|
|
|
|
return update_stages(opt, path,
|
|
|
|
is_null_oid(&o.oid) ? NULL : &o,
|
|
|
|
is_null_oid(&a.oid) ? NULL : &a,
|
|
|
|
is_null_oid(&b.oid) ? NULL : &b);
|
|
|
|
}
|
|
|
|
|
2011-08-12 13:20:02 +08:00
|
|
|
static void update_entry(struct stage_data *entry,
|
|
|
|
struct diff_filespec *o,
|
|
|
|
struct diff_filespec *a,
|
|
|
|
struct diff_filespec *b)
|
2010-09-20 16:28:54 +08:00
|
|
|
{
|
|
|
|
entry->processed = 0;
|
|
|
|
entry->stages[1].mode = o->mode;
|
|
|
|
entry->stages[2].mode = a->mode;
|
|
|
|
entry->stages[3].mode = b->mode;
|
2016-06-25 07:09:25 +08:00
|
|
|
oidcpy(&entry->stages[1].oid, &o->oid);
|
|
|
|
oidcpy(&entry->stages[2].oid, &a->oid);
|
|
|
|
oidcpy(&entry->stages[3].oid, &b->oid);
|
2010-09-20 16:28:54 +08:00
|
|
|
}
|
|
|
|
|
2008-09-03 05:53:47 +08:00
|
|
|
static int remove_file(struct merge_options *o, int clean,
|
|
|
|
const char *path, int no_wd)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2008-09-03 05:53:47 +08:00
|
|
|
int update_cache = o->call_depth || clean;
|
|
|
|
int update_working_directory = !o->call_depth && !no_wd;
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
if (update_cache) {
|
|
|
|
if (remove_file_from_cache(path))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (update_working_directory) {
|
2014-05-02 08:21:09 +08:00
|
|
|
if (ignore_case) {
|
|
|
|
struct cache_entry *ce;
|
|
|
|
ce = cache_file_exists(path, strlen(path), ignore_case);
|
merge-recursive: ignore_case shouldn't reject intentional removals
In commit ae352c7f3 (merge-recursive.c: fix case-changing merge bug,
2014-05-01), it was observed that removing files could be problematic on
case insensitive file systems, because we could end up removing files
that differed in case only rather than deleting the intended file --
something that happened when files were renamed on one branch in a way
that differed only in case. To avoid that problem, that commit added
logic to avoid removing files other than the one intended, rejecting the
removal if the files differed only in case.
Unfortunately, the logic it used didn't fully implement that condition as
stated above; instead it merely checked that a case-insensitive lookup of
the file that was requested resulted in finding a file in the index at
stage 0, not that the file found in the index actually differed in case.
Alternatively, one could view the implementation as making an implicit
assumption that the file we actually wanted to remove would never appear
in the index with a stage of 0, and thus that if we found a file with our
lookup, that it had to be a different file (but different in case only).
The net result of this implementation is that it can ignore more requests
than it should, leaving a file around in the working copy that should
have been removed. Make sure that the file found in the index actually
differs in case before silently ignoring the request to remove the file.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-11-25 03:59:01 +08:00
|
|
|
if (ce && ce_stage(ce) == 0 && strcmp(path, ce->name))
|
2014-05-02 08:21:09 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2010-03-26 23:25:35 +08:00
|
|
|
if (remove_path(path))
|
2008-08-13 00:45:14 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-06-20 05:30:26 +08:00
|
|
|
/* add a string to a strbuf, but converting "/" to "_" */
|
|
|
|
static void add_flattened_path(struct strbuf *out, const char *s)
|
|
|
|
{
|
|
|
|
size_t i = out->len;
|
|
|
|
strbuf_addstr(out, s);
|
|
|
|
for (; i < out->len; i++)
|
|
|
|
if (out->buf[i] == '/')
|
|
|
|
out->buf[i] = '_';
|
|
|
|
}
|
|
|
|
|
2008-09-04 01:08:56 +08:00
|
|
|
static char *unique_path(struct merge_options *o, const char *path, const char *branch)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2017-09-08 00:25:56 +08:00
|
|
|
struct path_hashmap_entry *entry;
|
2014-06-20 05:30:26 +08:00
|
|
|
struct strbuf newpath = STRBUF_INIT;
|
2008-08-13 00:45:14 +08:00
|
|
|
int suffix = 0;
|
2014-06-20 05:30:26 +08:00
|
|
|
size_t base_len;
|
|
|
|
|
|
|
|
strbuf_addf(&newpath, "%s~", path);
|
|
|
|
add_flattened_path(&newpath, branch);
|
|
|
|
|
|
|
|
base_len = newpath.len;
|
2017-09-08 00:25:56 +08:00
|
|
|
while (hashmap_get_from_hash(&o->current_file_dir_set,
|
|
|
|
path_hash(newpath.buf), newpath.buf) ||
|
2016-04-10 14:13:36 +08:00
|
|
|
(!o->call_depth && file_exists(newpath.buf))) {
|
2014-06-20 05:30:26 +08:00
|
|
|
strbuf_setlen(&newpath, base_len);
|
|
|
|
strbuf_addf(&newpath, "_%d", suffix++);
|
|
|
|
}
|
|
|
|
|
2017-09-08 00:25:56 +08:00
|
|
|
FLEX_ALLOC_MEM(entry, path, newpath.buf, newpath.len);
|
|
|
|
hashmap_entry_init(entry, path_hash(entry->path));
|
|
|
|
hashmap_add(&o->current_file_dir_set, entry);
|
2014-06-20 05:30:26 +08:00
|
|
|
return strbuf_detach(&newpath, NULL);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2016-11-08 02:31:31 +08:00
|
|
|
/**
|
|
|
|
* Check whether a directory in the index is in the way of an incoming
|
|
|
|
* file. Return 1 if so. If check_working_copy is non-zero, also
|
|
|
|
* check the working directory. If empty_ok is non-zero, also return
|
|
|
|
* 0 in the case where the working-tree dir exists but is empty.
|
|
|
|
*/
|
|
|
|
static int dir_in_way(const char *path, int check_working_copy, int empty_ok)
|
2011-08-12 13:19:57 +08:00
|
|
|
{
|
2015-09-25 05:07:43 +08:00
|
|
|
int pos;
|
|
|
|
struct strbuf dirpath = STRBUF_INIT;
|
2011-08-12 13:19:57 +08:00
|
|
|
struct stat st;
|
|
|
|
|
2015-09-25 05:07:43 +08:00
|
|
|
strbuf_addstr(&dirpath, path);
|
|
|
|
strbuf_addch(&dirpath, '/');
|
2011-08-12 13:19:57 +08:00
|
|
|
|
2015-09-25 05:07:43 +08:00
|
|
|
pos = cache_name_pos(dirpath.buf, dirpath.len);
|
2011-08-12 13:19:57 +08:00
|
|
|
|
|
|
|
if (pos < 0)
|
|
|
|
pos = -1 - pos;
|
|
|
|
if (pos < active_nr &&
|
2015-09-25 05:07:43 +08:00
|
|
|
!strncmp(dirpath.buf, active_cache[pos]->name, dirpath.len)) {
|
|
|
|
strbuf_release(&dirpath);
|
2011-08-12 13:19:57 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-09-25 05:07:43 +08:00
|
|
|
strbuf_release(&dirpath);
|
2016-11-08 02:31:31 +08:00
|
|
|
return check_working_copy && !lstat(path, &st) && S_ISDIR(st.st_mode) &&
|
|
|
|
!(empty_ok && is_empty_dir(path));
|
2011-08-12 13:19:57 +08:00
|
|
|
}
|
|
|
|
|
merge-recursive: fix check for skipability of working tree updates
The can-working-tree-updates-be-skipped check has had a long and blemished
history. The update can be skipped iff:
a) The merge is clean
b) The merge matches what was in HEAD (content, mode, pathname)
c) The target path is usable (i.e. not involved in D/F conflict)
Traditionally, we split b into parts:
b1) The merged result matches the content and mode found in HEAD
b2) The merged target path existed in HEAD
Steps a & b1 are easy to check; we have always gotten those right. While
it is easy to overlook step c, this was fixed seven years ago with commit
4ab9a157d069 ("merge_content(): Check whether D/F conflicts are still
present", 2010-09-20). merge-recursive didn't have a readily available
way to directly check step b2, so various approximations were used:
* In commit b2c8c0a76274 ("merge-recursive: When we detect we can skip
an update, actually skip it", 2011-02-28), it was noted that although
the code claimed it was skipping the update, it did not actually skip
the update. The code was made to skip it, but used lstat(path, ...)
as an approximation to path-was-tracked-in-index-before-merge.
* In commit 5b448b853030 ("merge-recursive: When we detect we can skip
an update, actually skip it", 2011-08-11), the problem with using
lstat was noted. It was changed to the approximation
path2 && strcmp(path, path2)
which is also wrong. !path2 || strcmp(path, path2) would have been
better, but would have fallen short with directory renames.
* In c5b761fb2711 ("merge-recursive: ensure we write updates for
directory-renamed file", 2018-02-14), the problem with the previous
approximation was noted and changed to
was_tracked(path)
That looks close to what we were trying to answer, but was_tracked()
as implemented at the time should have been named is_tracked(); it
returned something different than what we were looking for.
* To make matters more complex, fixing was_tracked() isn't sufficient
because the splitting of b into b1 and b2 is wrong. Consider the
following merge with a rename/add conflict:
side A: modify foo, add unrelated bar
side B: rename foo->bar (but don't modify the mode or contents)
In this case, the three-way merge of original foo, A's foo, and B's
bar will result in a desired pathname of bar with the same
mode/contents that A had for foo. Thus, A had the right mode and
contents for the file, and it had the right pathname present (namely,
bar), but the bar that was present was unrelated to the contents, so
the working tree update was not skippable.
Fix this by introducing a new function:
was_tracked_and_matches(o, path, &mfi.oid, mfi.mode)
and use it to directly check for condition b.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-20 01:58:23 +08:00
|
|
|
/*
|
|
|
|
* Returns whether path was tracked in the index before the merge started,
|
|
|
|
* and its oid and mode match the specified values
|
|
|
|
*/
|
|
|
|
static int was_tracked_and_matches(struct merge_options *o, const char *path,
|
|
|
|
const struct object_id *oid, unsigned mode)
|
2008-12-15 18:41:24 +08:00
|
|
|
{
|
merge-recursive: fix check for skipability of working tree updates
The can-working-tree-updates-be-skipped check has had a long and blemished
history. The update can be skipped iff:
a) The merge is clean
b) The merge matches what was in HEAD (content, mode, pathname)
c) The target path is usable (i.e. not involved in D/F conflict)
Traditionally, we split b into parts:
b1) The merged result matches the content and mode found in HEAD
b2) The merged target path existed in HEAD
Steps a & b1 are easy to check; we have always gotten those right. While
it is easy to overlook step c, this was fixed seven years ago with commit
4ab9a157d069 ("merge_content(): Check whether D/F conflicts are still
present", 2010-09-20). merge-recursive didn't have a readily available
way to directly check step b2, so various approximations were used:
* In commit b2c8c0a76274 ("merge-recursive: When we detect we can skip
an update, actually skip it", 2011-02-28), it was noted that although
the code claimed it was skipping the update, it did not actually skip
the update. The code was made to skip it, but used lstat(path, ...)
as an approximation to path-was-tracked-in-index-before-merge.
* In commit 5b448b853030 ("merge-recursive: When we detect we can skip
an update, actually skip it", 2011-08-11), the problem with using
lstat was noted. It was changed to the approximation
path2 && strcmp(path, path2)
which is also wrong. !path2 || strcmp(path, path2) would have been
better, but would have fallen short with directory renames.
* In c5b761fb2711 ("merge-recursive: ensure we write updates for
directory-renamed file", 2018-02-14), the problem with the previous
approximation was noted and changed to
was_tracked(path)
That looks close to what we were trying to answer, but was_tracked()
as implemented at the time should have been named is_tracked(); it
returned something different than what we were looking for.
* To make matters more complex, fixing was_tracked() isn't sufficient
because the splitting of b into b1 and b2 is wrong. Consider the
following merge with a rename/add conflict:
side A: modify foo, add unrelated bar
side B: rename foo->bar (but don't modify the mode or contents)
In this case, the three-way merge of original foo, A's foo, and B's
bar will result in a desired pathname of bar with the same
mode/contents that A had for foo. Thus, A had the right mode and
contents for the file, and it had the right pathname present (namely,
bar), but the bar that was present was unrelated to the contents, so
the working tree update was not skippable.
Fix this by introducing a new function:
was_tracked_and_matches(o, path, &mfi.oid, mfi.mode)
and use it to directly check for condition b.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-20 01:58:23 +08:00
|
|
|
int pos = index_name_pos(&o->orig_index, path, strlen(path));
|
|
|
|
struct cache_entry *ce;
|
|
|
|
|
|
|
|
if (0 > pos)
|
|
|
|
/* we were not tracking this path before the merge */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* See if the file we were tracking before matches */
|
|
|
|
ce = o->orig_index.cache[pos];
|
|
|
|
return (oid_eq(&ce->oid, oid) && ce->ce_mode == mode);
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:20 +08:00
|
|
|
/*
|
|
|
|
* Returns whether path was tracked in the index before the merge started
|
|
|
|
*/
|
|
|
|
static int was_tracked(struct merge_options *o, const char *path)
|
2008-12-15 18:41:24 +08:00
|
|
|
{
|
2018-04-20 01:58:20 +08:00
|
|
|
int pos = index_name_pos(&o->orig_index, path, strlen(path));
|
2008-12-15 18:41:24 +08:00
|
|
|
|
2016-07-27 00:05:57 +08:00
|
|
|
if (0 <= pos)
|
2018-04-20 01:58:20 +08:00
|
|
|
/* we were tracking this path before the merge */
|
2016-07-27 00:05:57 +08:00
|
|
|
return 1;
|
|
|
|
|
2011-08-12 13:19:59 +08:00
|
|
|
return 0;
|
2008-12-15 18:41:24 +08:00
|
|
|
}
|
|
|
|
|
2011-08-12 13:19:59 +08:00
|
|
|
static int would_lose_untracked(const char *path)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2018-04-20 01:58:20 +08:00
|
|
|
/*
|
|
|
|
* This may look like it can be simplified to:
|
|
|
|
* return !was_tracked(o, path) && file_exists(path)
|
|
|
|
* but it can't. This function needs to know whether path was in
|
|
|
|
* the working tree due to EITHER having been tracked in the index
|
|
|
|
* before the merge OR having been put into the working copy and
|
|
|
|
* index by unpack_trees(). Due to that either-or requirement, we
|
|
|
|
* check the current index instead of the original one.
|
|
|
|
*
|
|
|
|
* Note that we do not need to worry about merge-recursive itself
|
|
|
|
* updating the index after unpack_trees() and before calling this
|
|
|
|
* function, because we strictly require all code paths in
|
|
|
|
* merge-recursive to update the working tree first and the index
|
|
|
|
* second. Doing otherwise would break
|
|
|
|
* update_file()/would_lose_untracked(); see every comment in this
|
|
|
|
* file which mentions "update_stages".
|
|
|
|
*/
|
|
|
|
int pos = cache_name_pos(path, strlen(path));
|
|
|
|
|
|
|
|
if (pos < 0)
|
|
|
|
pos = -1 - pos;
|
|
|
|
while (pos < active_nr &&
|
|
|
|
!strcmp(path, active_cache[pos]->name)) {
|
|
|
|
/*
|
|
|
|
* If stage #0, it is definitely tracked.
|
|
|
|
* If it has stage #2 then it was tracked
|
|
|
|
* before this merge started. All other
|
|
|
|
* cases the path was not tracked.
|
|
|
|
*/
|
|
|
|
switch (ce_stage(active_cache[pos])) {
|
|
|
|
case 0:
|
|
|
|
case 2:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
return file_exists(path);
|
2008-12-15 18:41:24 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:12 +08:00
|
|
|
static int was_dirty(struct merge_options *o, const char *path)
|
|
|
|
{
|
|
|
|
struct cache_entry *ce;
|
|
|
|
int dirty = 1;
|
|
|
|
|
2018-04-20 01:58:20 +08:00
|
|
|
if (o->call_depth || !was_tracked(o, path))
|
2018-04-20 01:58:12 +08:00
|
|
|
return !dirty;
|
|
|
|
|
2018-04-20 01:58:21 +08:00
|
|
|
ce = index_file_exists(o->unpack_opts.src_index,
|
|
|
|
path, strlen(path), ignore_case);
|
|
|
|
dirty = verify_uptodate(ce, &o->unpack_opts) != 0;
|
2018-04-20 01:58:12 +08:00
|
|
|
return dirty;
|
2008-12-15 18:41:24 +08:00
|
|
|
}
|
|
|
|
|
2011-08-12 13:20:01 +08:00
|
|
|
static int make_room_for_path(struct merge_options *o, const char *path)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2011-08-12 13:20:01 +08:00
|
|
|
int status, i;
|
2012-07-25 22:53:13 +08:00
|
|
|
const char *msg = _("failed to create path '%s'%s");
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2011-08-12 13:20:01 +08:00
|
|
|
/* Unlink any D/F conflict files that are in the way */
|
|
|
|
for (i = 0; i < o->df_conflict_file_set.nr; i++) {
|
|
|
|
const char *df_path = o->df_conflict_file_set.items[i].string;
|
|
|
|
size_t pathlen = strlen(path);
|
|
|
|
size_t df_pathlen = strlen(df_path);
|
|
|
|
if (df_pathlen < pathlen &&
|
|
|
|
path[df_pathlen] == '/' &&
|
|
|
|
strncmp(path, df_path, df_pathlen) == 0) {
|
|
|
|
output(o, 3,
|
2012-07-25 22:53:13 +08:00
|
|
|
_("Removing %s to make room for subdirectory\n"),
|
2011-08-12 13:20:01 +08:00
|
|
|
df_path);
|
|
|
|
unlink(df_path);
|
|
|
|
unsorted_string_list_delete_item(&o->df_conflict_file_set,
|
|
|
|
i, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure leading directories are created */
|
2008-08-13 00:45:14 +08:00
|
|
|
status = safe_create_leading_directories_const(path);
|
|
|
|
if (status) {
|
2016-07-27 00:06:26 +08:00
|
|
|
if (status == SCLD_EXISTS)
|
2008-08-13 00:45:14 +08:00
|
|
|
/* something else exists */
|
2016-08-01 19:44:37 +08:00
|
|
|
return err(o, msg, path, _(": perhaps a D/F conflict?"));
|
|
|
|
return err(o, msg, path, "");
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2008-12-15 18:41:24 +08:00
|
|
|
/*
|
|
|
|
* Do not unlink a file in the work tree if we are not
|
|
|
|
* tracking it.
|
|
|
|
*/
|
|
|
|
if (would_lose_untracked(path))
|
2016-08-01 19:44:37 +08:00
|
|
|
return err(o, _("refusing to lose untracked file at '%s'"),
|
2008-12-15 18:41:24 +08:00
|
|
|
path);
|
|
|
|
|
2008-08-13 00:45:14 +08:00
|
|
|
/* Successful unlink is good.. */
|
|
|
|
if (!unlink(path))
|
|
|
|
return 0;
|
|
|
|
/* .. and so is no existing file */
|
|
|
|
if (errno == ENOENT)
|
|
|
|
return 0;
|
|
|
|
/* .. but not some other error (who really cares what?) */
|
2016-08-01 19:44:37 +08:00
|
|
|
return err(o, msg, path, _(": perhaps a D/F conflict?"));
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2016-07-27 00:06:21 +08:00
|
|
|
static int update_file_flags(struct merge_options *o,
|
|
|
|
const struct object_id *oid,
|
|
|
|
unsigned mode,
|
|
|
|
const char *path,
|
|
|
|
int update_cache,
|
|
|
|
int update_wd)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2016-07-27 00:06:26 +08:00
|
|
|
int ret = 0;
|
|
|
|
|
2008-09-03 05:53:47 +08:00
|
|
|
if (o->call_depth)
|
2008-08-13 00:45:14 +08:00
|
|
|
update_wd = 0;
|
|
|
|
|
|
|
|
if (update_wd) {
|
|
|
|
enum object_type type;
|
|
|
|
void *buf;
|
|
|
|
unsigned long size;
|
|
|
|
|
2010-07-07 21:39:13 +08:00
|
|
|
if (S_ISGITLINK(mode)) {
|
2009-04-30 02:08:18 +08:00
|
|
|
/*
|
|
|
|
* We may later decide to recursively descend into
|
|
|
|
* the submodule directory and update its index
|
|
|
|
* and/or work tree, but we do not do that now.
|
|
|
|
*/
|
2010-07-07 21:39:13 +08:00
|
|
|
update_wd = 0;
|
2009-04-30 02:08:18 +08:00
|
|
|
goto update_index;
|
2010-07-07 21:39:13 +08:00
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
|
sha1_file: convert read_sha1_file to struct object_id
Convert read_sha1_file to take a pointer to struct object_id and rename
it read_object_file. Do the same for read_sha1_file_extended.
Convert one use in grep.c to use the new function without any other code
change, since the pointer being passed is a void pointer that is already
initialized with a pointer to struct object_id. Update the declaration
and definitions of the modified functions, and apply the following
semantic patch to convert the remaining callers:
@@
expression E1, E2, E3;
@@
- read_sha1_file(E1.hash, E2, E3)
+ read_object_file(&E1, E2, E3)
@@
expression E1, E2, E3;
@@
- read_sha1_file(E1->hash, E2, E3)
+ read_object_file(E1, E2, E3)
@@
expression E1, E2, E3, E4;
@@
- read_sha1_file_extended(E1.hash, E2, E3, E4)
+ read_object_file_extended(&E1, E2, E3, E4)
@@
expression E1, E2, E3, E4;
@@
- read_sha1_file_extended(E1->hash, E2, E3, E4)
+ read_object_file_extended(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-12 10:27:53 +08:00
|
|
|
buf = read_object_file(oid, &type, &size);
|
2008-08-13 00:45:14 +08:00
|
|
|
if (!buf)
|
2016-08-01 19:44:37 +08:00
|
|
|
return err(o, _("cannot read object %s '%s'"), oid_to_hex(oid), path);
|
2016-07-27 00:06:26 +08:00
|
|
|
if (type != OBJ_BLOB) {
|
2016-08-01 19:44:37 +08:00
|
|
|
ret = err(o, _("blob expected for %s '%s'"), oid_to_hex(oid), path);
|
2016-07-27 00:06:26 +08:00
|
|
|
goto free_buf;
|
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
if (S_ISREG(mode)) {
|
2008-10-10 03:12:12 +08:00
|
|
|
struct strbuf strbuf = STRBUF_INIT;
|
2008-08-13 00:45:14 +08:00
|
|
|
if (convert_to_working_tree(path, buf, size, &strbuf)) {
|
|
|
|
free(buf);
|
|
|
|
size = strbuf.len;
|
|
|
|
buf = strbuf_detach(&strbuf, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-12 13:20:01 +08:00
|
|
|
if (make_room_for_path(o, path) < 0) {
|
2008-08-13 00:45:14 +08:00
|
|
|
update_wd = 0;
|
2016-07-27 00:06:21 +08:00
|
|
|
goto free_buf;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {
|
|
|
|
int fd;
|
|
|
|
if (mode & 0100)
|
|
|
|
mode = 0777;
|
|
|
|
else
|
|
|
|
mode = 0666;
|
|
|
|
fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
|
2016-07-27 00:06:26 +08:00
|
|
|
if (fd < 0) {
|
2016-08-01 19:44:37 +08:00
|
|
|
ret = err(o, _("failed to open '%s': %s"),
|
|
|
|
path, strerror(errno));
|
2016-07-27 00:06:26 +08:00
|
|
|
goto free_buf;
|
|
|
|
}
|
2012-08-03 20:16:25 +08:00
|
|
|
write_in_full(fd, buf, size);
|
2008-08-13 00:45:14 +08:00
|
|
|
close(fd);
|
|
|
|
} else if (S_ISLNK(mode)) {
|
|
|
|
char *lnk = xmemdupz(buf, size);
|
|
|
|
safe_create_leading_directories_const(path);
|
|
|
|
unlink(path);
|
2008-12-05 08:39:14 +08:00
|
|
|
if (symlink(lnk, path))
|
2016-08-01 19:44:37 +08:00
|
|
|
ret = err(o, _("failed to symlink '%s': %s"),
|
|
|
|
path, strerror(errno));
|
2008-08-13 00:45:14 +08:00
|
|
|
free(lnk);
|
|
|
|
} else
|
2016-08-01 19:44:37 +08:00
|
|
|
ret = err(o,
|
|
|
|
_("do not know what to do with %06o %s '%s'"),
|
|
|
|
mode, oid_to_hex(oid), path);
|
2016-07-27 00:06:21 +08:00
|
|
|
free_buf:
|
2008-08-13 00:45:14 +08:00
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
update_index:
|
2016-07-27 00:06:26 +08:00
|
|
|
if (!ret && update_cache)
|
merge-recursive: improve add_cacheinfo error handling
Four closely related changes all with the purpose of fixing error handling
in this function:
- fix reported function name in add_cacheinfo error messages
- differentiate between the two error messages
- abort early when we hit the error (stop ignoring return code)
- mark a test which was hitting this error as failing until we get the
right fix
In more detail...
In commit 0424138d5715 ("Fix bogus error message from merge-recursive
error path", 2007-04-01), it was noted that the name of the function which
the error message claimed it was reported from did not match the actual
function name. This was changed to something closer to the real function
name, but it still didn't match the actual function name. Fix the
reported name to match.
Second, the two errors in this function had identical messages, preventing
us from knowing which error had been triggered. Add a couple words to the
second error message to differentiate the two.
Next, make sure callers do not ignore the return code so that it will stop
processing further entries (processing further entries could result in
more output which could cause the error to scroll off the screen, or at
least be missed by the user) and make it clear the error is the cause of
the early abort. These errors should never be triggered in production; if
either one is, it represents a bug in the calling path somewhere and is
likely to have resulted in mis-merged content. The combination of
ignoring of the return code and continuing to print other standard
messages after hitting the error resulted in the following bug report from
Junio: "...the command pretends that everything went well and merged
cleanly in that path...[Behaving] in a buggy and unexplainable way is bad
enough, doing so silently is unexcusable." Fix this.
Finally, there was one test in the testsuite that did hit this error path,
but was passing anyway. This would have been easy to miss since it had a
test_must_fail and thus could have failed for the wrong reason, but in a
separate testing step I added an intentional NULL-dereference to the
codepath where these error messages are printed in order to flush out such
cases. I could modify that test to explicitly check for this error and
fail the test if it is hit, but since this test operates in a bit of a
gray area and needed other changes, I went for a different fix. The gray
area this test operates in is the following: If the merge of a certain
file results in the same version of the file that existed in HEAD, but
there are dirty modifications to the file, is that an error with a
"Refusing to overwrite existing file" expected, or a case where the merge
should succeed since we shouldn't have to touch the dirty file anyway?
Recent discussion on the list leaned towards saying it should be a
success. Therefore, change the expected behavior of this test to match.
As a side effect, this makes the failed-due-to-hitting-add_cacheinfo-error
very clear, and we can mark the test as test_expect_failure. A subsequent
commit will implement the necessary changes to get this test to pass
again.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-20 01:58:16 +08:00
|
|
|
if (add_cacheinfo(o, mode, oid, path, 0, update_wd,
|
|
|
|
ADD_CACHE_OK_TO_ADD))
|
|
|
|
return -1;
|
2016-07-27 00:06:26 +08:00
|
|
|
return ret;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2016-07-27 00:06:21 +08:00
|
|
|
static int update_file(struct merge_options *o,
|
|
|
|
int clean,
|
|
|
|
const struct object_id *oid,
|
|
|
|
unsigned mode,
|
|
|
|
const char *path)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2016-07-27 00:06:21 +08:00
|
|
|
return update_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Low level file merging, update and removal */
|
|
|
|
|
2011-03-16 15:08:34 +08:00
|
|
|
struct merge_file_info {
|
2016-06-25 07:09:26 +08:00
|
|
|
struct object_id oid;
|
2008-08-13 00:45:14 +08:00
|
|
|
unsigned mode;
|
|
|
|
unsigned clean:1,
|
|
|
|
merge:1;
|
|
|
|
};
|
|
|
|
|
2008-09-03 05:53:47 +08:00
|
|
|
static int merge_3way(struct merge_options *o,
|
|
|
|
mmbuffer_t *result_buf,
|
2011-08-12 13:19:51 +08:00
|
|
|
const struct diff_filespec *one,
|
|
|
|
const struct diff_filespec *a,
|
|
|
|
const struct diff_filespec *b,
|
2008-08-13 00:45:14 +08:00
|
|
|
const char *branch1,
|
|
|
|
const char *branch2)
|
|
|
|
{
|
|
|
|
mmfile_t orig, src1, src2;
|
2010-08-26 13:49:53 +08:00
|
|
|
struct ll_merge_options ll_opts = {0};
|
2010-03-21 08:41:38 +08:00
|
|
|
char *base_name, *name1, *name2;
|
2008-08-13 00:45:14 +08:00
|
|
|
int merge_status;
|
2009-11-26 10:23:55 +08:00
|
|
|
|
2010-08-26 13:49:53 +08:00
|
|
|
ll_opts.renormalize = o->renormalize;
|
2010-08-26 13:50:45 +08:00
|
|
|
ll_opts.xdl_opts = o->xdl_opts;
|
2010-08-26 13:49:53 +08:00
|
|
|
|
|
|
|
if (o->call_depth) {
|
|
|
|
ll_opts.virtual_ancestor = 1;
|
|
|
|
ll_opts.variant = 0;
|
|
|
|
} else {
|
2009-11-26 10:23:55 +08:00
|
|
|
switch (o->recursive_variant) {
|
|
|
|
case MERGE_RECURSIVE_OURS:
|
2010-08-26 13:49:53 +08:00
|
|
|
ll_opts.variant = XDL_MERGE_FAVOR_OURS;
|
2009-11-26 10:23:55 +08:00
|
|
|
break;
|
|
|
|
case MERGE_RECURSIVE_THEIRS:
|
2010-08-26 13:49:53 +08:00
|
|
|
ll_opts.variant = XDL_MERGE_FAVOR_THEIRS;
|
2009-11-26 10:23:55 +08:00
|
|
|
break;
|
|
|
|
default:
|
2010-08-26 13:49:53 +08:00
|
|
|
ll_opts.variant = 0;
|
2009-11-26 10:23:55 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2010-03-21 08:41:38 +08:00
|
|
|
if (strcmp(a->path, b->path) ||
|
|
|
|
(o->ancestor != NULL && strcmp(a->path, one->path) != 0)) {
|
|
|
|
base_name = o->ancestor == NULL ? NULL :
|
2012-09-05 01:31:14 +08:00
|
|
|
mkpathdup("%s:%s", o->ancestor, one->path);
|
|
|
|
name1 = mkpathdup("%s:%s", branch1, a->path);
|
|
|
|
name2 = mkpathdup("%s:%s", branch2, b->path);
|
2009-07-02 04:18:04 +08:00
|
|
|
} else {
|
2010-03-21 08:41:38 +08:00
|
|
|
base_name = o->ancestor == NULL ? NULL :
|
2012-09-05 01:31:14 +08:00
|
|
|
mkpathdup("%s", o->ancestor);
|
|
|
|
name1 = mkpathdup("%s", branch1);
|
|
|
|
name2 = mkpathdup("%s", branch2);
|
2009-07-02 04:18:04 +08:00
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2016-09-06 04:08:02 +08:00
|
|
|
read_mmblob(&orig, &one->oid);
|
|
|
|
read_mmblob(&src1, &a->oid);
|
|
|
|
read_mmblob(&src2, &b->oid);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2010-03-21 08:41:38 +08:00
|
|
|
merge_status = ll_merge(result_buf, a->path, &orig, base_name,
|
2010-08-26 13:49:53 +08:00
|
|
|
&src1, name1, &src2, name2, &ll_opts);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2012-09-05 01:31:14 +08:00
|
|
|
free(base_name);
|
2008-08-13 00:45:14 +08:00
|
|
|
free(name1);
|
|
|
|
free(name2);
|
|
|
|
free(orig.ptr);
|
|
|
|
free(src1.ptr);
|
|
|
|
free(src2.ptr);
|
|
|
|
return merge_status;
|
|
|
|
}
|
|
|
|
|
2018-05-16 04:00:28 +08:00
|
|
|
static int find_first_merges(struct object_array *result, const char *path,
|
|
|
|
struct commit *a, struct commit *b)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
struct object_array merges = OBJECT_ARRAY_INIT;
|
|
|
|
struct commit *commit;
|
|
|
|
int contains_another;
|
|
|
|
|
|
|
|
char merged_revision[42];
|
|
|
|
const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
|
|
|
|
"--all", merged_revision, NULL };
|
|
|
|
struct rev_info revs;
|
|
|
|
struct setup_revision_opt rev_opts;
|
|
|
|
|
|
|
|
memset(result, 0, sizeof(struct object_array));
|
|
|
|
memset(&rev_opts, 0, sizeof(rev_opts));
|
|
|
|
|
|
|
|
/* get all revisions that merge commit a */
|
|
|
|
xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
|
|
|
|
oid_to_hex(&a->object.oid));
|
|
|
|
init_revisions(&revs, NULL);
|
|
|
|
rev_opts.submodule = path;
|
|
|
|
/* FIXME: can't handle linked worktrees in submodules yet */
|
|
|
|
revs.single_worktree = path != NULL;
|
|
|
|
setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
|
|
|
|
|
|
|
|
/* save all revisions from the above list that contain b */
|
|
|
|
if (prepare_revision_walk(&revs))
|
|
|
|
die("revision walk setup failed");
|
|
|
|
while ((commit = get_revision(&revs)) != NULL) {
|
|
|
|
struct object *o = &(commit->object);
|
|
|
|
if (in_merge_bases(b, commit))
|
|
|
|
add_object_array(o, NULL, &merges);
|
|
|
|
}
|
|
|
|
reset_revision_walk();
|
|
|
|
|
|
|
|
/* Now we've got all merges that contain a and b. Prune all
|
|
|
|
* merges that contain another found merge and save them in
|
|
|
|
* result.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < merges.nr; i++) {
|
|
|
|
struct commit *m1 = (struct commit *) merges.objects[i].item;
|
|
|
|
|
|
|
|
contains_another = 0;
|
|
|
|
for (j = 0; j < merges.nr; j++) {
|
|
|
|
struct commit *m2 = (struct commit *) merges.objects[j].item;
|
|
|
|
if (i != j && in_merge_bases(m2, m1)) {
|
|
|
|
contains_another = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!contains_another)
|
|
|
|
add_object_array(merges.objects[i].item, NULL, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
object_array_clear(&merges);
|
|
|
|
return result->nr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_commit(struct commit *commit)
|
|
|
|
{
|
|
|
|
struct strbuf sb = STRBUF_INIT;
|
|
|
|
struct pretty_print_context ctx = {0};
|
|
|
|
ctx.date_mode.type = DATE_NORMAL;
|
|
|
|
format_commit_message(commit, " %h: %m %s", &sb, &ctx);
|
|
|
|
fprintf(stderr, "%s\n", sb.buf);
|
|
|
|
strbuf_release(&sb);
|
|
|
|
}
|
|
|
|
|
2018-05-16 04:00:29 +08:00
|
|
|
static int merge_submodule(struct merge_options *o,
|
|
|
|
struct object_id *result, const char *path,
|
2018-05-16 04:00:28 +08:00
|
|
|
const struct object_id *base, const struct object_id *a,
|
2018-05-16 04:00:29 +08:00
|
|
|
const struct object_id *b)
|
2018-05-16 04:00:28 +08:00
|
|
|
{
|
|
|
|
struct commit *commit_base, *commit_a, *commit_b;
|
|
|
|
int parent_count;
|
|
|
|
struct object_array merges;
|
|
|
|
|
|
|
|
int i;
|
2018-05-16 04:00:29 +08:00
|
|
|
int search = !o->call_depth;
|
2018-05-16 04:00:28 +08:00
|
|
|
|
|
|
|
/* store a in result in case we fail */
|
|
|
|
oidcpy(result, a);
|
|
|
|
|
|
|
|
/* we can not handle deletion conflicts */
|
|
|
|
if (is_null_oid(base))
|
|
|
|
return 0;
|
|
|
|
if (is_null_oid(a))
|
|
|
|
return 0;
|
|
|
|
if (is_null_oid(b))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (add_submodule_odb(path)) {
|
2018-05-16 04:00:29 +08:00
|
|
|
output(o, 1, _("Failed to merge submodule %s (not checked out)"), path);
|
2018-05-16 04:00:28 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(commit_base = lookup_commit_reference(base)) ||
|
|
|
|
!(commit_a = lookup_commit_reference(a)) ||
|
|
|
|
!(commit_b = lookup_commit_reference(b))) {
|
2018-05-16 04:00:29 +08:00
|
|
|
output(o, 1, _("Failed to merge submodule %s (commits not present)"), path);
|
2018-05-16 04:00:28 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check whether both changes are forward */
|
|
|
|
if (!in_merge_bases(commit_base, commit_a) ||
|
|
|
|
!in_merge_bases(commit_base, commit_b)) {
|
2018-05-16 04:00:29 +08:00
|
|
|
output(o, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
|
2018-05-16 04:00:28 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Case #1: a is contained in b or vice versa */
|
|
|
|
if (in_merge_bases(commit_a, commit_b)) {
|
|
|
|
oidcpy(result, b);
|
2018-05-18 02:40:08 +08:00
|
|
|
if (show(o, 3)) {
|
|
|
|
output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
|
|
|
|
output_commit_title(o, commit_b);
|
|
|
|
} else if (show(o, 2))
|
|
|
|
output(o, 2, _("Fast-forwarding submodule %s to %s"), path, oid_to_hex(b));
|
|
|
|
else
|
|
|
|
; /* no output */
|
|
|
|
|
2018-05-16 04:00:28 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (in_merge_bases(commit_b, commit_a)) {
|
|
|
|
oidcpy(result, a);
|
2018-05-18 02:40:08 +08:00
|
|
|
if (show(o, 3)) {
|
|
|
|
output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
|
|
|
|
output_commit_title(o, commit_a);
|
|
|
|
} else if (show(o, 2))
|
|
|
|
output(o, 2, _("Fast-forwarding submodule %s to %s"), path, oid_to_hex(a));
|
|
|
|
else
|
|
|
|
; /* no output */
|
|
|
|
|
2018-05-16 04:00:28 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Case #2: There are one or more merges that contain a and b in
|
|
|
|
* the submodule. If there is only one, then present it as a
|
|
|
|
* suggestion to the user, but leave it marked unmerged so the
|
|
|
|
* user needs to confirm the resolution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Skip the search if makes no sense to the calling context. */
|
|
|
|
if (!search)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* find commit which merges them */
|
|
|
|
parent_count = find_first_merges(&merges, path, commit_a, commit_b);
|
|
|
|
switch (parent_count) {
|
|
|
|
case 0:
|
2018-05-16 04:00:29 +08:00
|
|
|
output(o, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
|
2018-05-16 04:00:28 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
2018-05-16 04:00:29 +08:00
|
|
|
output(o, 1, _("Failed to merge submodule %s (not fast-forward)"), path);
|
|
|
|
output(o, 2, _("Found a possible merge resolution for the submodule:\n"));
|
2018-05-16 04:00:28 +08:00
|
|
|
print_commit((struct commit *) merges.objects[0].item);
|
2018-05-16 04:00:29 +08:00
|
|
|
output(o, 2, _(
|
2018-05-16 04:00:28 +08:00
|
|
|
"If this is correct simply add it to the index "
|
|
|
|
"for example\n"
|
|
|
|
"by using:\n\n"
|
|
|
|
" git update-index --cacheinfo 160000 %s \"%s\"\n\n"
|
2018-05-16 04:00:29 +08:00
|
|
|
"which will accept this suggestion.\n"),
|
2018-05-16 04:00:28 +08:00
|
|
|
oid_to_hex(&merges.objects[0].item->oid), path);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2018-05-16 04:00:29 +08:00
|
|
|
output(o, 1, _("Failed to merge submodule %s (multiple merges found)"), path);
|
2018-05-16 04:00:28 +08:00
|
|
|
for (i = 0; i < merges.nr; i++)
|
|
|
|
print_commit((struct commit *) merges.objects[i].item);
|
|
|
|
}
|
|
|
|
|
|
|
|
object_array_clear(&merges);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-27 00:06:10 +08:00
|
|
|
static int merge_file_1(struct merge_options *o,
|
2018-04-20 01:58:22 +08:00
|
|
|
const struct diff_filespec *one,
|
|
|
|
const struct diff_filespec *a,
|
|
|
|
const struct diff_filespec *b,
|
|
|
|
const char *filename,
|
|
|
|
const char *branch1,
|
|
|
|
const char *branch2,
|
|
|
|
struct merge_file_info *result)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2016-07-27 00:06:10 +08:00
|
|
|
result->merge = 0;
|
|
|
|
result->clean = 1;
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
if ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {
|
2016-07-27 00:06:10 +08:00
|
|
|
result->clean = 0;
|
2008-08-13 00:45:14 +08:00
|
|
|
if (S_ISREG(a->mode)) {
|
2016-07-27 00:06:10 +08:00
|
|
|
result->mode = a->mode;
|
|
|
|
oidcpy(&result->oid, &a->oid);
|
2008-08-13 00:45:14 +08:00
|
|
|
} else {
|
2016-07-27 00:06:10 +08:00
|
|
|
result->mode = b->mode;
|
|
|
|
oidcpy(&result->oid, &b->oid);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
} else {
|
2016-06-25 07:09:27 +08:00
|
|
|
if (!oid_eq(&a->oid, &one->oid) && !oid_eq(&b->oid, &one->oid))
|
2016-07-27 00:06:10 +08:00
|
|
|
result->merge = 1;
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Merge modes
|
|
|
|
*/
|
2008-09-03 05:53:47 +08:00
|
|
|
if (a->mode == b->mode || a->mode == one->mode)
|
2016-07-27 00:06:10 +08:00
|
|
|
result->mode = b->mode;
|
2008-08-13 00:45:14 +08:00
|
|
|
else {
|
2016-07-27 00:06:10 +08:00
|
|
|
result->mode = a->mode;
|
2008-09-03 05:53:47 +08:00
|
|
|
if (b->mode != one->mode) {
|
2016-07-27 00:06:10 +08:00
|
|
|
result->clean = 0;
|
|
|
|
result->merge = 1;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-25 07:09:27 +08:00
|
|
|
if (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &one->oid))
|
2016-07-27 00:06:10 +08:00
|
|
|
oidcpy(&result->oid, &b->oid);
|
2016-06-25 07:09:27 +08:00
|
|
|
else if (oid_eq(&b->oid, &one->oid))
|
2016-07-27 00:06:10 +08:00
|
|
|
oidcpy(&result->oid, &a->oid);
|
2008-08-13 00:45:14 +08:00
|
|
|
else if (S_ISREG(a->mode)) {
|
|
|
|
mmbuffer_t result_buf;
|
2016-07-27 00:06:26 +08:00
|
|
|
int ret = 0, merge_status;
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2008-09-03 05:53:47 +08:00
|
|
|
merge_status = merge_3way(o, &result_buf, one, a, b,
|
2008-08-13 00:45:14 +08:00
|
|
|
branch1, branch2);
|
|
|
|
|
|
|
|
if ((merge_status < 0) || !result_buf.ptr)
|
2016-08-01 19:44:37 +08:00
|
|
|
ret = err(o, _("Failed to execute internal merge"));
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2018-01-28 08:13:19 +08:00
|
|
|
if (!ret &&
|
|
|
|
write_object_file(result_buf.ptr, result_buf.size,
|
|
|
|
blob_type, &result->oid))
|
2016-08-01 19:44:37 +08:00
|
|
|
ret = err(o, _("Unable to add %s to database"),
|
|
|
|
a->path);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
free(result_buf.ptr);
|
2016-07-27 00:06:26 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2016-07-27 00:06:10 +08:00
|
|
|
result->clean = (merge_status == 0);
|
2008-08-13 00:45:14 +08:00
|
|
|
} else if (S_ISGITLINK(a->mode)) {
|
2018-05-16 04:00:29 +08:00
|
|
|
result->clean = merge_submodule(o, &result->oid,
|
2016-06-25 07:09:23 +08:00
|
|
|
one->path,
|
2017-05-07 06:09:57 +08:00
|
|
|
&one->oid,
|
|
|
|
&a->oid,
|
2018-05-16 04:00:29 +08:00
|
|
|
&b->oid);
|
2008-08-13 00:45:14 +08:00
|
|
|
} else if (S_ISLNK(a->mode)) {
|
2017-09-26 10:40:42 +08:00
|
|
|
switch (o->recursive_variant) {
|
|
|
|
case MERGE_RECURSIVE_NORMAL:
|
|
|
|
oidcpy(&result->oid, &a->oid);
|
|
|
|
if (!oid_eq(&a->oid, &b->oid))
|
|
|
|
result->clean = 0;
|
|
|
|
break;
|
|
|
|
case MERGE_RECURSIVE_OURS:
|
|
|
|
oidcpy(&result->oid, &a->oid);
|
|
|
|
break;
|
|
|
|
case MERGE_RECURSIVE_THEIRS:
|
|
|
|
oidcpy(&result->oid, &b->oid);
|
|
|
|
break;
|
|
|
|
}
|
2016-07-27 00:05:50 +08:00
|
|
|
} else
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("unsupported object type in the tree");
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:22 +08:00
|
|
|
if (result->merge)
|
|
|
|
output(o, 2, _("Auto-merging %s"), filename);
|
|
|
|
|
2016-07-27 00:06:10 +08:00
|
|
|
return 0;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2016-07-27 00:06:10 +08:00
|
|
|
static int merge_file_special_markers(struct merge_options *o,
|
2018-04-20 01:58:22 +08:00
|
|
|
const struct diff_filespec *one,
|
|
|
|
const struct diff_filespec *a,
|
|
|
|
const struct diff_filespec *b,
|
|
|
|
const char *target_filename,
|
|
|
|
const char *branch1,
|
|
|
|
const char *filename1,
|
|
|
|
const char *branch2,
|
|
|
|
const char *filename2,
|
|
|
|
struct merge_file_info *mfi)
|
2011-08-12 13:20:17 +08:00
|
|
|
{
|
|
|
|
char *side1 = NULL;
|
|
|
|
char *side2 = NULL;
|
2016-07-27 00:06:10 +08:00
|
|
|
int ret;
|
2011-08-12 13:20:17 +08:00
|
|
|
|
2014-06-20 05:24:33 +08:00
|
|
|
if (filename1)
|
|
|
|
side1 = xstrfmt("%s:%s", branch1, filename1);
|
|
|
|
if (filename2)
|
|
|
|
side2 = xstrfmt("%s:%s", branch2, filename2);
|
2011-08-12 13:20:17 +08:00
|
|
|
|
2018-04-20 01:58:22 +08:00
|
|
|
ret = merge_file_1(o, one, a, b, target_filename,
|
2016-07-27 00:06:10 +08:00
|
|
|
side1 ? side1 : branch1,
|
|
|
|
side2 ? side2 : branch2, mfi);
|
2018-04-20 01:58:22 +08:00
|
|
|
|
2011-08-12 13:20:17 +08:00
|
|
|
free(side1);
|
|
|
|
free(side2);
|
2016-07-27 00:06:10 +08:00
|
|
|
return ret;
|
2011-08-12 13:20:17 +08:00
|
|
|
}
|
|
|
|
|
2016-07-27 00:06:10 +08:00
|
|
|
static int merge_file_one(struct merge_options *o,
|
2018-04-20 01:58:22 +08:00
|
|
|
const char *path,
|
|
|
|
const struct object_id *o_oid, int o_mode,
|
|
|
|
const struct object_id *a_oid, int a_mode,
|
|
|
|
const struct object_id *b_oid, int b_mode,
|
|
|
|
const char *branch1,
|
|
|
|
const char *branch2,
|
|
|
|
struct merge_file_info *mfi)
|
2011-08-12 13:20:12 +08:00
|
|
|
{
|
|
|
|
struct diff_filespec one, a, b;
|
|
|
|
|
|
|
|
one.path = a.path = b.path = (char *)path;
|
2016-06-25 07:09:27 +08:00
|
|
|
oidcpy(&one.oid, o_oid);
|
2011-08-12 13:20:12 +08:00
|
|
|
one.mode = o_mode;
|
2016-06-25 07:09:27 +08:00
|
|
|
oidcpy(&a.oid, a_oid);
|
2011-08-12 13:20:12 +08:00
|
|
|
a.mode = a_mode;
|
2016-06-25 07:09:27 +08:00
|
|
|
oidcpy(&b.oid, b_oid);
|
2011-08-12 13:20:12 +08:00
|
|
|
b.mode = b_mode;
|
2018-04-20 01:58:22 +08:00
|
|
|
return merge_file_1(o, &one, &a, &b, path, branch1, branch2, mfi);
|
2011-08-12 13:20:12 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:10 +08:00
|
|
|
static int conflict_rename_dir(struct merge_options *o,
|
|
|
|
struct diff_filepair *pair,
|
|
|
|
const char *rename_branch,
|
|
|
|
const char *other_branch)
|
|
|
|
{
|
|
|
|
const struct diff_filespec *dest = pair->two;
|
|
|
|
|
2018-04-20 01:58:11 +08:00
|
|
|
if (!o->call_depth && would_lose_untracked(dest->path)) {
|
|
|
|
char *alt_path = unique_path(o, dest->path, rename_branch);
|
|
|
|
|
|
|
|
output(o, 1, _("Error: Refusing to lose untracked file at %s; "
|
|
|
|
"writing to %s instead."),
|
|
|
|
dest->path, alt_path);
|
|
|
|
/*
|
|
|
|
* Write the file in worktree at alt_path, but not in the
|
|
|
|
* index. Instead, write to dest->path for the index but
|
|
|
|
* only at the higher appropriate stage.
|
|
|
|
*/
|
|
|
|
if (update_file(o, 0, &dest->oid, dest->mode, alt_path))
|
|
|
|
return -1;
|
|
|
|
free(alt_path);
|
|
|
|
return update_stages(o, dest->path, NULL,
|
|
|
|
rename_branch == o->branch1 ? dest : NULL,
|
|
|
|
rename_branch == o->branch1 ? NULL : dest);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update dest->path both in index and in worktree */
|
2018-04-20 01:58:10 +08:00
|
|
|
if (update_file(o, 1, &dest->oid, dest->mode, dest->path))
|
|
|
|
return -1;
|
|
|
|
return 0;
|
2011-08-12 13:20:12 +08:00
|
|
|
}
|
|
|
|
|
2016-07-27 00:06:21 +08:00
|
|
|
static int handle_change_delete(struct merge_options *o,
|
2017-01-29 04:37:01 +08:00
|
|
|
const char *path, const char *old_path,
|
2016-06-25 07:09:27 +08:00
|
|
|
const struct object_id *o_oid, int o_mode,
|
2017-01-29 04:37:01 +08:00
|
|
|
const struct object_id *changed_oid,
|
|
|
|
int changed_mode,
|
|
|
|
const char *change_branch,
|
|
|
|
const char *delete_branch,
|
2011-08-12 13:20:19 +08:00
|
|
|
const char *change, const char *change_past)
|
|
|
|
{
|
2017-01-29 04:37:01 +08:00
|
|
|
char *alt_path = NULL;
|
|
|
|
const char *update_path = path;
|
2016-07-27 00:06:21 +08:00
|
|
|
int ret = 0;
|
2017-01-29 04:37:01 +08:00
|
|
|
|
2018-04-20 01:58:11 +08:00
|
|
|
if (dir_in_way(path, !o->call_depth, 0) ||
|
|
|
|
(!o->call_depth && would_lose_untracked(path))) {
|
2017-01-29 04:37:01 +08:00
|
|
|
update_path = alt_path = unique_path(o, path, change_branch);
|
2011-08-12 13:20:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (o->call_depth) {
|
|
|
|
/*
|
|
|
|
* We cannot arbitrarily accept either a_sha or b_sha as
|
|
|
|
* correct; since there is no true "middle point" between
|
|
|
|
* them, simply reuse the base version for virtual merge base.
|
|
|
|
*/
|
2016-07-27 00:06:21 +08:00
|
|
|
ret = remove_file_from_cache(path);
|
|
|
|
if (!ret)
|
2017-01-29 04:37:01 +08:00
|
|
|
ret = update_file(o, 0, o_oid, o_mode, update_path);
|
2011-08-12 13:20:19 +08:00
|
|
|
} else {
|
2017-01-29 04:37:01 +08:00
|
|
|
if (!alt_path) {
|
|
|
|
if (!old_path) {
|
|
|
|
output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
|
|
|
|
"and %s in %s. Version %s of %s left in tree."),
|
|
|
|
change, path, delete_branch, change_past,
|
|
|
|
change_branch, change_branch, path);
|
|
|
|
} else {
|
|
|
|
output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
|
|
|
|
"and %s to %s in %s. Version %s of %s left in tree."),
|
|
|
|
change, old_path, delete_branch, change_past, path,
|
|
|
|
change_branch, change_branch, path);
|
|
|
|
}
|
2012-07-25 22:53:13 +08:00
|
|
|
} else {
|
2017-01-29 04:37:01 +08:00
|
|
|
if (!old_path) {
|
|
|
|
output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
|
|
|
|
"and %s in %s. Version %s of %s left in tree at %s."),
|
|
|
|
change, path, delete_branch, change_past,
|
|
|
|
change_branch, change_branch, path, alt_path);
|
|
|
|
} else {
|
|
|
|
output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
|
|
|
|
"and %s to %s in %s. Version %s of %s left in tree at %s."),
|
|
|
|
change, old_path, delete_branch, change_past, path,
|
|
|
|
change_branch, change_branch, path, alt_path);
|
|
|
|
}
|
2012-07-25 22:53:13 +08:00
|
|
|
}
|
2011-08-12 13:20:27 +08:00
|
|
|
/*
|
2017-01-29 04:37:01 +08:00
|
|
|
* No need to call update_file() on path when change_branch ==
|
|
|
|
* o->branch1 && !alt_path, since that would needlessly touch
|
|
|
|
* path. We could call update_file_flags() with update_cache=0
|
|
|
|
* and update_wd=0, but that's a no-op.
|
2011-08-12 13:20:27 +08:00
|
|
|
*/
|
2017-01-29 04:37:01 +08:00
|
|
|
if (change_branch != o->branch1 || alt_path)
|
|
|
|
ret = update_file(o, 0, changed_oid, changed_mode, update_path);
|
2011-08-12 13:20:19 +08:00
|
|
|
}
|
2017-01-29 04:37:01 +08:00
|
|
|
free(alt_path);
|
2016-07-27 00:06:21 +08:00
|
|
|
|
|
|
|
return ret;
|
2011-08-12 13:20:19 +08:00
|
|
|
}
|
|
|
|
|
2016-07-27 00:06:21 +08:00
|
|
|
static int conflict_rename_delete(struct merge_options *o,
|
2010-09-20 16:28:50 +08:00
|
|
|
struct diff_filepair *pair,
|
|
|
|
const char *rename_branch,
|
2017-01-29 04:37:01 +08:00
|
|
|
const char *delete_branch)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2011-08-12 13:20:20 +08:00
|
|
|
const struct diff_filespec *orig = pair->one;
|
|
|
|
const struct diff_filespec *dest = pair->two;
|
2010-09-20 16:28:50 +08:00
|
|
|
|
2016-07-27 00:06:21 +08:00
|
|
|
if (handle_change_delete(o,
|
|
|
|
o->call_depth ? orig->path : dest->path,
|
2017-01-29 04:37:01 +08:00
|
|
|
o->call_depth ? NULL : orig->path,
|
2016-07-27 00:06:21 +08:00
|
|
|
&orig->oid, orig->mode,
|
2017-01-29 04:37:01 +08:00
|
|
|
&dest->oid, dest->mode,
|
|
|
|
rename_branch, delete_branch,
|
2016-07-27 00:06:21 +08:00
|
|
|
_("rename"), _("renamed")))
|
|
|
|
return -1;
|
2011-08-12 13:20:20 +08:00
|
|
|
|
2016-07-27 00:06:21 +08:00
|
|
|
if (o->call_depth)
|
|
|
|
return remove_file_from_cache(dest->path);
|
|
|
|
else
|
2016-08-01 19:44:37 +08:00
|
|
|
return update_stages(o, dest->path, NULL,
|
2016-07-27 00:06:21 +08:00
|
|
|
rename_branch == o->branch1 ? dest : NULL,
|
|
|
|
rename_branch == o->branch1 ? NULL : dest);
|
2010-09-20 16:28:50 +08:00
|
|
|
}
|
|
|
|
|
2011-08-12 13:20:21 +08:00
|
|
|
static struct diff_filespec *filespec_from_entry(struct diff_filespec *target,
|
|
|
|
struct stage_data *entry,
|
|
|
|
int stage)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2016-06-25 07:09:27 +08:00
|
|
|
struct object_id *oid = &entry->stages[stage].oid;
|
2011-08-12 13:20:21 +08:00
|
|
|
unsigned mode = entry->stages[stage].mode;
|
2016-06-25 07:09:27 +08:00
|
|
|
if (mode == 0 || is_null_oid(oid))
|
2011-08-12 13:20:21 +08:00
|
|
|
return NULL;
|
2016-06-25 07:09:27 +08:00
|
|
|
oidcpy(&target->oid, oid);
|
2011-08-12 13:20:21 +08:00
|
|
|
target->mode = mode;
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
2016-07-27 00:06:21 +08:00
|
|
|
static int handle_file(struct merge_options *o,
|
2011-08-12 13:20:22 +08:00
|
|
|
struct diff_filespec *rename,
|
|
|
|
int stage,
|
|
|
|
struct rename_conflict_info *ci)
|
|
|
|
{
|
|
|
|
char *dst_name = rename->path;
|
|
|
|
struct stage_data *dst_entry;
|
|
|
|
const char *cur_branch, *other_branch;
|
|
|
|
struct diff_filespec other;
|
|
|
|
struct diff_filespec *add;
|
2016-07-27 00:06:21 +08:00
|
|
|
int ret;
|
2011-08-12 13:20:22 +08:00
|
|
|
|
|
|
|
if (stage == 2) {
|
|
|
|
dst_entry = ci->dst_entry1;
|
|
|
|
cur_branch = ci->branch1;
|
|
|
|
other_branch = ci->branch2;
|
|
|
|
} else {
|
|
|
|
dst_entry = ci->dst_entry2;
|
|
|
|
cur_branch = ci->branch2;
|
|
|
|
other_branch = ci->branch1;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
2011-08-12 13:20:22 +08:00
|
|
|
|
|
|
|
add = filespec_from_entry(&other, dst_entry, stage ^ 1);
|
|
|
|
if (add) {
|
2018-04-20 01:58:13 +08:00
|
|
|
int ren_src_was_dirty = was_dirty(o, rename->path);
|
2011-08-12 13:20:22 +08:00
|
|
|
char *add_name = unique_path(o, rename->path, other_branch);
|
2016-07-27 00:06:21 +08:00
|
|
|
if (update_file(o, 0, &add->oid, add->mode, add_name))
|
|
|
|
return -1;
|
2011-08-12 13:20:22 +08:00
|
|
|
|
2018-04-20 01:58:13 +08:00
|
|
|
if (ren_src_was_dirty) {
|
|
|
|
output(o, 1, _("Refusing to lose dirty file at %s"),
|
|
|
|
rename->path);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Because the double negatives somehow keep confusing me...
|
|
|
|
* 1) update_wd iff !ren_src_was_dirty.
|
|
|
|
* 2) no_wd iff !update_wd
|
|
|
|
* 3) so, no_wd == !!ren_src_was_dirty == ren_src_was_dirty
|
|
|
|
*/
|
|
|
|
remove_file(o, 0, rename->path, ren_src_was_dirty);
|
2011-08-12 13:20:22 +08:00
|
|
|
dst_name = unique_path(o, rename->path, cur_branch);
|
|
|
|
} else {
|
2016-11-08 02:31:31 +08:00
|
|
|
if (dir_in_way(rename->path, !o->call_depth, 0)) {
|
2011-08-12 13:20:22 +08:00
|
|
|
dst_name = unique_path(o, rename->path, cur_branch);
|
2012-07-25 22:53:13 +08:00
|
|
|
output(o, 1, _("%s is a directory in %s adding as %s instead"),
|
2011-08-12 13:20:22 +08:00
|
|
|
rename->path, other_branch, dst_name);
|
2018-04-20 01:58:11 +08:00
|
|
|
} else if (!o->call_depth &&
|
|
|
|
would_lose_untracked(rename->path)) {
|
|
|
|
dst_name = unique_path(o, rename->path, cur_branch);
|
|
|
|
output(o, 1, _("Refusing to lose untracked file at %s; "
|
|
|
|
"adding as %s instead"),
|
|
|
|
rename->path, dst_name);
|
2011-08-12 13:20:22 +08:00
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
2016-07-27 00:06:21 +08:00
|
|
|
if ((ret = update_file(o, 0, &rename->oid, rename->mode, dst_name)))
|
|
|
|
; /* fall through, do allow dst_name to be released */
|
|
|
|
else if (stage == 2)
|
2016-08-01 19:44:37 +08:00
|
|
|
ret = update_stages(o, rename->path, NULL, rename, add);
|
2011-08-12 13:20:25 +08:00
|
|
|
else
|
2016-08-01 19:44:37 +08:00
|
|
|
ret = update_stages(o, rename->path, NULL, add, rename);
|
2011-08-12 13:20:22 +08:00
|
|
|
|
|
|
|
if (dst_name != rename->path)
|
|
|
|
free(dst_name);
|
2016-07-27 00:06:21 +08:00
|
|
|
|
|
|
|
return ret;
|
2011-08-12 13:20:22 +08:00
|
|
|
}
|
|
|
|
|
2016-07-27 00:06:21 +08:00
|
|
|
static int conflict_rename_rename_1to2(struct merge_options *o,
|
2011-08-12 13:20:14 +08:00
|
|
|
struct rename_conflict_info *ci)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2010-09-20 16:28:48 +08:00
|
|
|
/* One file was renamed in both branches, but to different names. */
|
2011-08-12 13:20:14 +08:00
|
|
|
struct diff_filespec *one = ci->pair1->one;
|
|
|
|
struct diff_filespec *a = ci->pair1->two;
|
|
|
|
struct diff_filespec *b = ci->pair2->two;
|
2011-08-12 13:20:08 +08:00
|
|
|
|
2012-07-25 22:53:13 +08:00
|
|
|
output(o, 1, _("CONFLICT (rename/rename): "
|
2011-08-12 13:20:08 +08:00
|
|
|
"Rename \"%s\"->\"%s\" in branch \"%s\" "
|
2012-07-25 22:53:13 +08:00
|
|
|
"rename \"%s\"->\"%s\" in \"%s\"%s"),
|
2011-08-12 13:20:14 +08:00
|
|
|
one->path, a->path, ci->branch1,
|
|
|
|
one->path, b->path, ci->branch2,
|
2012-07-25 22:53:13 +08:00
|
|
|
o->call_depth ? _(" (left unresolved)") : "");
|
2008-09-03 05:53:47 +08:00
|
|
|
if (o->call_depth) {
|
2011-08-12 13:20:13 +08:00
|
|
|
struct merge_file_info mfi;
|
2011-08-12 13:20:29 +08:00
|
|
|
struct diff_filespec other;
|
|
|
|
struct diff_filespec *add;
|
2016-07-27 00:06:10 +08:00
|
|
|
if (merge_file_one(o, one->path,
|
2016-06-25 07:09:27 +08:00
|
|
|
&one->oid, one->mode,
|
|
|
|
&a->oid, a->mode,
|
|
|
|
&b->oid, b->mode,
|
2016-07-27 00:06:10 +08:00
|
|
|
ci->branch1, ci->branch2, &mfi))
|
2016-07-27 00:06:21 +08:00
|
|
|
return -1;
|
|
|
|
|
2008-08-13 00:45:14 +08:00
|
|
|
/*
|
2011-08-12 13:20:13 +08:00
|
|
|
* FIXME: For rename/add-source conflicts (if we could detect
|
|
|
|
* such), this is wrong. We should instead find a unique
|
|
|
|
* pathname and then either rename the add-source file to that
|
|
|
|
* unique path, or use that unique path instead of src here.
|
2008-08-13 00:45:14 +08:00
|
|
|
*/
|
2016-07-27 00:06:21 +08:00
|
|
|
if (update_file(o, 0, &mfi.oid, mfi.mode, one->path))
|
|
|
|
return -1;
|
2010-09-20 16:29:00 +08:00
|
|
|
|
2011-08-12 13:20:29 +08:00
|
|
|
/*
|
|
|
|
* Above, we put the merged content at the merge-base's
|
|
|
|
* path. Now we usually need to delete both a->path and
|
|
|
|
* b->path. However, the rename on each side of the merge
|
|
|
|
* could also be involved in a rename/add conflict. In
|
|
|
|
* such cases, we should keep the added file around,
|
|
|
|
* resolving the conflict at that path in its favor.
|
|
|
|
*/
|
|
|
|
add = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1);
|
2016-07-27 00:06:21 +08:00
|
|
|
if (add) {
|
|
|
|
if (update_file(o, 0, &add->oid, add->mode, a->path))
|
|
|
|
return -1;
|
|
|
|
}
|
2011-08-12 13:20:29 +08:00
|
|
|
else
|
|
|
|
remove_file_from_cache(a->path);
|
|
|
|
add = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1);
|
2016-07-27 00:06:21 +08:00
|
|
|
if (add) {
|
|
|
|
if (update_file(o, 0, &add->oid, add->mode, b->path))
|
|
|
|
return -1;
|
|
|
|
}
|
2011-08-12 13:20:29 +08:00
|
|
|
else
|
|
|
|
remove_file_from_cache(b->path);
|
2016-07-27 00:06:21 +08:00
|
|
|
} else if (handle_file(o, a, 2, ci) || handle_file(o, b, 3, ci))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2016-07-27 00:06:21 +08:00
|
|
|
static int conflict_rename_rename_2to1(struct merge_options *o,
|
2011-08-12 13:20:15 +08:00
|
|
|
struct rename_conflict_info *ci)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2011-08-12 13:20:15 +08:00
|
|
|
/* Two files, a & b, were renamed to the same thing, c. */
|
|
|
|
struct diff_filespec *a = ci->pair1->one;
|
|
|
|
struct diff_filespec *b = ci->pair2->one;
|
|
|
|
struct diff_filespec *c1 = ci->pair1->two;
|
|
|
|
struct diff_filespec *c2 = ci->pair2->two;
|
|
|
|
char *path = c1->path; /* == c2->path */
|
2018-04-20 01:58:22 +08:00
|
|
|
char *path_side_1_desc;
|
|
|
|
char *path_side_2_desc;
|
merge-recursive: Consider modifications in rename/rename(2to1) conflicts
Our previous conflict resolution for renaming two different files to the
same name ignored the fact that each of those files may have modifications
from both sides of history to consider. We need to do a three-way merge
for each of those files, and then handle the conflict of both sets of
merged contents trying to be recorded with the same name.
It is important to note that this changes our strategy in the recursive
case. After doing a three-way content merge of each of the files
involved, we still are faced with the fact that we are trying to put both
of the results (including conflict markers) into the same path. We could
do another two-way merge, but I think that becomes confusing. Also,
taking a hint from the modify/delete and rename/delete cases we handled
earlier, a more useful "common ground" would be to keep the three-way
content merge but record it with the original filename. The renames can
still be detected, we just allow it to be done in the o->call_depth=0
case. This seems to result in simpler & easier to understand merge
conflicts as well, as evidenced by some of the changes needed in our
testsuite in t6036. (However, it should be noted that this change will
cause problems those renames also occur along with a file being added
whose name matches the source of the rename. Since git currently cannot
detect rename/add-source situations, though, this codepath is not
currently used for those cases anyway.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-08-12 13:20:18 +08:00
|
|
|
struct merge_file_info mfi_c1;
|
|
|
|
struct merge_file_info mfi_c2;
|
2016-07-27 00:06:21 +08:00
|
|
|
int ret;
|
2011-08-12 13:20:15 +08:00
|
|
|
|
2012-07-25 22:53:13 +08:00
|
|
|
output(o, 1, _("CONFLICT (rename/rename): "
|
2011-08-12 13:20:15 +08:00
|
|
|
"Rename %s->%s in %s. "
|
2012-07-25 22:53:13 +08:00
|
|
|
"Rename %s->%s in %s"),
|
2011-08-12 13:20:15 +08:00
|
|
|
a->path, c1->path, ci->branch1,
|
|
|
|
b->path, c2->path, ci->branch2);
|
|
|
|
|
2016-04-10 14:13:36 +08:00
|
|
|
remove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));
|
|
|
|
remove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));
|
2011-08-12 13:20:15 +08:00
|
|
|
|
2018-04-20 01:58:22 +08:00
|
|
|
path_side_1_desc = xstrfmt("%s (was %s)", path, a->path);
|
|
|
|
path_side_2_desc = xstrfmt("%s (was %s)", path, b->path);
|
2016-07-27 00:06:10 +08:00
|
|
|
if (merge_file_special_markers(o, a, c1, &ci->ren1_other,
|
2018-04-20 01:58:22 +08:00
|
|
|
path_side_1_desc,
|
2016-07-27 00:06:10 +08:00
|
|
|
o->branch1, c1->path,
|
|
|
|
o->branch2, ci->ren1_other.path, &mfi_c1) ||
|
|
|
|
merge_file_special_markers(o, b, &ci->ren2_other, c2,
|
2018-04-20 01:58:22 +08:00
|
|
|
path_side_2_desc,
|
2016-07-27 00:06:10 +08:00
|
|
|
o->branch1, ci->ren2_other.path,
|
|
|
|
o->branch2, c2->path, &mfi_c2))
|
2016-07-27 00:06:21 +08:00
|
|
|
return -1;
|
2018-04-20 01:58:22 +08:00
|
|
|
free(path_side_1_desc);
|
|
|
|
free(path_side_2_desc);
|
merge-recursive: Consider modifications in rename/rename(2to1) conflicts
Our previous conflict resolution for renaming two different files to the
same name ignored the fact that each of those files may have modifications
from both sides of history to consider. We need to do a three-way merge
for each of those files, and then handle the conflict of both sets of
merged contents trying to be recorded with the same name.
It is important to note that this changes our strategy in the recursive
case. After doing a three-way content merge of each of the files
involved, we still are faced with the fact that we are trying to put both
of the results (including conflict markers) into the same path. We could
do another two-way merge, but I think that becomes confusing. Also,
taking a hint from the modify/delete and rename/delete cases we handled
earlier, a more useful "common ground" would be to keep the three-way
content merge but record it with the original filename. The renames can
still be detected, we just allow it to be done in the o->call_depth=0
case. This seems to result in simpler & easier to understand merge
conflicts as well, as evidenced by some of the changes needed in our
testsuite in t6036. (However, it should be noted that this change will
cause problems those renames also occur along with a file being added
whose name matches the source of the rename. Since git currently cannot
detect rename/add-source situations, though, this codepath is not
currently used for those cases anyway.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-08-12 13:20:18 +08:00
|
|
|
|
2011-08-12 13:20:04 +08:00
|
|
|
if (o->call_depth) {
|
merge-recursive: Consider modifications in rename/rename(2to1) conflicts
Our previous conflict resolution for renaming two different files to the
same name ignored the fact that each of those files may have modifications
from both sides of history to consider. We need to do a three-way merge
for each of those files, and then handle the conflict of both sets of
merged contents trying to be recorded with the same name.
It is important to note that this changes our strategy in the recursive
case. After doing a three-way content merge of each of the files
involved, we still are faced with the fact that we are trying to put both
of the results (including conflict markers) into the same path. We could
do another two-way merge, but I think that becomes confusing. Also,
taking a hint from the modify/delete and rename/delete cases we handled
earlier, a more useful "common ground" would be to keep the three-way
content merge but record it with the original filename. The renames can
still be detected, we just allow it to be done in the o->call_depth=0
case. This seems to result in simpler & easier to understand merge
conflicts as well, as evidenced by some of the changes needed in our
testsuite in t6036. (However, it should be noted that this change will
cause problems those renames also occur along with a file being added
whose name matches the source of the rename. Since git currently cannot
detect rename/add-source situations, though, this codepath is not
currently used for those cases anyway.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-08-12 13:20:18 +08:00
|
|
|
/*
|
|
|
|
* If mfi_c1.clean && mfi_c2.clean, then it might make
|
|
|
|
* sense to do a two-way merge of those results. But, I
|
|
|
|
* think in all cases, it makes sense to have the virtual
|
|
|
|
* merge base just undo the renames; they can be detected
|
|
|
|
* again later for the non-recursive merge.
|
|
|
|
*/
|
|
|
|
remove_file(o, 0, path, 0);
|
2016-07-27 00:06:21 +08:00
|
|
|
ret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, a->path);
|
|
|
|
if (!ret)
|
|
|
|
ret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,
|
|
|
|
b->path);
|
2011-08-12 13:20:04 +08:00
|
|
|
} else {
|
2011-08-12 13:20:15 +08:00
|
|
|
char *new_path1 = unique_path(o, path, ci->branch1);
|
|
|
|
char *new_path2 = unique_path(o, path, ci->branch2);
|
2012-07-25 22:53:13 +08:00
|
|
|
output(o, 1, _("Renaming %s to %s and %s to %s instead"),
|
2011-08-12 13:20:15 +08:00
|
|
|
a->path, new_path1, b->path, new_path2);
|
2018-04-20 01:58:13 +08:00
|
|
|
if (was_dirty(o, path))
|
|
|
|
output(o, 1, _("Refusing to lose dirty file at %s"),
|
|
|
|
path);
|
|
|
|
else if (would_lose_untracked(path))
|
2018-04-20 01:58:11 +08:00
|
|
|
/*
|
|
|
|
* Only way we get here is if both renames were from
|
|
|
|
* a directory rename AND user had an untracked file
|
|
|
|
* at the location where both files end up after the
|
|
|
|
* two directory renames. See testcase 10d of t6043.
|
|
|
|
*/
|
|
|
|
output(o, 1, _("Refusing to lose untracked file at "
|
|
|
|
"%s, even though it's in the way."),
|
|
|
|
path);
|
|
|
|
else
|
|
|
|
remove_file(o, 0, path, 0);
|
2016-07-27 00:06:21 +08:00
|
|
|
ret = update_file(o, 0, &mfi_c1.oid, mfi_c1.mode, new_path1);
|
|
|
|
if (!ret)
|
|
|
|
ret = update_file(o, 0, &mfi_c2.oid, mfi_c2.mode,
|
|
|
|
new_path2);
|
2018-04-20 01:58:10 +08:00
|
|
|
/*
|
|
|
|
* unpack_trees() actually populates the index for us for
|
|
|
|
* "normal" rename/rename(2to1) situtations so that the
|
|
|
|
* correct entries are at the higher stages, which would
|
|
|
|
* make the call below to update_stages_for_stage_data
|
|
|
|
* unnecessary. However, if either of the renames came
|
|
|
|
* from a directory rename, then unpack_trees() will not
|
|
|
|
* have gotten the right data loaded into the index, so we
|
|
|
|
* need to do so now. (While it'd be tempting to move this
|
|
|
|
* call to update_stages_for_stage_data() to
|
|
|
|
* apply_directory_rename_modifications(), that would break
|
|
|
|
* our intermediate calls to would_lose_untracked() since
|
|
|
|
* those rely on the current in-memory index. See also the
|
|
|
|
* big "NOTE" in update_stages()).
|
|
|
|
*/
|
|
|
|
if (update_stages_for_stage_data(o, path, ci->dst_entry1))
|
|
|
|
ret = -1;
|
|
|
|
|
2011-08-12 13:20:04 +08:00
|
|
|
free(new_path2);
|
|
|
|
free(new_path1);
|
|
|
|
}
|
2016-07-27 00:06:21 +08:00
|
|
|
|
|
|
|
return ret;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 01:57:59 +08:00
|
|
|
/*
|
2018-04-20 01:58:03 +08:00
|
|
|
* Get the diff_filepairs changed between o_tree and tree.
|
2018-04-20 01:57:59 +08:00
|
|
|
*/
|
2018-04-20 01:58:03 +08:00
|
|
|
static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
|
|
|
|
struct tree *o_tree,
|
|
|
|
struct tree *tree)
|
2018-04-20 01:57:59 +08:00
|
|
|
{
|
2018-04-20 01:58:03 +08:00
|
|
|
struct diff_queue_struct *ret;
|
2018-04-20 01:57:59 +08:00
|
|
|
struct diff_options opts;
|
|
|
|
|
|
|
|
diff_setup(&opts);
|
|
|
|
opts.flags.recursive = 1;
|
|
|
|
opts.flags.rename_empty = 0;
|
2018-05-03 00:01:14 +08:00
|
|
|
opts.detect_rename = merge_detect_rename(o);
|
|
|
|
/*
|
|
|
|
* We do not have logic to handle the detection of copies. In
|
|
|
|
* fact, it may not even make sense to add such logic: would we
|
|
|
|
* really want a change to a base file to be propagated through
|
|
|
|
* multiple other files by a merge?
|
|
|
|
*/
|
|
|
|
if (opts.detect_rename > DIFF_DETECT_RENAME)
|
|
|
|
opts.detect_rename = DIFF_DETECT_RENAME;
|
2018-04-20 01:57:59 +08:00
|
|
|
opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
|
|
|
|
o->diff_rename_limit >= 0 ? o->diff_rename_limit :
|
|
|
|
1000;
|
|
|
|
opts.rename_score = o->rename_score;
|
|
|
|
opts.show_rename_progress = o->show_rename_progress;
|
|
|
|
opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
|
|
|
diff_setup_done(&opts);
|
|
|
|
diff_tree_oid(&o_tree->object.oid, &tree->object.oid, "", &opts);
|
|
|
|
diffcore_std(&opts);
|
|
|
|
if (opts.needed_rename_limit > o->needed_rename_limit)
|
|
|
|
o->needed_rename_limit = opts.needed_rename_limit;
|
2018-04-20 01:58:03 +08:00
|
|
|
|
|
|
|
ret = xmalloc(sizeof(*ret));
|
|
|
|
*ret = diff_queued_diff;
|
|
|
|
|
|
|
|
opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
|
|
|
diff_queued_diff.nr = 0;
|
|
|
|
diff_queued_diff.queue = NULL;
|
|
|
|
diff_flush(&opts);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:06 +08:00
|
|
|
static int tree_has_path(struct tree *tree, const char *path)
|
|
|
|
{
|
|
|
|
struct object_id hashy;
|
|
|
|
unsigned int mode_o;
|
|
|
|
|
|
|
|
return !get_tree_entry(&tree->object.oid, path,
|
|
|
|
&hashy, &mode_o);
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:07 +08:00
|
|
|
/*
|
|
|
|
* Return a new string that replaces the beginning portion (which matches
|
|
|
|
* entry->dir), with entry->new_dir. In perl-speak:
|
|
|
|
* new_path_name = (old_path =~ s/entry->dir/entry->new_dir/);
|
|
|
|
* NOTE:
|
|
|
|
* Caller must ensure that old_path starts with entry->dir + '/'.
|
|
|
|
*/
|
|
|
|
static char *apply_dir_rename(struct dir_rename_entry *entry,
|
|
|
|
const char *old_path)
|
|
|
|
{
|
|
|
|
struct strbuf new_path = STRBUF_INIT;
|
|
|
|
int oldlen, newlen;
|
|
|
|
|
|
|
|
if (entry->non_unique_new_dir)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
oldlen = strlen(entry->dir);
|
|
|
|
newlen = entry->new_dir.len + (strlen(old_path) - oldlen) + 1;
|
|
|
|
strbuf_grow(&new_path, newlen);
|
|
|
|
strbuf_addbuf(&new_path, &entry->new_dir);
|
|
|
|
strbuf_addstr(&new_path, &old_path[oldlen]);
|
|
|
|
|
|
|
|
return strbuf_detach(&new_path, NULL);
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:05 +08:00
|
|
|
static void get_renamed_dir_portion(const char *old_path, const char *new_path,
|
|
|
|
char **old_dir, char **new_dir)
|
|
|
|
{
|
|
|
|
char *end_of_old, *end_of_new;
|
|
|
|
int old_len, new_len;
|
|
|
|
|
|
|
|
*old_dir = NULL;
|
|
|
|
*new_dir = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For
|
|
|
|
* "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c"
|
|
|
|
* the "e/foo.c" part is the same, we just want to know that
|
|
|
|
* "a/b/c/d" was renamed to "a/b/some/thing/else"
|
|
|
|
* so, for this example, this function returns "a/b/c/d" in
|
|
|
|
* *old_dir and "a/b/some/thing/else" in *new_dir.
|
|
|
|
*
|
|
|
|
* Also, if the basename of the file changed, we don't care. We
|
|
|
|
* want to know which portion of the directory, if any, changed.
|
|
|
|
*/
|
|
|
|
end_of_old = strrchr(old_path, '/');
|
|
|
|
end_of_new = strrchr(new_path, '/');
|
|
|
|
|
|
|
|
if (end_of_old == NULL || end_of_new == NULL)
|
|
|
|
return;
|
|
|
|
while (*--end_of_new == *--end_of_old &&
|
|
|
|
end_of_old != old_path &&
|
|
|
|
end_of_new != new_path)
|
|
|
|
; /* Do nothing; all in the while loop */
|
|
|
|
/*
|
|
|
|
* We've found the first non-matching character in the directory
|
|
|
|
* paths. That means the current directory we were comparing
|
|
|
|
* represents the rename. Move end_of_old and end_of_new back
|
|
|
|
* to the full directory name.
|
|
|
|
*/
|
|
|
|
if (*end_of_old == '/')
|
|
|
|
end_of_old++;
|
|
|
|
if (*end_of_old != '/')
|
|
|
|
end_of_new++;
|
|
|
|
end_of_old = strchr(end_of_old, '/');
|
|
|
|
end_of_new = strchr(end_of_new, '/');
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It may have been the case that old_path and new_path were the same
|
|
|
|
* directory all along. Don't claim a rename if they're the same.
|
|
|
|
*/
|
|
|
|
old_len = end_of_old - old_path;
|
|
|
|
new_len = end_of_new - new_path;
|
|
|
|
|
|
|
|
if (old_len != new_len || strncmp(old_path, new_path, old_len)) {
|
|
|
|
*old_dir = xstrndup(old_path, old_len);
|
|
|
|
*new_dir = xstrndup(new_path, new_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:06 +08:00
|
|
|
static void remove_hashmap_entries(struct hashmap *dir_renames,
|
|
|
|
struct string_list *items_to_remove)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct dir_rename_entry *entry;
|
|
|
|
|
|
|
|
for (i = 0; i < items_to_remove->nr; i++) {
|
|
|
|
entry = items_to_remove->items[i].util;
|
|
|
|
hashmap_remove(dir_renames, entry, NULL);
|
|
|
|
}
|
|
|
|
string_list_clear(items_to_remove, 0);
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:08 +08:00
|
|
|
/*
|
|
|
|
* See if there is a directory rename for path, and if there are any file
|
|
|
|
* level conflicts for the renamed location. If there is a rename and
|
|
|
|
* there are no conflicts, return the new name. Otherwise, return NULL.
|
|
|
|
*/
|
|
|
|
static char *handle_path_level_conflicts(struct merge_options *o,
|
|
|
|
const char *path,
|
|
|
|
struct dir_rename_entry *entry,
|
|
|
|
struct hashmap *collisions,
|
|
|
|
struct tree *tree)
|
|
|
|
{
|
|
|
|
char *new_path = NULL;
|
|
|
|
struct collision_entry *collision_ent;
|
|
|
|
int clean = 1;
|
|
|
|
struct strbuf collision_paths = STRBUF_INIT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* entry has the mapping of old directory name to new directory name
|
|
|
|
* that we want to apply to path.
|
|
|
|
*/
|
|
|
|
new_path = apply_dir_rename(entry, path);
|
|
|
|
|
|
|
|
if (!new_path) {
|
|
|
|
/* This should only happen when entry->non_unique_new_dir set */
|
|
|
|
if (!entry->non_unique_new_dir)
|
|
|
|
BUG("entry->non_unqiue_dir not set and !new_path");
|
|
|
|
output(o, 1, _("CONFLICT (directory rename split): "
|
|
|
|
"Unclear where to place %s because directory "
|
|
|
|
"%s was renamed to multiple other directories, "
|
|
|
|
"with no destination getting a majority of the "
|
|
|
|
"files."),
|
|
|
|
path, entry->dir);
|
|
|
|
clean = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The caller needs to have ensured that it has pre-populated
|
|
|
|
* collisions with all paths that map to new_path. Do a quick check
|
|
|
|
* to ensure that's the case.
|
|
|
|
*/
|
|
|
|
collision_ent = collision_find_entry(collisions, new_path);
|
|
|
|
if (collision_ent == NULL)
|
|
|
|
BUG("collision_ent is NULL");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for one-sided add/add/.../add conflicts, i.e.
|
|
|
|
* where implicit renames from the other side doing
|
|
|
|
* directory rename(s) can affect this side of history
|
|
|
|
* to put multiple paths into the same location. Warn
|
|
|
|
* and bail on directory renames for such paths.
|
|
|
|
*/
|
|
|
|
if (collision_ent->reported_already) {
|
|
|
|
clean = 0;
|
|
|
|
} else if (tree_has_path(tree, new_path)) {
|
|
|
|
collision_ent->reported_already = 1;
|
|
|
|
strbuf_add_separated_string_list(&collision_paths, ", ",
|
|
|
|
&collision_ent->source_files);
|
|
|
|
output(o, 1, _("CONFLICT (implicit dir rename): Existing "
|
|
|
|
"file/dir at %s in the way of implicit "
|
|
|
|
"directory rename(s) putting the following "
|
|
|
|
"path(s) there: %s."),
|
|
|
|
new_path, collision_paths.buf);
|
|
|
|
clean = 0;
|
|
|
|
} else if (collision_ent->source_files.nr > 1) {
|
|
|
|
collision_ent->reported_already = 1;
|
|
|
|
strbuf_add_separated_string_list(&collision_paths, ", ",
|
|
|
|
&collision_ent->source_files);
|
|
|
|
output(o, 1, _("CONFLICT (implicit dir rename): Cannot map "
|
|
|
|
"more than one path to %s; implicit directory "
|
|
|
|
"renames tried to put these paths there: %s"),
|
|
|
|
new_path, collision_paths.buf);
|
|
|
|
clean = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free memory we no longer need */
|
|
|
|
strbuf_release(&collision_paths);
|
|
|
|
if (!clean && new_path) {
|
|
|
|
free(new_path);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new_path;
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:06 +08:00
|
|
|
/*
|
|
|
|
* There are a couple things we want to do at the directory level:
|
|
|
|
* 1. Check for both sides renaming to the same thing, in order to avoid
|
|
|
|
* implicit renaming of files that should be left in place. (See
|
|
|
|
* testcase 6b in t6043 for details.)
|
|
|
|
* 2. Prune directory renames if there are still files left in the
|
|
|
|
* the original directory. These represent a partial directory rename,
|
|
|
|
* i.e. a rename where only some of the files within the directory
|
|
|
|
* were renamed elsewhere. (Technically, this could be done earlier
|
|
|
|
* in get_directory_renames(), except that would prevent us from
|
|
|
|
* doing the previous check and thus failing testcase 6b.)
|
|
|
|
* 3. Check for rename/rename(1to2) conflicts (at the directory level).
|
|
|
|
* In the future, we could potentially record this info as well and
|
|
|
|
* omit reporting rename/rename(1to2) conflicts for each path within
|
|
|
|
* the affected directories, thus cleaning up the merge output.
|
|
|
|
* NOTE: We do NOT check for rename/rename(2to1) conflicts at the
|
|
|
|
* directory level, because merging directories is fine. If it
|
|
|
|
* causes conflicts for files within those merged directories, then
|
|
|
|
* that should be detected at the individual path level.
|
|
|
|
*/
|
|
|
|
static void handle_directory_level_conflicts(struct merge_options *o,
|
|
|
|
struct hashmap *dir_re_head,
|
|
|
|
struct tree *head,
|
|
|
|
struct hashmap *dir_re_merge,
|
|
|
|
struct tree *merge)
|
|
|
|
{
|
|
|
|
struct hashmap_iter iter;
|
|
|
|
struct dir_rename_entry *head_ent;
|
|
|
|
struct dir_rename_entry *merge_ent;
|
|
|
|
|
|
|
|
struct string_list remove_from_head = STRING_LIST_INIT_NODUP;
|
|
|
|
struct string_list remove_from_merge = STRING_LIST_INIT_NODUP;
|
|
|
|
|
|
|
|
hashmap_iter_init(dir_re_head, &iter);
|
|
|
|
while ((head_ent = hashmap_iter_next(&iter))) {
|
|
|
|
merge_ent = dir_rename_find_entry(dir_re_merge, head_ent->dir);
|
|
|
|
if (merge_ent &&
|
|
|
|
!head_ent->non_unique_new_dir &&
|
|
|
|
!merge_ent->non_unique_new_dir &&
|
|
|
|
!strbuf_cmp(&head_ent->new_dir, &merge_ent->new_dir)) {
|
|
|
|
/* 1. Renamed identically; remove it from both sides */
|
|
|
|
string_list_append(&remove_from_head,
|
|
|
|
head_ent->dir)->util = head_ent;
|
|
|
|
strbuf_release(&head_ent->new_dir);
|
|
|
|
string_list_append(&remove_from_merge,
|
|
|
|
merge_ent->dir)->util = merge_ent;
|
|
|
|
strbuf_release(&merge_ent->new_dir);
|
|
|
|
} else if (tree_has_path(head, head_ent->dir)) {
|
|
|
|
/* 2. This wasn't a directory rename after all */
|
|
|
|
string_list_append(&remove_from_head,
|
|
|
|
head_ent->dir)->util = head_ent;
|
|
|
|
strbuf_release(&head_ent->new_dir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
remove_hashmap_entries(dir_re_head, &remove_from_head);
|
|
|
|
remove_hashmap_entries(dir_re_merge, &remove_from_merge);
|
|
|
|
|
|
|
|
hashmap_iter_init(dir_re_merge, &iter);
|
|
|
|
while ((merge_ent = hashmap_iter_next(&iter))) {
|
|
|
|
head_ent = dir_rename_find_entry(dir_re_head, merge_ent->dir);
|
|
|
|
if (tree_has_path(merge, merge_ent->dir)) {
|
|
|
|
/* 2. This wasn't a directory rename after all */
|
|
|
|
string_list_append(&remove_from_merge,
|
|
|
|
merge_ent->dir)->util = merge_ent;
|
|
|
|
} else if (head_ent &&
|
|
|
|
!head_ent->non_unique_new_dir &&
|
|
|
|
!merge_ent->non_unique_new_dir) {
|
|
|
|
/* 3. rename/rename(1to2) */
|
|
|
|
/*
|
|
|
|
* We can assume it's not rename/rename(1to1) because
|
|
|
|
* that was case (1), already checked above. So we
|
|
|
|
* know that head_ent->new_dir and merge_ent->new_dir
|
|
|
|
* are different strings.
|
|
|
|
*/
|
|
|
|
output(o, 1, _("CONFLICT (rename/rename): "
|
|
|
|
"Rename directory %s->%s in %s. "
|
|
|
|
"Rename directory %s->%s in %s"),
|
|
|
|
head_ent->dir, head_ent->new_dir.buf, o->branch1,
|
|
|
|
head_ent->dir, merge_ent->new_dir.buf, o->branch2);
|
|
|
|
string_list_append(&remove_from_head,
|
|
|
|
head_ent->dir)->util = head_ent;
|
|
|
|
strbuf_release(&head_ent->new_dir);
|
|
|
|
string_list_append(&remove_from_merge,
|
|
|
|
merge_ent->dir)->util = merge_ent;
|
|
|
|
strbuf_release(&merge_ent->new_dir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
remove_hashmap_entries(dir_re_head, &remove_from_head);
|
|
|
|
remove_hashmap_entries(dir_re_merge, &remove_from_merge);
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:05 +08:00
|
|
|
static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs,
|
|
|
|
struct tree *tree)
|
|
|
|
{
|
|
|
|
struct hashmap *dir_renames;
|
|
|
|
struct hashmap_iter iter;
|
|
|
|
struct dir_rename_entry *entry;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Typically, we think of a directory rename as all files from a
|
|
|
|
* certain directory being moved to a target directory. However,
|
|
|
|
* what if someone first moved two files from the original
|
|
|
|
* directory in one commit, and then renamed the directory
|
|
|
|
* somewhere else in a later commit? At merge time, we just know
|
|
|
|
* that files from the original directory went to two different
|
|
|
|
* places, and that the bulk of them ended up in the same place.
|
|
|
|
* We want each directory rename to represent where the bulk of the
|
|
|
|
* files from that directory end up; this function exists to find
|
|
|
|
* where the bulk of the files went.
|
|
|
|
*
|
|
|
|
* The first loop below simply iterates through the list of file
|
|
|
|
* renames, finding out how often each directory rename pair
|
|
|
|
* possibility occurs.
|
|
|
|
*/
|
|
|
|
dir_renames = xmalloc(sizeof(*dir_renames));
|
|
|
|
dir_rename_init(dir_renames);
|
|
|
|
for (i = 0; i < pairs->nr; ++i) {
|
|
|
|
struct string_list_item *item;
|
|
|
|
int *count;
|
|
|
|
struct diff_filepair *pair = pairs->queue[i];
|
|
|
|
char *old_dir, *new_dir;
|
|
|
|
|
|
|
|
/* File not part of directory rename if it wasn't renamed */
|
|
|
|
if (pair->status != 'R')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
get_renamed_dir_portion(pair->one->path, pair->two->path,
|
|
|
|
&old_dir, &new_dir);
|
|
|
|
if (!old_dir)
|
|
|
|
/* Directory didn't change at all; ignore this one. */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
entry = dir_rename_find_entry(dir_renames, old_dir);
|
|
|
|
if (!entry) {
|
|
|
|
entry = xmalloc(sizeof(*entry));
|
|
|
|
dir_rename_entry_init(entry, old_dir);
|
|
|
|
hashmap_put(dir_renames, entry);
|
|
|
|
} else {
|
|
|
|
free(old_dir);
|
|
|
|
}
|
|
|
|
item = string_list_lookup(&entry->possible_new_dirs, new_dir);
|
|
|
|
if (!item) {
|
|
|
|
item = string_list_insert(&entry->possible_new_dirs,
|
|
|
|
new_dir);
|
|
|
|
item->util = xcalloc(1, sizeof(int));
|
|
|
|
} else {
|
|
|
|
free(new_dir);
|
|
|
|
}
|
|
|
|
count = item->util;
|
|
|
|
*count += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For each directory with files moved out of it, we find out which
|
|
|
|
* target directory received the most files so we can declare it to
|
|
|
|
* be the "winning" target location for the directory rename. This
|
|
|
|
* winner gets recorded in new_dir. If there is no winner
|
|
|
|
* (multiple target directories received the same number of files),
|
|
|
|
* we set non_unique_new_dir. Once we've determined the winner (or
|
|
|
|
* that there is no winner), we no longer need possible_new_dirs.
|
|
|
|
*/
|
|
|
|
hashmap_iter_init(dir_renames, &iter);
|
|
|
|
while ((entry = hashmap_iter_next(&iter))) {
|
|
|
|
int max = 0;
|
|
|
|
int bad_max = 0;
|
|
|
|
char *best = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < entry->possible_new_dirs.nr; i++) {
|
|
|
|
int *count = entry->possible_new_dirs.items[i].util;
|
|
|
|
|
|
|
|
if (*count == max)
|
|
|
|
bad_max = max;
|
|
|
|
else if (*count > max) {
|
|
|
|
max = *count;
|
|
|
|
best = entry->possible_new_dirs.items[i].string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bad_max == max)
|
|
|
|
entry->non_unique_new_dir = 1;
|
|
|
|
else {
|
|
|
|
assert(entry->new_dir.len == 0);
|
|
|
|
strbuf_addstr(&entry->new_dir, best);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* The relevant directory sub-portion of the original full
|
|
|
|
* filepaths were xstrndup'ed before inserting into
|
|
|
|
* possible_new_dirs, and instead of manually iterating the
|
|
|
|
* list and free'ing each, just lie and tell
|
|
|
|
* possible_new_dirs that it did the strdup'ing so that it
|
|
|
|
* will free them for us.
|
|
|
|
*/
|
|
|
|
entry->possible_new_dirs.strdup_strings = 1;
|
|
|
|
string_list_clear(&entry->possible_new_dirs, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dir_renames;
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:07 +08:00
|
|
|
static struct dir_rename_entry *check_dir_renamed(const char *path,
|
|
|
|
struct hashmap *dir_renames)
|
|
|
|
{
|
|
|
|
char temp[PATH_MAX];
|
|
|
|
char *end;
|
|
|
|
struct dir_rename_entry *entry;
|
|
|
|
|
|
|
|
strcpy(temp, path);
|
|
|
|
while ((end = strrchr(temp, '/'))) {
|
|
|
|
*end = '\0';
|
|
|
|
entry = dir_rename_find_entry(dir_renames, temp);
|
|
|
|
if (entry)
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void compute_collisions(struct hashmap *collisions,
|
|
|
|
struct hashmap *dir_renames,
|
|
|
|
struct diff_queue_struct *pairs)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Multiple files can be mapped to the same path due to directory
|
|
|
|
* renames done by the other side of history. Since that other
|
|
|
|
* side of history could have merged multiple directories into one,
|
|
|
|
* if our side of history added the same file basename to each of
|
|
|
|
* those directories, then all N of them would get implicitly
|
|
|
|
* renamed by the directory rename detection into the same path,
|
|
|
|
* and we'd get an add/add/.../add conflict, and all those adds
|
|
|
|
* from *this* side of history. This is not representable in the
|
|
|
|
* index, and users aren't going to easily be able to make sense of
|
|
|
|
* it. So we need to provide a good warning about what's
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
collision_init(collisions);
|
|
|
|
|
|
|
|
for (i = 0; i < pairs->nr; ++i) {
|
|
|
|
struct dir_rename_entry *dir_rename_ent;
|
|
|
|
struct collision_entry *collision_ent;
|
|
|
|
char *new_path;
|
|
|
|
struct diff_filepair *pair = pairs->queue[i];
|
|
|
|
|
2018-04-20 01:58:15 +08:00
|
|
|
if (pair->status != 'A' && pair->status != 'R')
|
2018-04-20 01:58:07 +08:00
|
|
|
continue;
|
|
|
|
dir_rename_ent = check_dir_renamed(pair->two->path,
|
|
|
|
dir_renames);
|
|
|
|
if (!dir_rename_ent)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
new_path = apply_dir_rename(dir_rename_ent, pair->two->path);
|
|
|
|
if (!new_path)
|
|
|
|
/*
|
|
|
|
* dir_rename_ent->non_unique_new_path is true, which
|
|
|
|
* means there is no directory rename for us to use,
|
|
|
|
* which means it won't cause us any additional
|
|
|
|
* collisions.
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
collision_ent = collision_find_entry(collisions, new_path);
|
|
|
|
if (!collision_ent) {
|
|
|
|
collision_ent = xcalloc(1,
|
|
|
|
sizeof(struct collision_entry));
|
|
|
|
hashmap_entry_init(collision_ent, strhash(new_path));
|
|
|
|
hashmap_put(collisions, collision_ent);
|
|
|
|
collision_ent->target_file = new_path;
|
|
|
|
} else {
|
|
|
|
free(new_path);
|
|
|
|
}
|
|
|
|
string_list_insert(&collision_ent->source_files,
|
|
|
|
pair->two->path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:08 +08:00
|
|
|
static char *check_for_directory_rename(struct merge_options *o,
|
|
|
|
const char *path,
|
|
|
|
struct tree *tree,
|
|
|
|
struct hashmap *dir_renames,
|
|
|
|
struct hashmap *dir_rename_exclusions,
|
|
|
|
struct hashmap *collisions,
|
|
|
|
int *clean_merge)
|
|
|
|
{
|
|
|
|
char *new_path = NULL;
|
|
|
|
struct dir_rename_entry *entry = check_dir_renamed(path, dir_renames);
|
|
|
|
struct dir_rename_entry *oentry = NULL;
|
|
|
|
|
|
|
|
if (!entry)
|
|
|
|
return new_path;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This next part is a little weird. We do not want to do an
|
|
|
|
* implicit rename into a directory we renamed on our side, because
|
|
|
|
* that will result in a spurious rename/rename(1to2) conflict. An
|
|
|
|
* example:
|
|
|
|
* Base commit: dumbdir/afile, otherdir/bfile
|
|
|
|
* Side 1: smrtdir/afile, otherdir/bfile
|
|
|
|
* Side 2: dumbdir/afile, dumbdir/bfile
|
|
|
|
* Here, while working on Side 1, we could notice that otherdir was
|
|
|
|
* renamed/merged to dumbdir, and change the diff_filepair for
|
|
|
|
* otherdir/bfile into a rename into dumbdir/bfile. However, Side
|
|
|
|
* 2 will notice the rename from dumbdir to smrtdir, and do the
|
|
|
|
* transitive rename to move it from dumbdir/bfile to
|
|
|
|
* smrtdir/bfile. That gives us bfile in dumbdir vs being in
|
|
|
|
* smrtdir, a rename/rename(1to2) conflict. We really just want
|
|
|
|
* the file to end up in smrtdir. And the way to achieve that is
|
|
|
|
* 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 oentry and dir_rename_exclusions is here.
|
|
|
|
*
|
|
|
|
* As it turns out, this also prevents N-way transient rename
|
|
|
|
* confusion; See testcases 9c and 9d of t6043.
|
|
|
|
*/
|
|
|
|
oentry = dir_rename_find_entry(dir_rename_exclusions, entry->new_dir.buf);
|
|
|
|
if (oentry) {
|
|
|
|
output(o, 1, _("WARNING: Avoiding applying %s -> %s rename "
|
|
|
|
"to %s, because %s itself was renamed."),
|
|
|
|
entry->dir, entry->new_dir.buf, path, entry->new_dir.buf);
|
|
|
|
} else {
|
|
|
|
new_path = handle_path_level_conflicts(o, path, entry,
|
|
|
|
collisions, tree);
|
|
|
|
*clean_merge &= (new_path != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new_path;
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:10 +08:00
|
|
|
static void apply_directory_rename_modifications(struct merge_options *o,
|
|
|
|
struct diff_filepair *pair,
|
|
|
|
char *new_path,
|
|
|
|
struct rename *re,
|
|
|
|
struct tree *tree,
|
|
|
|
struct tree *o_tree,
|
|
|
|
struct tree *a_tree,
|
|
|
|
struct tree *b_tree,
|
|
|
|
struct string_list *entries,
|
|
|
|
int *clean)
|
|
|
|
{
|
|
|
|
struct string_list_item *item;
|
|
|
|
int stage = (tree == a_tree ? 2 : 3);
|
2018-04-20 01:58:13 +08:00
|
|
|
int update_wd;
|
2018-04-20 01:58:10 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* In all cases where we can do directory rename detection,
|
|
|
|
* unpack_trees() will have read pair->two->path into the
|
|
|
|
* index and the working copy. We need to remove it so that
|
|
|
|
* we can instead place it at new_path. It is guaranteed to
|
|
|
|
* not be untracked (unpack_trees() would have errored out
|
|
|
|
* saying the file would have been overwritten), but it might
|
|
|
|
* be dirty, though.
|
|
|
|
*/
|
2018-04-20 01:58:13 +08:00
|
|
|
update_wd = !was_dirty(o, pair->two->path);
|
|
|
|
if (!update_wd)
|
|
|
|
output(o, 1, _("Refusing to lose dirty file at %s"),
|
|
|
|
pair->two->path);
|
|
|
|
remove_file(o, 1, pair->two->path, !update_wd);
|
2018-04-20 01:58:10 +08:00
|
|
|
|
|
|
|
/* Find or create a new re->dst_entry */
|
|
|
|
item = string_list_lookup(entries, new_path);
|
|
|
|
if (item) {
|
|
|
|
/*
|
|
|
|
* Since we're renaming on this side of history, and it's
|
|
|
|
* due to a directory rename on the other side of history
|
|
|
|
* (which we only allow when the directory in question no
|
|
|
|
* longer exists on the other side of history), the
|
|
|
|
* original entry for re->dst_entry is no longer
|
|
|
|
* necessary...
|
|
|
|
*/
|
|
|
|
re->dst_entry->processed = 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ...because we'll be using this new one.
|
|
|
|
*/
|
|
|
|
re->dst_entry = item->util;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* re->dst_entry is for the before-dir-rename path, and we
|
|
|
|
* need it to hold information for the after-dir-rename
|
|
|
|
* path. Before creating a new entry, we need to mark the
|
|
|
|
* old one as unnecessary (...unless it is shared by
|
|
|
|
* src_entry, i.e. this didn't use to be a rename, in which
|
|
|
|
* case we can just allow the normal processing to happen
|
|
|
|
* for it).
|
|
|
|
*/
|
|
|
|
if (pair->status == 'R')
|
|
|
|
re->dst_entry->processed = 1;
|
|
|
|
|
|
|
|
re->dst_entry = insert_stage_data(new_path,
|
|
|
|
o_tree, a_tree, b_tree,
|
|
|
|
entries);
|
|
|
|
item = string_list_insert(entries, new_path);
|
|
|
|
item->util = re->dst_entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the stage_data with the information about the path we are
|
|
|
|
* moving into place. That slot will be empty and available for us
|
|
|
|
* to write to because of the collision checks in
|
|
|
|
* handle_path_level_conflicts(). In other words,
|
|
|
|
* re->dst_entry->stages[stage].oid will be the null_oid, so it's
|
|
|
|
* open for us to write to.
|
|
|
|
*
|
|
|
|
* It may be tempting to actually update the index at this point as
|
|
|
|
* well, using update_stages_for_stage_data(), but as per the big
|
|
|
|
* "NOTE" in update_stages(), doing so will modify the current
|
|
|
|
* in-memory index which will break calls to would_lose_untracked()
|
|
|
|
* that we need to make. Instead, we need to just make sure that
|
|
|
|
* the various conflict_rename_*() functions update the index
|
|
|
|
* explicitly rather than relying on unpack_trees() to have done it.
|
|
|
|
*/
|
|
|
|
get_tree_entry(&tree->object.oid,
|
|
|
|
pair->two->path,
|
|
|
|
&re->dst_entry->stages[stage].oid,
|
|
|
|
&re->dst_entry->stages[stage].mode);
|
|
|
|
|
|
|
|
/* Update pair status */
|
|
|
|
if (pair->status == 'A') {
|
|
|
|
/*
|
|
|
|
* Recording rename information for this add makes it look
|
|
|
|
* like a rename/delete conflict. Make sure we can
|
|
|
|
* correctly handle this as an add that was moved to a new
|
|
|
|
* directory instead of reporting a rename/delete conflict.
|
|
|
|
*/
|
|
|
|
re->add_turned_into_rename = 1;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We don't actually look at pair->status again, but it seems
|
|
|
|
* pedagogically correct to adjust it.
|
|
|
|
*/
|
|
|
|
pair->status = 'R';
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finally, record the new location.
|
|
|
|
*/
|
|
|
|
pair->two->path = new_path;
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:03 +08:00
|
|
|
/*
|
|
|
|
* Get information of all renames which occurred in 'pairs', making use of
|
|
|
|
* any implicit directory renames inferred from the other side of history.
|
|
|
|
* We need the three trees in the merge ('o_tree', 'a_tree' and 'b_tree')
|
|
|
|
* to be able to associate the correct cache entries with the rename
|
|
|
|
* information; tree is always equal to either a_tree or b_tree.
|
|
|
|
*/
|
|
|
|
static struct string_list *get_renames(struct merge_options *o,
|
|
|
|
struct diff_queue_struct *pairs,
|
2018-04-20 01:58:07 +08:00
|
|
|
struct hashmap *dir_renames,
|
2018-04-20 01:58:08 +08:00
|
|
|
struct hashmap *dir_rename_exclusions,
|
2018-04-20 01:58:03 +08:00
|
|
|
struct tree *tree,
|
|
|
|
struct tree *o_tree,
|
|
|
|
struct tree *a_tree,
|
|
|
|
struct tree *b_tree,
|
2018-04-20 01:58:08 +08:00
|
|
|
struct string_list *entries,
|
|
|
|
int *clean_merge)
|
2018-04-20 01:58:03 +08:00
|
|
|
{
|
|
|
|
int i;
|
2018-04-20 01:58:07 +08:00
|
|
|
struct hashmap collisions;
|
|
|
|
struct hashmap_iter iter;
|
|
|
|
struct collision_entry *e;
|
2018-04-20 01:58:03 +08:00
|
|
|
struct string_list *renames;
|
|
|
|
|
2018-04-20 01:58:07 +08:00
|
|
|
compute_collisions(&collisions, dir_renames, pairs);
|
2018-04-20 01:58:03 +08:00
|
|
|
renames = xcalloc(1, sizeof(struct string_list));
|
|
|
|
|
|
|
|
for (i = 0; i < pairs->nr; ++i) {
|
2018-04-20 01:57:59 +08:00
|
|
|
struct string_list_item *item;
|
|
|
|
struct rename *re;
|
2018-04-20 01:58:03 +08:00
|
|
|
struct diff_filepair *pair = pairs->queue[i];
|
2018-04-20 01:58:08 +08:00
|
|
|
char *new_path; /* non-NULL only with directory renames */
|
2018-04-20 01:57:59 +08:00
|
|
|
|
2018-04-20 01:58:15 +08:00
|
|
|
if (pair->status != 'A' && pair->status != 'R') {
|
2018-04-20 01:57:59 +08:00
|
|
|
diff_free_filepair(pair);
|
|
|
|
continue;
|
|
|
|
}
|
2018-04-20 01:58:08 +08:00
|
|
|
new_path = check_for_directory_rename(o, pair->two->path, tree,
|
|
|
|
dir_renames,
|
|
|
|
dir_rename_exclusions,
|
|
|
|
&collisions,
|
|
|
|
clean_merge);
|
|
|
|
if (pair->status != 'R' && !new_path) {
|
|
|
|
diff_free_filepair(pair);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:57:59 +08:00
|
|
|
re = xmalloc(sizeof(*re));
|
|
|
|
re->processed = 0;
|
2018-04-20 01:58:10 +08:00
|
|
|
re->add_turned_into_rename = 0;
|
2018-04-20 01:57:59 +08:00
|
|
|
re->pair = pair;
|
|
|
|
item = string_list_lookup(entries, re->pair->one->path);
|
|
|
|
if (!item)
|
|
|
|
re->src_entry = insert_stage_data(re->pair->one->path,
|
|
|
|
o_tree, a_tree, b_tree, entries);
|
|
|
|
else
|
|
|
|
re->src_entry = item->util;
|
|
|
|
|
|
|
|
item = string_list_lookup(entries, re->pair->two->path);
|
|
|
|
if (!item)
|
|
|
|
re->dst_entry = insert_stage_data(re->pair->two->path,
|
|
|
|
o_tree, a_tree, b_tree, entries);
|
|
|
|
else
|
|
|
|
re->dst_entry = item->util;
|
|
|
|
item = string_list_insert(renames, pair->one->path);
|
|
|
|
item->util = re;
|
2018-04-20 01:58:10 +08:00
|
|
|
if (new_path)
|
|
|
|
apply_directory_rename_modifications(o, pair, new_path,
|
|
|
|
re, tree, o_tree,
|
|
|
|
a_tree, b_tree,
|
|
|
|
entries,
|
|
|
|
clean_merge);
|
2018-04-20 01:57:59 +08:00
|
|
|
}
|
2018-04-20 01:58:07 +08:00
|
|
|
|
|
|
|
hashmap_iter_init(&collisions, &iter);
|
|
|
|
while ((e = hashmap_iter_next(&iter))) {
|
|
|
|
free(e->target_file);
|
|
|
|
string_list_clear(&e->source_files, 0);
|
|
|
|
}
|
|
|
|
hashmap_free(&collisions, 1);
|
2018-04-20 01:57:59 +08:00
|
|
|
return renames;
|
|
|
|
}
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
static int process_renames(struct merge_options *o,
|
|
|
|
struct string_list *a_renames,
|
|
|
|
struct string_list *b_renames)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
|
|
|
int clean_merge = 1, i, j;
|
2010-07-05 03:46:19 +08:00
|
|
|
struct string_list a_by_dst = STRING_LIST_INIT_NODUP;
|
|
|
|
struct string_list b_by_dst = STRING_LIST_INIT_NODUP;
|
2008-08-13 00:45:14 +08:00
|
|
|
const struct rename *sre;
|
|
|
|
|
|
|
|
for (i = 0; i < a_renames->nr; i++) {
|
|
|
|
sre = a_renames->items[i].util;
|
2010-06-26 07:41:35 +08:00
|
|
|
string_list_insert(&a_by_dst, sre->pair->two->path)->util
|
2011-08-12 13:20:04 +08:00
|
|
|
= (void *)sre;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
for (i = 0; i < b_renames->nr; i++) {
|
|
|
|
sre = b_renames->items[i].util;
|
2010-06-26 07:41:35 +08:00
|
|
|
string_list_insert(&b_by_dst, sre->pair->two->path)->util
|
2011-08-12 13:20:04 +08:00
|
|
|
= (void *)sre;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
|
2009-03-16 05:01:20 +08:00
|
|
|
struct string_list *renames1, *renames2Dst;
|
2008-08-13 00:45:14 +08:00
|
|
|
struct rename *ren1 = NULL, *ren2 = NULL;
|
|
|
|
const char *branch1, *branch2;
|
|
|
|
const char *ren1_src, *ren1_dst;
|
2011-08-12 13:20:15 +08:00
|
|
|
struct string_list_item *lookup;
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
if (i >= a_renames->nr) {
|
|
|
|
ren2 = b_renames->items[j++].util;
|
|
|
|
} else if (j >= b_renames->nr) {
|
|
|
|
ren1 = a_renames->items[i++].util;
|
|
|
|
} else {
|
2009-03-16 05:01:20 +08:00
|
|
|
int compare = strcmp(a_renames->items[i].string,
|
|
|
|
b_renames->items[j].string);
|
2008-08-13 00:45:14 +08:00
|
|
|
if (compare <= 0)
|
|
|
|
ren1 = a_renames->items[i++].util;
|
|
|
|
if (compare >= 0)
|
|
|
|
ren2 = b_renames->items[j++].util;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: refactor, so that 1/2 are not needed */
|
|
|
|
if (ren1) {
|
|
|
|
renames1 = a_renames;
|
|
|
|
renames2Dst = &b_by_dst;
|
2008-08-25 22:25:57 +08:00
|
|
|
branch1 = o->branch1;
|
|
|
|
branch2 = o->branch2;
|
2008-08-13 00:45:14 +08:00
|
|
|
} else {
|
|
|
|
renames1 = b_renames;
|
|
|
|
renames2Dst = &a_by_dst;
|
2008-08-25 22:25:57 +08:00
|
|
|
branch1 = o->branch2;
|
|
|
|
branch2 = o->branch1;
|
2017-01-29 05:40:58 +08:00
|
|
|
SWAP(ren2, ren1);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ren1->processed)
|
|
|
|
continue;
|
|
|
|
ren1->processed = 1;
|
|
|
|
ren1->dst_entry->processed = 1;
|
2011-08-12 13:20:05 +08:00
|
|
|
/* BUG: We should only mark src_entry as processed if we
|
|
|
|
* are not dealing with a rename + add-source case.
|
|
|
|
*/
|
2008-08-13 00:45:14 +08:00
|
|
|
ren1->src_entry->processed = 1;
|
|
|
|
|
|
|
|
ren1_src = ren1->pair->one->path;
|
|
|
|
ren1_dst = ren1->pair->two->path;
|
|
|
|
|
|
|
|
if (ren2) {
|
2011-08-12 13:20:15 +08:00
|
|
|
/* One file renamed on both sides */
|
2008-08-13 00:45:14 +08:00
|
|
|
const char *ren2_src = ren2->pair->one->path;
|
|
|
|
const char *ren2_dst = ren2->pair->two->path;
|
2011-08-12 13:20:08 +08:00
|
|
|
enum rename_type rename_type;
|
2008-08-13 00:45:14 +08:00
|
|
|
if (strcmp(ren1_src, ren2_src) != 0)
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("ren1_src != ren2_src");
|
2008-08-13 00:45:14 +08:00
|
|
|
ren2->dst_entry->processed = 1;
|
|
|
|
ren2->processed = 1;
|
|
|
|
if (strcmp(ren1_dst, ren2_dst) != 0) {
|
2011-08-12 13:20:08 +08:00
|
|
|
rename_type = RENAME_ONE_FILE_TO_TWO;
|
2011-08-12 13:20:15 +08:00
|
|
|
clean_merge = 0;
|
2008-08-13 00:45:14 +08:00
|
|
|
} else {
|
2011-08-12 13:20:08 +08:00
|
|
|
rename_type = RENAME_ONE_FILE_TO_ONE;
|
2011-08-12 13:20:05 +08:00
|
|
|
/* BUG: We should only remove ren1_src in
|
|
|
|
* the base stage (think of rename +
|
|
|
|
* add-source cases).
|
|
|
|
*/
|
2008-09-03 05:53:47 +08:00
|
|
|
remove_file(o, 1, ren1_src, 1);
|
2011-08-12 13:20:02 +08:00
|
|
|
update_entry(ren1->dst_entry,
|
|
|
|
ren1->pair->one,
|
|
|
|
ren1->pair->two,
|
|
|
|
ren2->pair->two);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
2011-08-12 13:20:08 +08:00
|
|
|
setup_rename_conflict_info(rename_type,
|
|
|
|
ren1->pair,
|
|
|
|
ren2->pair,
|
|
|
|
branch1,
|
|
|
|
branch2,
|
|
|
|
ren1->dst_entry,
|
2011-08-12 13:20:16 +08:00
|
|
|
ren2->dst_entry,
|
|
|
|
o,
|
|
|
|
NULL,
|
|
|
|
NULL);
|
2011-08-12 13:20:15 +08:00
|
|
|
} else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) {
|
|
|
|
/* Two different files renamed to the same thing */
|
|
|
|
char *ren2_dst;
|
|
|
|
ren2 = lookup->util;
|
|
|
|
ren2_dst = ren2->pair->two->path;
|
|
|
|
if (strcmp(ren1_dst, ren2_dst) != 0)
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("ren1_dst != ren2_dst");
|
2011-08-12 13:20:15 +08:00
|
|
|
|
|
|
|
clean_merge = 0;
|
|
|
|
ren2->processed = 1;
|
|
|
|
/*
|
|
|
|
* BUG: We should only mark src_entry as processed
|
|
|
|
* if we are not dealing with a rename + add-source
|
|
|
|
* case.
|
|
|
|
*/
|
|
|
|
ren2->src_entry->processed = 1;
|
|
|
|
|
|
|
|
setup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE,
|
|
|
|
ren1->pair,
|
|
|
|
ren2->pair,
|
|
|
|
branch1,
|
|
|
|
branch2,
|
|
|
|
ren1->dst_entry,
|
2011-08-12 13:20:16 +08:00
|
|
|
ren2->dst_entry,
|
|
|
|
o,
|
|
|
|
ren1->src_entry,
|
|
|
|
ren2->src_entry);
|
|
|
|
|
2008-08-13 00:45:14 +08:00
|
|
|
} else {
|
|
|
|
/* Renamed in 1, maybe changed in 2 */
|
|
|
|
/* we only use sha1 and mode of these */
|
|
|
|
struct diff_filespec src_other, dst_other;
|
2010-09-20 16:28:47 +08:00
|
|
|
int try_merge;
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2010-09-20 16:28:47 +08:00
|
|
|
/*
|
|
|
|
* unpack_trees loads entries from common-commit
|
|
|
|
* into stage 1, from head-commit into stage 2, and
|
|
|
|
* from merge-commit into stage 3. We keep track
|
|
|
|
* of which side corresponds to the rename.
|
|
|
|
*/
|
|
|
|
int renamed_stage = a_renames == renames1 ? 2 : 3;
|
|
|
|
int other_stage = a_renames == renames1 ? 3 : 2;
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2011-08-12 13:20:05 +08:00
|
|
|
/* BUG: We should only remove ren1_src in the base
|
|
|
|
* stage and in other_stage (think of rename +
|
|
|
|
* add-source case).
|
|
|
|
*/
|
merge-recursive: Fix deletion of untracked file in rename/delete conflicts
In the recursive case (o->call_depth > 0), we do not modify the working
directory. However, when o->call_depth==0, file renames can mean we need
to delete the old filename from the working copy. Since there have been
lots of changes and mistakes here, let's go through the details. Let's
start with a simple explanation of what we are trying to achieve:
Original goal: If a file is renamed on the side of history being merged
into head, the filename serving as the source of that rename needs to be
removed from the working directory.
The path to getting the above statement implemented in merge-recursive took
several steps. The relevant bits of code may be instructive to keep in
mind for the explanation, especially since an English-only description
involves double negatives that are hard to follow. These bits of code are:
int remove_file(..., const char *path, int no_wd)
{
...
int update_working_directory = !o->call_depth && !no_wd;
and
remove_file(o, 1, ren1_src, <expression>);
Where the choice for <expression> has morphed over time:
65ac6e9 (merge-recursive: adjust to loosened "working file clobbered"
check 2006-10-27), introduced the "no_wd" parameter to remove_file() and
used "1" for <expression>. This meant ren1_src was never deleted, leaving
it around in the working copy.
In 8371234 (Remove uncontested renamed files during merge. 2006-12-13),
<expression> was changed to "index_only" (where index_only ==
!!o->call_depth; see b7fa51da). This was equivalent to using "0" for
<expression> (due to the early logic in remove_file), and is orthogonal to
the condition we actually want to check at this point; it resulted in the
source file being removed except when index_only was false. This was
problematic because the file could have been renamed on the side of history
including head, in which case ren1_src could correspond to an untracked
file that should not be deleted.
In 183d797 (Keep untracked files not involved in a merge. 2007-02-04),
<expression> was changed to "index_only || stage == 3". While this gives
correct behavior, the "index_only ||" portion of <expression> is
unnecessary and makes the code slightly harder to follow.
There were also two further changes to this expression, though without
any change in behavior. First in b7fa51d (merge-recursive: get rid of the
index_only global variable 2008-09-02), it was changed to "o->call_depth
|| stage == 3". (index_only == !!o->call_depth). Later, in 41d70bd6
(merge-recursive: Small code clarification -- variable name and comments),
this was changed to "o->call_depth || renamed_stage == 2" (where stage was
renamed to other_stage and renamed_stage == other_stage ^ 1).
So we ended with <expression> being "o->call_depth || renamed_stage == 2".
But the "o->call_depth ||" piece was unnecessary. We can remove it,
leaving us with <expression> being "renamed_stage == 2". This doesn't
change behavior at all, but it makes the code clearer. Which is good,
because it's about to get uglier.
Corrected goal: If a file is renamed on the side of history being merged
into head, the filename serving as the source of that rename needs to be
removed from the working directory *IF* that file is tracked in head AND
the file tracked in head is related to the original file.
Note that the only difference between the original goal and the corrected
goal is the two extra conditions added at the end. The first condition is
relevant in a rename/delete conflict. If the file was deleted on the
HEAD side of the merge and an untracked file of the same name was added to
the working copy, then without that extra condition the untracked file
will be erroneously deleted. This changes <expression> to "renamed_stage
== 2 || !was_tracked(ren1_src)".
The second additional condition is relevant in two cases.
The first case the second condition can occur is when a file is deleted
and a completely different file is added with the same name. To my
knowledge, merge-recursive has no mechanism for detecting deleted-and-
replaced-by-different-file cases, so I am simply punting on this
possibility.
The second case for the second condition to occur is when there is a
rename/rename/add-source conflict. That is, when the original file was
renamed on both sides of history AND the original filename is being
re-used by some unrelated (but tracked) content. This case also presents
some additional difficulties for us since we cannot currently detect these
rename/rename/add-source conflicts; as long as the rename detection logic
"optimizes" by ignoring filenames that are present at both ends of the
diff, these conflicts will go unnoticed. However, rename/rename conflicts
are handled by an entirely separate codepath not being discussed here, so
this case is not relevant for the line of code under consideration.
In summary:
Change <expression> from "o->call_depth || renamed_stage == 2" to
"renamed_stage == 2 || !was_tracked(ren1_src)", in order to remove
unnecessary code and avoid deleting untracked files.
96 lines of explanation in the changelog to describe a one-line fix...
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-08-12 13:20:03 +08:00
|
|
|
remove_file(o, 1, ren1_src,
|
2018-04-20 01:58:20 +08:00
|
|
|
renamed_stage == 2 || !was_tracked(o, ren1_src));
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2016-06-25 07:09:25 +08:00
|
|
|
oidcpy(&src_other.oid,
|
|
|
|
&ren1->src_entry->stages[other_stage].oid);
|
2010-09-20 16:28:47 +08:00
|
|
|
src_other.mode = ren1->src_entry->stages[other_stage].mode;
|
2016-06-25 07:09:25 +08:00
|
|
|
oidcpy(&dst_other.oid,
|
|
|
|
&ren1->dst_entry->stages[other_stage].oid);
|
2010-09-20 16:28:47 +08:00
|
|
|
dst_other.mode = ren1->dst_entry->stages[other_stage].mode;
|
2008-08-13 00:45:14 +08:00
|
|
|
try_merge = 0;
|
|
|
|
|
2018-04-20 01:58:10 +08:00
|
|
|
if (oid_eq(&src_other.oid, &null_oid) &&
|
|
|
|
ren1->add_turned_into_rename) {
|
|
|
|
setup_rename_conflict_info(RENAME_DIR,
|
|
|
|
ren1->pair,
|
|
|
|
NULL,
|
|
|
|
branch1,
|
|
|
|
branch2,
|
|
|
|
ren1->dst_entry,
|
|
|
|
NULL,
|
|
|
|
o,
|
|
|
|
NULL,
|
|
|
|
NULL);
|
|
|
|
} else if (oid_eq(&src_other.oid, &null_oid)) {
|
2011-08-12 13:20:08 +08:00
|
|
|
setup_rename_conflict_info(RENAME_DELETE,
|
|
|
|
ren1->pair,
|
|
|
|
NULL,
|
|
|
|
branch1,
|
|
|
|
branch2,
|
|
|
|
ren1->dst_entry,
|
2011-08-12 13:20:16 +08:00
|
|
|
NULL,
|
|
|
|
o,
|
|
|
|
NULL,
|
2011-08-12 13:20:08 +08:00
|
|
|
NULL);
|
2010-09-02 04:15:32 +08:00
|
|
|
} else if ((dst_other.mode == ren1->pair->two->mode) &&
|
2016-06-25 07:09:27 +08:00
|
|
|
oid_eq(&dst_other.oid, &ren1->pair->two->oid)) {
|
2011-08-12 13:20:27 +08:00
|
|
|
/*
|
|
|
|
* Added file on the other side identical to
|
|
|
|
* the file being renamed: clean merge.
|
|
|
|
* Also, there is no need to overwrite the
|
|
|
|
* file already in the working copy, so call
|
|
|
|
* update_file_flags() instead of
|
|
|
|
* update_file().
|
|
|
|
*/
|
2016-07-27 00:06:21 +08:00
|
|
|
if (update_file_flags(o,
|
|
|
|
&ren1->pair->two->oid,
|
|
|
|
ren1->pair->two->mode,
|
|
|
|
ren1_dst,
|
|
|
|
1, /* update_cache */
|
|
|
|
0 /* update_wd */))
|
|
|
|
clean_merge = -1;
|
2016-06-25 07:09:27 +08:00
|
|
|
} else if (!oid_eq(&dst_other.oid, &null_oid)) {
|
2008-08-13 00:45:14 +08:00
|
|
|
clean_merge = 0;
|
|
|
|
try_merge = 1;
|
2012-07-25 22:53:13 +08:00
|
|
|
output(o, 1, _("CONFLICT (rename/add): Rename %s->%s in %s. "
|
|
|
|
"%s added in %s"),
|
2008-08-13 00:45:14 +08:00
|
|
|
ren1_src, ren1_dst, branch1,
|
|
|
|
ren1_dst, branch2);
|
2009-07-31 08:38:15 +08:00
|
|
|
if (o->call_depth) {
|
|
|
|
struct merge_file_info mfi;
|
2016-07-27 00:06:10 +08:00
|
|
|
if (merge_file_one(o, ren1_dst, &null_oid, 0,
|
|
|
|
&ren1->pair->two->oid,
|
|
|
|
ren1->pair->two->mode,
|
|
|
|
&dst_other.oid,
|
|
|
|
dst_other.mode,
|
2016-07-27 00:06:21 +08:00
|
|
|
branch1, branch2, &mfi)) {
|
|
|
|
clean_merge = -1;
|
|
|
|
goto cleanup_and_return;
|
|
|
|
}
|
2012-07-25 22:53:13 +08:00
|
|
|
output(o, 1, _("Adding merged %s"), ren1_dst);
|
2016-07-27 00:06:21 +08:00
|
|
|
if (update_file(o, 0, &mfi.oid,
|
|
|
|
mfi.mode, ren1_dst))
|
|
|
|
clean_merge = -1;
|
merge-recursive: Avoid doubly merging rename/add conflict contents
When a commit moves A to B while another commit created B (or moved C to
B), and these two different commits serve as different merge-bases for a
later merge, c94736a (merge-recursive: don't segfault while handling
rename clashes 2009-07-30) added some special code to avoid segfaults.
Since that commit, the two versions of B are merged in place (which could
be potentially conflicting) and the intermediate result is used as the
virtual ancestor.
However, right before this special merge, try_merge was turned on, meaning
that process_renames() would try an alternative merge that ignores the
'add' part of the conflict, and, if the merge is clean, store that as the
new virtual ancestor. This could cause incorrect merging of criss-cross
merges; it would typically result in just recording a slightly confusing
merge base, but in some cases it could cause silent acceptance of one side
of a merge as the final resolution when a conflict should have been
flagged.
When we do a special merge for such a rename/add conflict between
merge-bases, turn try_merge off to avoid an inappropriate second merge.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-09-20 16:28:59 +08:00
|
|
|
try_merge = 0;
|
2009-07-31 08:38:15 +08:00
|
|
|
} else {
|
2011-08-12 13:19:53 +08:00
|
|
|
char *new_path = unique_path(o, ren1_dst, branch2);
|
2012-07-25 22:53:13 +08:00
|
|
|
output(o, 1, _("Adding as %s instead"), new_path);
|
2016-07-27 00:06:21 +08:00
|
|
|
if (update_file(o, 0, &dst_other.oid,
|
|
|
|
dst_other.mode, new_path))
|
|
|
|
clean_merge = -1;
|
2011-08-12 13:19:53 +08:00
|
|
|
free(new_path);
|
2009-07-31 08:38:15 +08:00
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
} else
|
|
|
|
try_merge = 1;
|
|
|
|
|
2016-07-27 00:06:21 +08:00
|
|
|
if (clean_merge < 0)
|
|
|
|
goto cleanup_and_return;
|
2008-08-13 00:45:14 +08:00
|
|
|
if (try_merge) {
|
2008-08-25 22:25:57 +08:00
|
|
|
struct diff_filespec *one, *a, *b;
|
2008-08-13 00:45:14 +08:00
|
|
|
src_other.path = (char *)ren1_src;
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
one = ren1->pair->one;
|
2008-08-13 00:45:14 +08:00
|
|
|
if (a_renames == renames1) {
|
|
|
|
a = ren1->pair->two;
|
|
|
|
b = &src_other;
|
|
|
|
} else {
|
|
|
|
b = ren1->pair->two;
|
|
|
|
a = &src_other;
|
|
|
|
}
|
2011-08-12 13:20:02 +08:00
|
|
|
update_entry(ren1->dst_entry, one, a, b);
|
2011-08-12 13:20:08 +08:00
|
|
|
setup_rename_conflict_info(RENAME_NORMAL,
|
|
|
|
ren1->pair,
|
|
|
|
NULL,
|
|
|
|
branch1,
|
|
|
|
NULL,
|
|
|
|
ren1->dst_entry,
|
2011-08-12 13:20:16 +08:00
|
|
|
NULL,
|
|
|
|
o,
|
|
|
|
NULL,
|
2011-08-12 13:20:08 +08:00
|
|
|
NULL);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-07-27 00:06:21 +08:00
|
|
|
cleanup_and_return:
|
2008-08-13 00:45:14 +08:00
|
|
|
string_list_clear(&a_by_dst, 0);
|
|
|
|
string_list_clear(&b_by_dst, 0);
|
|
|
|
|
|
|
|
return clean_merge;
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:00 +08:00
|
|
|
struct rename_info {
|
|
|
|
struct string_list *head_renames;
|
|
|
|
struct string_list *merge_renames;
|
|
|
|
};
|
|
|
|
|
2018-04-20 01:58:05 +08:00
|
|
|
static void initial_cleanup_rename(struct diff_queue_struct *pairs,
|
|
|
|
struct hashmap *dir_renames)
|
2018-04-20 01:58:04 +08:00
|
|
|
{
|
2018-04-20 01:58:05 +08:00
|
|
|
struct hashmap_iter iter;
|
|
|
|
struct dir_rename_entry *e;
|
|
|
|
|
|
|
|
hashmap_iter_init(dir_renames, &iter);
|
|
|
|
while ((e = hashmap_iter_next(&iter))) {
|
|
|
|
free(e->dir);
|
|
|
|
strbuf_release(&e->new_dir);
|
|
|
|
/* possible_new_dirs already cleared in get_directory_renames */
|
|
|
|
}
|
|
|
|
hashmap_free(dir_renames, 1);
|
|
|
|
free(dir_renames);
|
|
|
|
|
2018-04-20 01:58:04 +08:00
|
|
|
free(pairs->queue);
|
|
|
|
free(pairs);
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:00 +08:00
|
|
|
static int handle_renames(struct merge_options *o,
|
|
|
|
struct tree *common,
|
|
|
|
struct tree *head,
|
|
|
|
struct tree *merge,
|
|
|
|
struct string_list *entries,
|
|
|
|
struct rename_info *ri)
|
|
|
|
{
|
2018-04-20 01:58:03 +08:00
|
|
|
struct diff_queue_struct *head_pairs, *merge_pairs;
|
2018-04-20 01:58:05 +08:00
|
|
|
struct hashmap *dir_re_head, *dir_re_merge;
|
2018-04-20 01:58:08 +08:00
|
|
|
int clean = 1;
|
2018-04-20 01:58:03 +08:00
|
|
|
|
2018-04-20 01:58:02 +08:00
|
|
|
ri->head_renames = NULL;
|
|
|
|
ri->merge_renames = NULL;
|
|
|
|
|
2018-05-03 00:01:14 +08:00
|
|
|
if (!merge_detect_rename(o))
|
2018-04-20 01:58:02 +08:00
|
|
|
return 1;
|
|
|
|
|
2018-04-20 01:58:03 +08:00
|
|
|
head_pairs = get_diffpairs(o, common, head);
|
|
|
|
merge_pairs = get_diffpairs(o, common, merge);
|
|
|
|
|
2018-04-20 01:58:05 +08:00
|
|
|
dir_re_head = get_directory_renames(head_pairs, head);
|
|
|
|
dir_re_merge = get_directory_renames(merge_pairs, merge);
|
|
|
|
|
2018-04-20 01:58:06 +08:00
|
|
|
handle_directory_level_conflicts(o,
|
|
|
|
dir_re_head, head,
|
|
|
|
dir_re_merge, merge);
|
|
|
|
|
2018-04-20 01:58:07 +08:00
|
|
|
ri->head_renames = get_renames(o, head_pairs,
|
2018-04-20 01:58:08 +08:00
|
|
|
dir_re_merge, dir_re_head, head,
|
|
|
|
common, head, merge, entries,
|
|
|
|
&clean);
|
|
|
|
if (clean < 0)
|
|
|
|
goto cleanup;
|
2018-04-20 01:58:07 +08:00
|
|
|
ri->merge_renames = get_renames(o, merge_pairs,
|
2018-04-20 01:58:08 +08:00
|
|
|
dir_re_head, dir_re_merge, merge,
|
|
|
|
common, head, merge, entries,
|
|
|
|
&clean);
|
|
|
|
if (clean < 0)
|
|
|
|
goto cleanup;
|
|
|
|
clean &= process_renames(o, ri->head_renames, ri->merge_renames);
|
|
|
|
|
|
|
|
cleanup:
|
2018-04-20 01:58:03 +08:00
|
|
|
/*
|
|
|
|
* Some cleanup is deferred until cleanup_renames() because the
|
|
|
|
* data structures are still needed and referenced in
|
|
|
|
* process_entry(). But there are a few things we can free now.
|
|
|
|
*/
|
2018-04-20 01:58:05 +08:00
|
|
|
initial_cleanup_rename(head_pairs, dir_re_head);
|
|
|
|
initial_cleanup_rename(merge_pairs, dir_re_merge);
|
2018-04-20 01:58:03 +08:00
|
|
|
|
|
|
|
return clean;
|
2018-04-20 01:58:00 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:04 +08:00
|
|
|
static void final_cleanup_rename(struct string_list *rename)
|
2018-04-20 01:58:00 +08:00
|
|
|
{
|
2018-04-20 01:58:01 +08:00
|
|
|
const struct rename *re;
|
|
|
|
int i;
|
2018-04-20 01:58:00 +08:00
|
|
|
|
2018-04-20 01:58:02 +08:00
|
|
|
if (rename == NULL)
|
|
|
|
return;
|
|
|
|
|
2018-04-20 01:58:01 +08:00
|
|
|
for (i = 0; i < rename->nr; i++) {
|
|
|
|
re = rename->items[i].util;
|
|
|
|
diff_free_filepair(re->pair);
|
|
|
|
}
|
|
|
|
string_list_clear(rename, 1);
|
|
|
|
free(rename);
|
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:04 +08:00
|
|
|
static void final_cleanup_renames(struct rename_info *re_info)
|
2018-04-20 01:58:01 +08:00
|
|
|
{
|
2018-04-20 01:58:04 +08:00
|
|
|
final_cleanup_rename(re_info->head_renames);
|
|
|
|
final_cleanup_rename(re_info->merge_renames);
|
2018-04-20 01:58:00 +08:00
|
|
|
}
|
|
|
|
|
2016-06-25 07:09:27 +08:00
|
|
|
static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2016-06-25 07:09:27 +08:00
|
|
|
return (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2016-08-01 19:44:37 +08:00
|
|
|
static int read_oid_strbuf(struct merge_options *o,
|
|
|
|
const struct object_id *oid, struct strbuf *dst)
|
2010-07-03 03:20:48 +08:00
|
|
|
{
|
|
|
|
void *buf;
|
|
|
|
enum object_type type;
|
|
|
|
unsigned long size;
|
sha1_file: convert read_sha1_file to struct object_id
Convert read_sha1_file to take a pointer to struct object_id and rename
it read_object_file. Do the same for read_sha1_file_extended.
Convert one use in grep.c to use the new function without any other code
change, since the pointer being passed is a void pointer that is already
initialized with a pointer to struct object_id. Update the declaration
and definitions of the modified functions, and apply the following
semantic patch to convert the remaining callers:
@@
expression E1, E2, E3;
@@
- read_sha1_file(E1.hash, E2, E3)
+ read_object_file(&E1, E2, E3)
@@
expression E1, E2, E3;
@@
- read_sha1_file(E1->hash, E2, E3)
+ read_object_file(E1, E2, E3)
@@
expression E1, E2, E3, E4;
@@
- read_sha1_file_extended(E1.hash, E2, E3, E4)
+ read_object_file_extended(&E1, E2, E3, E4)
@@
expression E1, E2, E3, E4;
@@
- read_sha1_file_extended(E1->hash, E2, E3, E4)
+ read_object_file_extended(E1, E2, E3, E4)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-12 10:27:53 +08:00
|
|
|
buf = read_object_file(oid, &type, &size);
|
2010-07-03 03:20:48 +08:00
|
|
|
if (!buf)
|
2016-08-01 19:44:37 +08:00
|
|
|
return err(o, _("cannot read object %s"), oid_to_hex(oid));
|
2010-07-03 03:20:48 +08:00
|
|
|
if (type != OBJ_BLOB) {
|
|
|
|
free(buf);
|
2016-08-01 19:44:37 +08:00
|
|
|
return err(o, _("object %s is not a blob"), oid_to_hex(oid));
|
2010-07-03 03:20:48 +08:00
|
|
|
}
|
|
|
|
strbuf_attach(dst, buf, size, size + 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-01 19:44:37 +08:00
|
|
|
static int blob_unchanged(struct merge_options *opt,
|
|
|
|
const struct object_id *o_oid,
|
2015-10-27 05:39:39 +08:00
|
|
|
unsigned o_mode,
|
2016-06-25 07:09:27 +08:00
|
|
|
const struct object_id *a_oid,
|
2015-10-27 05:39:39 +08:00
|
|
|
unsigned a_mode,
|
2010-08-05 19:13:49 +08:00
|
|
|
int renormalize, const char *path)
|
2010-07-03 03:20:48 +08:00
|
|
|
{
|
|
|
|
struct strbuf o = STRBUF_INIT;
|
|
|
|
struct strbuf a = STRBUF_INIT;
|
|
|
|
int ret = 0; /* assume changed for safety */
|
|
|
|
|
2015-10-27 05:39:39 +08:00
|
|
|
if (a_mode != o_mode)
|
|
|
|
return 0;
|
2016-06-25 07:09:27 +08:00
|
|
|
if (oid_eq(o_oid, a_oid))
|
2010-07-03 03:20:48 +08:00
|
|
|
return 1;
|
2010-08-05 19:13:49 +08:00
|
|
|
if (!renormalize)
|
2010-07-03 03:20:48 +08:00
|
|
|
return 0;
|
|
|
|
|
2016-06-25 07:09:27 +08:00
|
|
|
assert(o_oid && a_oid);
|
2016-08-01 19:44:37 +08:00
|
|
|
if (read_oid_strbuf(opt, o_oid, &o) || read_oid_strbuf(opt, a_oid, &a))
|
2010-07-03 03:20:48 +08:00
|
|
|
goto error_return;
|
|
|
|
/*
|
|
|
|
* Note: binary | is used so that both renormalizations are
|
|
|
|
* performed. Comparison can be skipped if both files are
|
|
|
|
* unchanged since their sha1s have already been compared.
|
|
|
|
*/
|
2017-06-13 06:13:56 +08:00
|
|
|
if (renormalize_buffer(&the_index, path, o.buf, o.len, &o) |
|
|
|
|
renormalize_buffer(&the_index, path, a.buf, a.len, &a))
|
2010-07-03 03:20:48 +08:00
|
|
|
ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));
|
|
|
|
|
|
|
|
error_return:
|
|
|
|
strbuf_release(&o);
|
|
|
|
strbuf_release(&a);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-07-27 00:06:21 +08:00
|
|
|
static int handle_modify_delete(struct merge_options *o,
|
2010-09-20 16:28:51 +08:00
|
|
|
const char *path,
|
2016-06-25 07:09:27 +08:00
|
|
|
struct object_id *o_oid, int o_mode,
|
|
|
|
struct object_id *a_oid, int a_mode,
|
|
|
|
struct object_id *b_oid, int b_mode)
|
2010-09-20 16:28:51 +08:00
|
|
|
{
|
2017-01-29 04:37:01 +08:00
|
|
|
const char *modify_branch, *delete_branch;
|
|
|
|
struct object_id *changed_oid;
|
|
|
|
int changed_mode;
|
|
|
|
|
|
|
|
if (a_oid) {
|
|
|
|
modify_branch = o->branch1;
|
|
|
|
delete_branch = o->branch2;
|
|
|
|
changed_oid = a_oid;
|
|
|
|
changed_mode = a_mode;
|
|
|
|
} else {
|
|
|
|
modify_branch = o->branch2;
|
|
|
|
delete_branch = o->branch1;
|
|
|
|
changed_oid = b_oid;
|
|
|
|
changed_mode = b_mode;
|
|
|
|
}
|
|
|
|
|
2016-07-27 00:06:21 +08:00
|
|
|
return handle_change_delete(o,
|
2017-01-29 04:37:01 +08:00
|
|
|
path, NULL,
|
2016-07-27 00:06:21 +08:00
|
|
|
o_oid, o_mode,
|
2017-01-29 04:37:01 +08:00
|
|
|
changed_oid, changed_mode,
|
|
|
|
modify_branch, delete_branch,
|
2016-07-27 00:06:21 +08:00
|
|
|
_("modify"), _("modified"));
|
2010-09-20 16:28:51 +08:00
|
|
|
}
|
|
|
|
|
2010-09-20 16:28:52 +08:00
|
|
|
static int merge_content(struct merge_options *o,
|
|
|
|
const char *path,
|
2018-04-20 01:58:17 +08:00
|
|
|
int is_dirty,
|
2016-06-25 07:09:27 +08:00
|
|
|
struct object_id *o_oid, int o_mode,
|
|
|
|
struct object_id *a_oid, int a_mode,
|
|
|
|
struct object_id *b_oid, int b_mode,
|
2011-08-12 13:20:08 +08:00
|
|
|
struct rename_conflict_info *rename_conflict_info)
|
2010-09-20 16:28:52 +08:00
|
|
|
{
|
2012-07-25 22:53:13 +08:00
|
|
|
const char *reason = _("content");
|
merge-recursive: When we detect we can skip an update, actually skip it
In 882fd11 (merge-recursive: Delay content merging for renames 2010-09-20),
there was code that checked for whether we could skip updating a file in
the working directory, based on whether the merged version matched the
current working copy. Due to the desire to handle directory/file conflicts
that were resolvable, that commit deferred content merging by first
updating the index with the unmerged entries and then moving the actual
merging (along with the skip-the-content-update check) to another function
that ran later in the merge process. As part moving the content merging
code, a bug was introduced such that although the message about skipping
the update would be printed (whenever GIT_MERGE_VERBOSITY was sufficiently
high), the file would be unconditionally updated in the working copy
anyway.
When we detect that the file does not need to be updated in the working
copy, update the index appropriately and then return early before updating
the working copy.
Note that there was a similar change in b2c8c0a (merge-recursive: When we
detect we can skip an update, actually skip it 2011-02-28), but it was
reverted by 6db4105 (Revert "Merge branch 'en/merge-recursive'"
2011-05-19) since it did not fix both of the relevant types of unnecessary
update breakages and, worse, it made use of some band-aids that caused
other problems. The reason this change works is due to the changes earlier
in this series to (a) record_df_conflict_files instead of just unlinking
them early, (b) allowing make_room_for_path() to remove D/F entries,
(c) the splitting of update_stages_and_entry() to have its functionality
called at different points, and (d) making the pathnames of the files
involved in the merge available to merge_content().
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-08-12 13:20:10 +08:00
|
|
|
const char *path1 = NULL, *path2 = NULL;
|
2010-09-20 16:28:52 +08:00
|
|
|
struct merge_file_info mfi;
|
|
|
|
struct diff_filespec one, a, b;
|
2010-09-20 16:29:07 +08:00
|
|
|
unsigned df_conflict_remains = 0;
|
2010-09-20 16:28:52 +08:00
|
|
|
|
2016-06-25 07:09:27 +08:00
|
|
|
if (!o_oid) {
|
2012-07-25 22:53:13 +08:00
|
|
|
reason = _("add/add");
|
2016-06-25 07:09:27 +08:00
|
|
|
o_oid = (struct object_id *)&null_oid;
|
2010-09-20 16:28:52 +08:00
|
|
|
}
|
|
|
|
one.path = a.path = b.path = (char *)path;
|
2016-06-25 07:09:27 +08:00
|
|
|
oidcpy(&one.oid, o_oid);
|
2010-09-20 16:28:52 +08:00
|
|
|
one.mode = o_mode;
|
2016-06-25 07:09:27 +08:00
|
|
|
oidcpy(&a.oid, a_oid);
|
2010-09-20 16:28:52 +08:00
|
|
|
a.mode = a_mode;
|
2016-06-25 07:09:27 +08:00
|
|
|
oidcpy(&b.oid, b_oid);
|
2010-09-20 16:28:52 +08:00
|
|
|
b.mode = b_mode;
|
|
|
|
|
2011-08-12 13:20:09 +08:00
|
|
|
if (rename_conflict_info) {
|
|
|
|
struct diff_filepair *pair1 = rename_conflict_info->pair1;
|
|
|
|
|
|
|
|
path1 = (o->branch1 == rename_conflict_info->branch1) ?
|
|
|
|
pair1->two->path : pair1->one->path;
|
|
|
|
/* If rename_conflict_info->pair2 != NULL, we are in
|
|
|
|
* RENAME_ONE_FILE_TO_ONE case. Otherwise, we have a
|
|
|
|
* normal rename.
|
|
|
|
*/
|
|
|
|
path2 = (rename_conflict_info->pair2 ||
|
|
|
|
o->branch2 == rename_conflict_info->branch1) ?
|
|
|
|
pair1->two->path : pair1->one->path;
|
|
|
|
|
2016-11-08 02:31:31 +08:00
|
|
|
if (dir_in_way(path, !o->call_depth,
|
|
|
|
S_ISGITLINK(pair1->two->mode)))
|
2011-08-12 13:20:09 +08:00
|
|
|
df_conflict_remains = 1;
|
2010-09-20 16:29:07 +08:00
|
|
|
}
|
2018-04-20 01:58:22 +08:00
|
|
|
if (merge_file_special_markers(o, &one, &a, &b, path,
|
2016-07-27 00:06:10 +08:00
|
|
|
o->branch1, path1,
|
|
|
|
o->branch2, path2, &mfi))
|
|
|
|
return -1;
|
2010-09-20 16:29:07 +08:00
|
|
|
|
merge-recursive: fix check for skipability of working tree updates
The can-working-tree-updates-be-skipped check has had a long and blemished
history. The update can be skipped iff:
a) The merge is clean
b) The merge matches what was in HEAD (content, mode, pathname)
c) The target path is usable (i.e. not involved in D/F conflict)
Traditionally, we split b into parts:
b1) The merged result matches the content and mode found in HEAD
b2) The merged target path existed in HEAD
Steps a & b1 are easy to check; we have always gotten those right. While
it is easy to overlook step c, this was fixed seven years ago with commit
4ab9a157d069 ("merge_content(): Check whether D/F conflicts are still
present", 2010-09-20). merge-recursive didn't have a readily available
way to directly check step b2, so various approximations were used:
* In commit b2c8c0a76274 ("merge-recursive: When we detect we can skip
an update, actually skip it", 2011-02-28), it was noted that although
the code claimed it was skipping the update, it did not actually skip
the update. The code was made to skip it, but used lstat(path, ...)
as an approximation to path-was-tracked-in-index-before-merge.
* In commit 5b448b853030 ("merge-recursive: When we detect we can skip
an update, actually skip it", 2011-08-11), the problem with using
lstat was noted. It was changed to the approximation
path2 && strcmp(path, path2)
which is also wrong. !path2 || strcmp(path, path2) would have been
better, but would have fallen short with directory renames.
* In c5b761fb2711 ("merge-recursive: ensure we write updates for
directory-renamed file", 2018-02-14), the problem with the previous
approximation was noted and changed to
was_tracked(path)
That looks close to what we were trying to answer, but was_tracked()
as implemented at the time should have been named is_tracked(); it
returned something different than what we were looking for.
* To make matters more complex, fixing was_tracked() isn't sufficient
because the splitting of b into b1 and b2 is wrong. Consider the
following merge with a rename/add conflict:
side A: modify foo, add unrelated bar
side B: rename foo->bar (but don't modify the mode or contents)
In this case, the three-way merge of original foo, A's foo, and B's
bar will result in a desired pathname of bar with the same
mode/contents that A had for foo. Thus, A had the right mode and
contents for the file, and it had the right pathname present (namely,
bar), but the bar that was present was unrelated to the contents, so
the working tree update was not skippable.
Fix this by introducing a new function:
was_tracked_and_matches(o, path, &mfi.oid, mfi.mode)
and use it to directly check for condition b.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-20 01:58:23 +08:00
|
|
|
/*
|
|
|
|
* We can skip updating the working tree file iff:
|
|
|
|
* a) The merge is clean
|
|
|
|
* b) The merge matches what was in HEAD (content, mode, pathname)
|
|
|
|
* c) The target path is usable (i.e. not involved in D/F conflict)
|
|
|
|
*/
|
|
|
|
if (mfi.clean &&
|
|
|
|
was_tracked_and_matches(o, path, &mfi.oid, mfi.mode) &&
|
|
|
|
!df_conflict_remains) {
|
2012-07-25 22:53:13 +08:00
|
|
|
output(o, 3, _("Skipped %s (merged same as existing)"), path);
|
merge-recursive: fix check for skipability of working tree updates
The can-working-tree-updates-be-skipped check has had a long and blemished
history. The update can be skipped iff:
a) The merge is clean
b) The merge matches what was in HEAD (content, mode, pathname)
c) The target path is usable (i.e. not involved in D/F conflict)
Traditionally, we split b into parts:
b1) The merged result matches the content and mode found in HEAD
b2) The merged target path existed in HEAD
Steps a & b1 are easy to check; we have always gotten those right. While
it is easy to overlook step c, this was fixed seven years ago with commit
4ab9a157d069 ("merge_content(): Check whether D/F conflicts are still
present", 2010-09-20). merge-recursive didn't have a readily available
way to directly check step b2, so various approximations were used:
* In commit b2c8c0a76274 ("merge-recursive: When we detect we can skip
an update, actually skip it", 2011-02-28), it was noted that although
the code claimed it was skipping the update, it did not actually skip
the update. The code was made to skip it, but used lstat(path, ...)
as an approximation to path-was-tracked-in-index-before-merge.
* In commit 5b448b853030 ("merge-recursive: When we detect we can skip
an update, actually skip it", 2011-08-11), the problem with using
lstat was noted. It was changed to the approximation
path2 && strcmp(path, path2)
which is also wrong. !path2 || strcmp(path, path2) would have been
better, but would have fallen short with directory renames.
* In c5b761fb2711 ("merge-recursive: ensure we write updates for
directory-renamed file", 2018-02-14), the problem with the previous
approximation was noted and changed to
was_tracked(path)
That looks close to what we were trying to answer, but was_tracked()
as implemented at the time should have been named is_tracked(); it
returned something different than what we were looking for.
* To make matters more complex, fixing was_tracked() isn't sufficient
because the splitting of b into b1 and b2 is wrong. Consider the
following merge with a rename/add conflict:
side A: modify foo, add unrelated bar
side B: rename foo->bar (but don't modify the mode or contents)
In this case, the three-way merge of original foo, A's foo, and B's
bar will result in a desired pathname of bar with the same
mode/contents that A had for foo. Thus, A had the right mode and
contents for the file, and it had the right pathname present (namely,
bar), but the bar that was present was unrelated to the contents, so
the working tree update was not skippable.
Fix this by introducing a new function:
was_tracked_and_matches(o, path, &mfi.oid, mfi.mode)
and use it to directly check for condition b.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-04-20 01:58:23 +08:00
|
|
|
if (add_cacheinfo(o, mfi.mode, &mfi.oid, path,
|
|
|
|
0, (!o->call_depth && !is_dirty), 0))
|
|
|
|
return -1;
|
|
|
|
return mfi.clean;
|
2018-04-20 01:58:22 +08:00
|
|
|
}
|
2010-09-20 16:28:52 +08:00
|
|
|
|
|
|
|
if (!mfi.clean) {
|
|
|
|
if (S_ISGITLINK(mfi.mode))
|
2012-07-25 22:53:13 +08:00
|
|
|
reason = _("submodule");
|
|
|
|
output(o, 1, _("CONFLICT (%s): Merge conflict in %s"),
|
2010-09-20 16:28:52 +08:00
|
|
|
reason, path);
|
2011-08-12 13:20:08 +08:00
|
|
|
if (rename_conflict_info && !df_conflict_remains)
|
2016-08-01 19:44:37 +08:00
|
|
|
if (update_stages(o, path, &one, &a, &b))
|
2016-07-27 00:06:21 +08:00
|
|
|
return -1;
|
2010-09-20 16:28:52 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:17 +08:00
|
|
|
if (df_conflict_remains || is_dirty) {
|
2011-08-12 13:19:53 +08:00
|
|
|
char *new_path;
|
2011-08-12 13:20:06 +08:00
|
|
|
if (o->call_depth) {
|
|
|
|
remove_file_from_cache(path);
|
|
|
|
} else {
|
2016-07-27 00:06:21 +08:00
|
|
|
if (!mfi.clean) {
|
2016-08-01 19:44:37 +08:00
|
|
|
if (update_stages(o, path, &one, &a, &b))
|
2016-07-27 00:06:21 +08:00
|
|
|
return -1;
|
|
|
|
} else {
|
2018-04-20 01:58:20 +08:00
|
|
|
int file_from_stage2 = was_tracked(o, path);
|
2011-08-12 13:20:06 +08:00
|
|
|
struct diff_filespec merged;
|
2016-06-25 07:09:26 +08:00
|
|
|
oidcpy(&merged.oid, &mfi.oid);
|
2011-08-12 13:20:06 +08:00
|
|
|
merged.mode = mfi.mode;
|
|
|
|
|
2016-08-01 19:44:37 +08:00
|
|
|
if (update_stages(o, path, NULL,
|
2016-07-27 00:06:21 +08:00
|
|
|
file_from_stage2 ? &merged : NULL,
|
|
|
|
file_from_stage2 ? NULL : &merged))
|
|
|
|
return -1;
|
2011-08-12 13:20:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2011-08-12 13:20:08 +08:00
|
|
|
new_path = unique_path(o, path, rename_conflict_info->branch1);
|
2018-04-20 01:58:17 +08:00
|
|
|
if (is_dirty) {
|
|
|
|
output(o, 1, _("Refusing to lose dirty file at %s"),
|
|
|
|
path);
|
|
|
|
}
|
2012-07-25 22:53:13 +08:00
|
|
|
output(o, 1, _("Adding as %s instead"), new_path);
|
2016-07-27 00:06:21 +08:00
|
|
|
if (update_file(o, 0, &mfi.oid, mfi.mode, new_path)) {
|
|
|
|
free(new_path);
|
|
|
|
return -1;
|
|
|
|
}
|
2011-08-12 13:19:53 +08:00
|
|
|
free(new_path);
|
2011-08-12 13:20:06 +08:00
|
|
|
mfi.clean = 0;
|
2016-07-27 00:06:21 +08:00
|
|
|
} else if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, path))
|
|
|
|
return -1;
|
2018-04-20 01:58:17 +08:00
|
|
|
return !is_dirty && mfi.clean;
|
2010-09-20 16:28:52 +08:00
|
|
|
}
|
|
|
|
|
2018-04-20 01:58:12 +08:00
|
|
|
static int conflict_rename_normal(struct merge_options *o,
|
|
|
|
const char *path,
|
|
|
|
struct object_id *o_oid, unsigned int o_mode,
|
|
|
|
struct object_id *a_oid, unsigned int a_mode,
|
|
|
|
struct object_id *b_oid, unsigned int b_mode,
|
|
|
|
struct rename_conflict_info *ci)
|
|
|
|
{
|
|
|
|
/* Merge the content and write it out */
|
2018-04-20 01:58:17 +08:00
|
|
|
return merge_content(o, path, was_dirty(o, path),
|
|
|
|
o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,
|
|
|
|
ci);
|
2010-09-20 16:28:52 +08:00
|
|
|
}
|
|
|
|
|
2008-08-13 00:45:14 +08:00
|
|
|
/* Per entry merge function */
|
2008-08-25 22:25:57 +08:00
|
|
|
static int process_entry(struct merge_options *o,
|
|
|
|
const char *path, struct stage_data *entry)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
|
|
|
int clean_merge = 1;
|
2010-08-05 19:15:32 +08:00
|
|
|
int normalize = o->renormalize;
|
2008-08-13 00:45:14 +08:00
|
|
|
unsigned o_mode = entry->stages[1].mode;
|
|
|
|
unsigned a_mode = entry->stages[2].mode;
|
|
|
|
unsigned b_mode = entry->stages[3].mode;
|
2016-06-25 07:09:27 +08:00
|
|
|
struct object_id *o_oid = stage_oid(&entry->stages[1].oid, o_mode);
|
|
|
|
struct object_id *a_oid = stage_oid(&entry->stages[2].oid, a_mode);
|
|
|
|
struct object_id *b_oid = stage_oid(&entry->stages[3].oid, b_mode);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2010-07-09 21:10:53 +08:00
|
|
|
entry->processed = 1;
|
2011-08-12 13:20:08 +08:00
|
|
|
if (entry->rename_conflict_info) {
|
|
|
|
struct rename_conflict_info *conflict_info = entry->rename_conflict_info;
|
2010-09-20 16:29:00 +08:00
|
|
|
switch (conflict_info->rename_type) {
|
2010-09-20 16:29:03 +08:00
|
|
|
case RENAME_NORMAL:
|
2011-08-12 13:20:08 +08:00
|
|
|
case RENAME_ONE_FILE_TO_ONE:
|
2018-04-20 01:58:12 +08:00
|
|
|
clean_merge = conflict_rename_normal(o,
|
|
|
|
path,
|
|
|
|
o_oid, o_mode,
|
|
|
|
a_oid, a_mode,
|
|
|
|
b_oid, b_mode,
|
|
|
|
conflict_info);
|
2010-09-20 16:29:03 +08:00
|
|
|
break;
|
2018-04-20 01:58:10 +08:00
|
|
|
case RENAME_DIR:
|
|
|
|
clean_merge = 1;
|
|
|
|
if (conflict_rename_dir(o,
|
|
|
|
conflict_info->pair1,
|
|
|
|
conflict_info->branch1,
|
|
|
|
conflict_info->branch2))
|
|
|
|
clean_merge = -1;
|
2010-09-20 16:29:03 +08:00
|
|
|
break;
|
2010-09-20 16:29:02 +08:00
|
|
|
case RENAME_DELETE:
|
|
|
|
clean_merge = 0;
|
2016-07-27 00:06:21 +08:00
|
|
|
if (conflict_rename_delete(o,
|
|
|
|
conflict_info->pair1,
|
|
|
|
conflict_info->branch1,
|
|
|
|
conflict_info->branch2))
|
|
|
|
clean_merge = -1;
|
2010-09-20 16:29:02 +08:00
|
|
|
break;
|
2010-09-20 16:29:00 +08:00
|
|
|
case RENAME_ONE_FILE_TO_TWO:
|
|
|
|
clean_merge = 0;
|
2016-07-27 00:06:21 +08:00
|
|
|
if (conflict_rename_rename_1to2(o, conflict_info))
|
|
|
|
clean_merge = -1;
|
2010-09-20 16:29:00 +08:00
|
|
|
break;
|
2011-08-12 13:20:15 +08:00
|
|
|
case RENAME_TWO_FILES_TO_ONE:
|
|
|
|
clean_merge = 0;
|
2016-07-27 00:06:21 +08:00
|
|
|
if (conflict_rename_rename_2to1(o, conflict_info))
|
|
|
|
clean_merge = -1;
|
2010-09-20 16:29:00 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
entry->processed = 0;
|
|
|
|
break;
|
|
|
|
}
|
2016-06-25 07:09:27 +08:00
|
|
|
} else if (o_oid && (!a_oid || !b_oid)) {
|
2011-08-12 13:20:07 +08:00
|
|
|
/* Case A: Deleted in one */
|
2016-06-25 07:09:27 +08:00
|
|
|
if ((!a_oid && !b_oid) ||
|
2016-08-01 19:44:37 +08:00
|
|
|
(!b_oid && blob_unchanged(o, o_oid, o_mode, a_oid, a_mode, normalize, path)) ||
|
|
|
|
(!a_oid && blob_unchanged(o, o_oid, o_mode, b_oid, b_mode, normalize, path))) {
|
2011-08-12 13:20:07 +08:00
|
|
|
/* Deleted in both or deleted in one and
|
|
|
|
* unchanged in the other */
|
2016-06-25 07:09:27 +08:00
|
|
|
if (a_oid)
|
2012-07-25 22:53:13 +08:00
|
|
|
output(o, 2, _("Removing %s"), path);
|
2011-08-12 13:20:07 +08:00
|
|
|
/* do not touch working file if it did not exist */
|
2016-06-25 07:09:27 +08:00
|
|
|
remove_file(o, 1, path, !a_oid);
|
2011-08-12 13:20:07 +08:00
|
|
|
} else {
|
|
|
|
/* Modify/delete; deleted side may have put a directory in the way */
|
|
|
|
clean_merge = 0;
|
2016-07-27 00:06:21 +08:00
|
|
|
if (handle_modify_delete(o, path, o_oid, o_mode,
|
|
|
|
a_oid, a_mode, b_oid, b_mode))
|
|
|
|
clean_merge = -1;
|
2011-08-12 13:19:53 +08:00
|
|
|
}
|
2016-06-25 07:09:27 +08:00
|
|
|
} else if ((!o_oid && a_oid && !b_oid) ||
|
|
|
|
(!o_oid && !a_oid && b_oid)) {
|
2011-08-12 13:20:07 +08:00
|
|
|
/* Case B: Added in one. */
|
|
|
|
/* [nothing|directory] -> ([nothing|directory], file) */
|
|
|
|
|
2010-09-20 16:28:56 +08:00
|
|
|
const char *add_branch;
|
|
|
|
const char *other_branch;
|
|
|
|
unsigned mode;
|
2016-06-25 07:09:27 +08:00
|
|
|
const struct object_id *oid;
|
2010-09-20 16:28:56 +08:00
|
|
|
const char *conf;
|
2010-07-09 21:10:53 +08:00
|
|
|
|
2016-06-25 07:09:27 +08:00
|
|
|
if (a_oid) {
|
2010-09-20 16:28:56 +08:00
|
|
|
add_branch = o->branch1;
|
|
|
|
other_branch = o->branch2;
|
|
|
|
mode = a_mode;
|
2016-06-25 07:09:27 +08:00
|
|
|
oid = a_oid;
|
2012-07-25 22:53:13 +08:00
|
|
|
conf = _("file/directory");
|
2010-09-20 16:28:56 +08:00
|
|
|
} else {
|
|
|
|
add_branch = o->branch2;
|
|
|
|
other_branch = o->branch1;
|
|
|
|
mode = b_mode;
|
2016-06-25 07:09:27 +08:00
|
|
|
oid = b_oid;
|
2012-07-25 22:53:13 +08:00
|
|
|
conf = _("directory/file");
|
2010-09-20 16:28:56 +08:00
|
|
|
}
|
2017-11-15 01:31:24 +08:00
|
|
|
if (dir_in_way(path,
|
|
|
|
!o->call_depth && !S_ISGITLINK(a_mode),
|
|
|
|
0)) {
|
2011-08-12 13:19:53 +08:00
|
|
|
char *new_path = unique_path(o, path, add_branch);
|
2010-09-20 16:28:56 +08:00
|
|
|
clean_merge = 0;
|
2012-07-25 22:53:13 +08:00
|
|
|
output(o, 1, _("CONFLICT (%s): There is a directory with name %s in %s. "
|
|
|
|
"Adding %s as %s"),
|
2010-09-20 16:28:56 +08:00
|
|
|
conf, path, other_branch, path, new_path);
|
2016-07-27 00:06:21 +08:00
|
|
|
if (update_file(o, 0, oid, mode, new_path))
|
|
|
|
clean_merge = -1;
|
|
|
|
else if (o->call_depth)
|
2011-08-12 13:19:55 +08:00
|
|
|
remove_file_from_cache(path);
|
2011-08-12 13:19:53 +08:00
|
|
|
free(new_path);
|
2010-09-20 16:28:56 +08:00
|
|
|
} else {
|
2012-07-25 22:53:13 +08:00
|
|
|
output(o, 2, _("Adding %s"), path);
|
2011-08-12 13:20:27 +08:00
|
|
|
/* do not overwrite file if already present */
|
2016-07-27 00:06:21 +08:00
|
|
|
if (update_file_flags(o, oid, mode, path, 1, !a_oid))
|
|
|
|
clean_merge = -1;
|
2010-09-20 16:28:56 +08:00
|
|
|
}
|
2016-06-25 07:09:27 +08:00
|
|
|
} else if (a_oid && b_oid) {
|
2011-08-12 13:20:07 +08:00
|
|
|
/* Case C: Added in both (check for same permissions) and */
|
|
|
|
/* case D: Modified in both, but differently. */
|
2018-04-20 01:58:17 +08:00
|
|
|
int is_dirty = 0; /* unpack_trees would have bailed if dirty */
|
|
|
|
clean_merge = merge_content(o, path, is_dirty,
|
2016-06-25 07:09:27 +08:00
|
|
|
o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,
|
2011-08-12 13:20:07 +08:00
|
|
|
NULL);
|
2016-06-25 07:09:27 +08:00
|
|
|
} else if (!o_oid && !a_oid && !b_oid) {
|
2011-08-12 13:20:07 +08:00
|
|
|
/*
|
|
|
|
* this entry was deleted altogether. a_mode == 0 means
|
|
|
|
* we had that path and want to actively remove it.
|
|
|
|
*/
|
|
|
|
remove_file(o, 1, path, !a_mode);
|
|
|
|
} else
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("fatal merge failure, shouldn't happen.");
|
2010-07-09 21:10:53 +08:00
|
|
|
|
|
|
|
return clean_merge;
|
|
|
|
}
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
int merge_trees(struct merge_options *o,
|
|
|
|
struct tree *head,
|
2008-08-13 00:45:14 +08:00
|
|
|
struct tree *merge,
|
|
|
|
struct tree *common,
|
|
|
|
struct tree **result)
|
|
|
|
{
|
|
|
|
int code, clean;
|
|
|
|
|
2008-07-01 13:18:57 +08:00
|
|
|
if (o->subtree_shift) {
|
|
|
|
merge = shift_tree_object(head, merge, o->subtree_shift);
|
|
|
|
common = shift_tree_object(head, common, o->subtree_shift);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2016-06-25 07:09:27 +08:00
|
|
|
if (oid_eq(&common->object.oid, &merge->object.oid)) {
|
merge-recursive: avoid incorporating uncommitted changes in a merge
builtin/merge.c contains this important requirement for merge strategies:
/*
* At this point, we need a real merge. No matter what strategy
* we use, it would operate on the index, possibly affecting the
* working tree, and when resolved cleanly, have the desired
* tree in the index -- this means that the index must be in
* sync with the head commit. The strategies are responsible
* to ensure this.
*/
merge-recursive does not do this check directly, instead it relies on
unpack_trees() to do it. However, merge_trees() has a special check for
the merge branch exactly matching the merge base; when it detects that
situation, it returns early without calling unpack_trees(), because it
knows that the HEAD commit already has the correct result. Unfortunately,
it didn't check that the index matched HEAD, so after it returned, the
outer logic ended up creating a merge commit that included something
other than HEAD.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-12-22 03:19:07 +08:00
|
|
|
struct strbuf sb = STRBUF_INIT;
|
|
|
|
|
2018-01-10 02:05:51 +08:00
|
|
|
if (!o->call_depth && index_has_changes(&sb)) {
|
merge-recursive: avoid incorporating uncommitted changes in a merge
builtin/merge.c contains this important requirement for merge strategies:
/*
* At this point, we need a real merge. No matter what strategy
* we use, it would operate on the index, possibly affecting the
* working tree, and when resolved cleanly, have the desired
* tree in the index -- this means that the index must be in
* sync with the head commit. The strategies are responsible
* to ensure this.
*/
merge-recursive does not do this check directly, instead it relies on
unpack_trees() to do it. However, merge_trees() has a special check for
the merge branch exactly matching the merge base; when it detects that
situation, it returns early without calling unpack_trees(), because it
knows that the HEAD commit already has the correct result. Unfortunately,
it didn't check that the index matched HEAD, so after it returned, the
outer logic ended up creating a merge commit that included something
other than HEAD.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-12-22 03:19:07 +08:00
|
|
|
err(o, _("Dirty index: cannot merge (dirty: %s)"),
|
|
|
|
sb.buf);
|
|
|
|
return 0;
|
|
|
|
}
|
2017-08-24 01:49:35 +08:00
|
|
|
output(o, 0, _("Already up to date!"));
|
2008-08-13 00:45:14 +08:00
|
|
|
*result = head;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-05-20 18:17:35 +08:00
|
|
|
code = unpack_trees_start(o, common, head, merge);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
merge-recursive: give less scary messages when merge did not start
When unpack_trees() three-way merge logic is called from merge-recursive
and finds that local changes are going to be clobbered, its plumbing level
messages were given as errors first, and then the merge driver added even
more scary message "fatal: merging of trees <a long object name> and
<another long object name> failed".
This is most often encountered by new CVS/SVN migrants who are used to
start a merge from a dirty work tree. The saddest part is that the merge
refused to run to prevent _any_ damage from being done to your work tree
when these messages are given, but the messages look a lot more scarier
than the conflicted case where the user needs to resolve them.
Replace the plumbing level messages so that they talk about what it is
protecting the user from, and end the messages with "Aborting." so that it
becomes clear that the command did not do any harm.
The final "merging of trees failed" message is superfluous, unless you are
interested in debugging the merge-recursive itself. Squelch the current
die() message by default, but allow it to help people who debug git with
verbosity level 4 or greater.
Unless there is some bug, an inner merge that does not touch working tree
should not trigger any such error, so emit the current die() message when
we see an error return from it while running the inner merge, too. It
would also help people who debug git.
We could later add instructions on how to recover (i.e. "stash changes
away or commit on a side branch and retry") instead of the silent
exit(128) I have in this patch, and then use Peff's advice.* mechanism to
squelch it (e.g. "advice.mergeindirtytree"), but they are separate topics.
Tested-by: Nanako Shiraishi <nanako3@lavabit.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-08 13:43:11 +08:00
|
|
|
if (code != 0) {
|
|
|
|
if (show(o, 4) || o->call_depth)
|
2016-08-01 19:44:37 +08:00
|
|
|
err(o, _("merging of trees %s and %s failed"),
|
2015-11-10 10:22:28 +08:00
|
|
|
oid_to_hex(&head->object.oid),
|
|
|
|
oid_to_hex(&merge->object.oid));
|
2018-05-20 18:17:35 +08:00
|
|
|
unpack_trees_finish(o);
|
2016-07-27 00:06:26 +08:00
|
|
|
return -1;
|
merge-recursive: give less scary messages when merge did not start
When unpack_trees() three-way merge logic is called from merge-recursive
and finds that local changes are going to be clobbered, its plumbing level
messages were given as errors first, and then the merge driver added even
more scary message "fatal: merging of trees <a long object name> and
<another long object name> failed".
This is most often encountered by new CVS/SVN migrants who are used to
start a merge from a dirty work tree. The saddest part is that the merge
refused to run to prevent _any_ damage from being done to your work tree
when these messages are given, but the messages look a lot more scarier
than the conflicted case where the user needs to resolve them.
Replace the plumbing level messages so that they talk about what it is
protecting the user from, and end the messages with "Aborting." so that it
becomes clear that the command did not do any harm.
The final "merging of trees failed" message is superfluous, unless you are
interested in debugging the merge-recursive itself. Squelch the current
die() message by default, but allow it to help people who debug git with
verbosity level 4 or greater.
Unless there is some bug, an inner merge that does not touch working tree
should not trigger any such error, so emit the current die() message when
we see an error return from it while running the inner merge, too. It
would also help people who debug git.
We could later add instructions on how to recover (i.e. "stash changes
away or commit on a side branch and retry") instead of the silent
exit(128) I have in this patch, and then use Peff's advice.* mechanism to
squelch it (e.g. "advice.mergeindirtytree"), but they are separate topics.
Tested-by: Nanako Shiraishi <nanako3@lavabit.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-09-08 13:43:11 +08:00
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
if (unmerged_cache()) {
|
2018-04-20 01:58:00 +08:00
|
|
|
struct string_list *entries;
|
|
|
|
struct rename_info re_info;
|
2008-08-13 00:45:14 +08:00
|
|
|
int i;
|
2017-09-08 00:25:56 +08:00
|
|
|
/*
|
|
|
|
* Only need the hashmap while processing entries, so
|
|
|
|
* initialize it here and free it when we are done running
|
|
|
|
* through the entries. Keeping it in the merge_options as
|
|
|
|
* opposed to decaring a local hashmap is for convenience
|
|
|
|
* so that we don't have to pass it to around.
|
|
|
|
*/
|
|
|
|
hashmap_init(&o->current_file_dir_set, path_hashmap_cmp, NULL, 512);
|
2008-09-04 01:08:56 +08:00
|
|
|
get_files_dirs(o, head);
|
|
|
|
get_files_dirs(o, merge);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
entries = get_unmerged();
|
2018-04-20 01:58:00 +08:00
|
|
|
clean = handle_renames(o, common, head, merge, entries,
|
|
|
|
&re_info);
|
2018-01-06 04:20:00 +08:00
|
|
|
record_df_conflict_files(o, entries);
|
2016-07-27 00:06:21 +08:00
|
|
|
if (clean < 0)
|
2017-08-29 04:28:27 +08:00
|
|
|
goto cleanup;
|
2011-08-12 13:20:07 +08:00
|
|
|
for (i = entries->nr-1; 0 <= i; i--) {
|
2008-08-13 00:45:14 +08:00
|
|
|
const char *path = entries->items[i].string;
|
|
|
|
struct stage_data *e = entries->items[i].util;
|
2016-07-27 00:06:21 +08:00
|
|
|
if (!e->processed) {
|
|
|
|
int ret = process_entry(o, path, e);
|
|
|
|
if (!ret)
|
|
|
|
clean = 0;
|
2017-08-29 04:28:27 +08:00
|
|
|
else if (ret < 0) {
|
|
|
|
clean = ret;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2016-07-27 00:06:21 +08:00
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
2010-09-20 16:28:35 +08:00
|
|
|
for (i = 0; i < entries->nr; i++) {
|
|
|
|
struct stage_data *e = entries->items[i].util;
|
|
|
|
if (!e->processed)
|
2018-05-02 17:38:39 +08:00
|
|
|
BUG("unprocessed path??? %s",
|
2010-09-20 16:28:35 +08:00
|
|
|
entries->items[i].string);
|
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2017-08-29 04:28:27 +08:00
|
|
|
cleanup:
|
2018-04-20 01:58:04 +08:00
|
|
|
final_cleanup_renames(&re_info);
|
2018-04-20 01:58:00 +08:00
|
|
|
|
2008-08-13 00:45:14 +08:00
|
|
|
string_list_clear(entries, 1);
|
2018-04-20 01:58:00 +08:00
|
|
|
free(entries);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2017-09-08 00:25:56 +08:00
|
|
|
hashmap_free(&o->current_file_dir_set, 1);
|
|
|
|
|
2018-05-20 18:17:35 +08:00
|
|
|
if (clean < 0) {
|
|
|
|
unpack_trees_finish(o);
|
2017-08-29 04:28:27 +08:00
|
|
|
return clean;
|
2018-05-20 18:17:35 +08:00
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
clean = 1;
|
|
|
|
|
2018-05-20 18:17:35 +08:00
|
|
|
unpack_trees_finish(o);
|
2018-04-20 01:58:20 +08:00
|
|
|
|
2016-07-27 00:06:17 +08:00
|
|
|
if (o->call_depth && !(*result = write_tree_from_memory(o)))
|
|
|
|
return -1;
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
return clean;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct commit_list *reverse_commit_list(struct commit_list *list)
|
|
|
|
{
|
|
|
|
struct commit_list *next = NULL, *current, *backup;
|
|
|
|
for (current = list; current; current = backup) {
|
|
|
|
backup = current->next;
|
|
|
|
current->next = next;
|
|
|
|
next = current;
|
|
|
|
}
|
|
|
|
return next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Merge the commits h1 and h2, return the resulting virtual
|
|
|
|
* commit object and a flag indicating the cleanness of the merge.
|
|
|
|
*/
|
2008-08-25 22:25:57 +08:00
|
|
|
int merge_recursive(struct merge_options *o,
|
|
|
|
struct commit *h1,
|
2008-08-13 00:45:14 +08:00
|
|
|
struct commit *h2,
|
|
|
|
struct commit_list *ca,
|
|
|
|
struct commit **result)
|
|
|
|
{
|
|
|
|
struct commit_list *iter;
|
|
|
|
struct commit *merged_common_ancestors;
|
-Wuninitialized: remove some 'init-self' workarounds
The 'self-initialised' variables construct (ie <type> var = var;) has
been used to silence gcc '-W[maybe-]uninitialized' warnings. This has,
unfortunately, caused MSVC to issue 'uninitialized variable' warnings.
Also, using clang static analysis causes complaints about an 'Assigned
value is garbage or undefined'.
There are six such constructs in the current codebase. Only one of the
six causes gcc to issue a '-Wmaybe-uninitialized' warning (which will
be addressed elsewhere). The remaining five 'init-self' gcc workarounds
are noted below, along with the commit which introduced them:
1. builtin/rev-list.c: 'reaches' and 'all', see commit 457f08a030
("git-rev-list: add --bisect-vars option.", 2007-03-21).
2. merge-recursive.c:2064 'mrtree', see commit f120ae2a8e ("merge-
recursive.c: mrtree in merge() is not used before set", 2007-10-29).
3. fast-import.c:3023 'oe', see commit 85c62395b1 ("fast-import: let
importers retrieve blobs", 2010-11-28).
4. fast-import.c:3006 'oe', see commit 28c7b1f7b7 ("fast-import: add a
get-mark command", 2015-07-01).
Remove the 'self-initialised' variable constructs noted above.
Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-20 01:54:35 +08:00
|
|
|
struct tree *mrtree;
|
2008-08-13 00:45:14 +08:00
|
|
|
int clean;
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
if (show(o, 4)) {
|
2012-07-25 22:53:13 +08:00
|
|
|
output(o, 4, _("Merging:"));
|
2008-09-03 05:30:09 +08:00
|
|
|
output_commit_title(o, h1);
|
|
|
|
output_commit_title(o, h2);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!ca) {
|
2014-10-31 03:20:44 +08:00
|
|
|
ca = get_merge_bases(h1, h2);
|
2008-08-13 00:45:14 +08:00
|
|
|
ca = reverse_commit_list(ca);
|
|
|
|
}
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
if (show(o, 5)) {
|
2012-08-06 01:56:38 +08:00
|
|
|
unsigned cnt = commit_list_count(ca);
|
|
|
|
|
|
|
|
output(o, 5, Q_("found %u common ancestor:",
|
|
|
|
"found %u common ancestors:", cnt), cnt);
|
2008-08-13 00:45:14 +08:00
|
|
|
for (iter = ca; iter; iter = iter->next)
|
2008-09-03 05:30:09 +08:00
|
|
|
output_commit_title(o, iter->item);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
merged_common_ancestors = pop_commit(&ca);
|
|
|
|
if (merged_common_ancestors == NULL) {
|
2011-08-17 02:27:39 +08:00
|
|
|
/* if there is no common ancestor, use an empty tree */
|
|
|
|
struct tree *tree;
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2017-11-13 05:28:54 +08:00
|
|
|
tree = lookup_tree(the_hash_algo->empty_tree);
|
2008-08-13 00:45:14 +08:00
|
|
|
merged_common_ancestors = make_virtual_commit(tree, "ancestor");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (iter = ca; iter; iter = iter->next) {
|
2008-08-25 22:25:57 +08:00
|
|
|
const char *saved_b1, *saved_b2;
|
2008-09-03 05:30:09 +08:00
|
|
|
o->call_depth++;
|
2008-08-13 00:45:14 +08:00
|
|
|
/*
|
|
|
|
* When the merge fails, the result contains files
|
|
|
|
* with conflict markers. The cleanness flag is
|
2016-07-27 00:06:07 +08:00
|
|
|
* ignored (unless indicating an error), it was never
|
|
|
|
* actually used, as result of merge_trees has always
|
|
|
|
* overwritten it: the committed "conflicts" were
|
|
|
|
* already resolved.
|
2008-08-13 00:45:14 +08:00
|
|
|
*/
|
|
|
|
discard_cache();
|
2008-08-25 22:25:57 +08:00
|
|
|
saved_b1 = o->branch1;
|
|
|
|
saved_b2 = o->branch2;
|
|
|
|
o->branch1 = "Temporary merge branch 1";
|
|
|
|
o->branch2 = "Temporary merge branch 2";
|
2016-07-27 00:06:07 +08:00
|
|
|
if (merge_recursive(o, merged_common_ancestors, iter->item,
|
|
|
|
NULL, &merged_common_ancestors) < 0)
|
|
|
|
return -1;
|
2008-08-25 22:25:57 +08:00
|
|
|
o->branch1 = saved_b1;
|
|
|
|
o->branch2 = saved_b2;
|
2008-09-03 05:30:09 +08:00
|
|
|
o->call_depth--;
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
if (!merged_common_ancestors)
|
2016-08-01 19:44:37 +08:00
|
|
|
return err(o, _("merge returned no commit"));
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
discard_cache();
|
2008-09-03 05:53:47 +08:00
|
|
|
if (!o->call_depth)
|
2008-08-13 00:45:14 +08:00
|
|
|
read_cache();
|
|
|
|
|
2010-03-21 08:52:21 +08:00
|
|
|
o->ancestor = "merged common ancestors";
|
2018-04-07 03:09:38 +08:00
|
|
|
clean = merge_trees(o, get_commit_tree(h1), get_commit_tree(h2),
|
|
|
|
get_commit_tree(merged_common_ancestors),
|
2008-08-25 22:25:57 +08:00
|
|
|
&mrtree);
|
2016-08-01 19:44:57 +08:00
|
|
|
if (clean < 0) {
|
|
|
|
flush_output(o);
|
2016-07-27 00:06:07 +08:00
|
|
|
return clean;
|
2016-08-01 19:44:57 +08:00
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2008-09-03 05:53:47 +08:00
|
|
|
if (o->call_depth) {
|
2008-08-13 00:45:14 +08:00
|
|
|
*result = make_virtual_commit(mrtree, "merged tree");
|
|
|
|
commit_list_insert(h1, &(*result)->parents);
|
|
|
|
commit_list_insert(h2, &(*result)->parents->next);
|
|
|
|
}
|
2008-09-03 08:30:03 +08:00
|
|
|
flush_output(o);
|
2016-08-01 19:44:53 +08:00
|
|
|
if (!o->call_depth && o->buffer_output < 2)
|
|
|
|
strbuf_release(&o->obuf);
|
2011-01-07 05:50:06 +08:00
|
|
|
if (show(o, 2))
|
|
|
|
diff_warn_rename_limit("merge.renamelimit",
|
|
|
|
o->needed_rename_limit, 0);
|
2008-08-13 00:45:14 +08:00
|
|
|
return clean;
|
|
|
|
}
|
|
|
|
|
2016-06-25 07:09:28 +08:00
|
|
|
static struct commit *get_ref(const struct object_id *oid, const char *name)
|
2008-08-13 04:13:59 +08:00
|
|
|
{
|
|
|
|
struct object *object;
|
|
|
|
|
object: convert parse_object* to take struct object_id
Make parse_object, parse_object_or_die, and parse_object_buffer take a
pointer to struct object_id. Remove the temporary variables inserted
earlier, since they are no longer necessary. Transform all of the
callers using the following semantic patch:
@@
expression E1;
@@
- parse_object(E1.hash)
+ parse_object(&E1)
@@
expression E1;
@@
- parse_object(E1->hash)
+ parse_object(E1)
@@
expression E1, E2;
@@
- parse_object_or_die(E1.hash, E2)
+ parse_object_or_die(&E1, E2)
@@
expression E1, E2;
@@
- parse_object_or_die(E1->hash, E2)
+ parse_object_or_die(E1, E2)
@@
expression E1, E2, E3, E4, E5;
@@
- parse_object_buffer(E1.hash, E2, E3, E4, E5)
+ parse_object_buffer(&E1, E2, E3, E4, E5)
@@
expression E1, E2, E3, E4, E5;
@@
- parse_object_buffer(E1->hash, E2, E3, E4, E5)
+ parse_object_buffer(E1, E2, E3, E4, E5)
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-05-07 06:10:38 +08:00
|
|
|
object = deref_tag(parse_object(oid), name, strlen(name));
|
2008-08-13 04:13:59 +08:00
|
|
|
if (!object)
|
|
|
|
return NULL;
|
|
|
|
if (object->type == OBJ_TREE)
|
|
|
|
return make_virtual_commit((struct tree*)object, name);
|
|
|
|
if (object->type != OBJ_COMMIT)
|
|
|
|
return NULL;
|
|
|
|
if (parse_commit((struct commit *)object))
|
|
|
|
return NULL;
|
|
|
|
return (struct commit *)object;
|
|
|
|
}
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
int merge_recursive_generic(struct merge_options *o,
|
2016-06-25 07:09:28 +08:00
|
|
|
const struct object_id *head,
|
|
|
|
const struct object_id *merge,
|
2008-08-25 22:25:57 +08:00
|
|
|
int num_base_list,
|
2016-06-25 07:09:28 +08:00
|
|
|
const struct object_id **base_list,
|
2008-08-25 22:25:57 +08:00
|
|
|
struct commit **result)
|
2008-08-13 04:13:59 +08:00
|
|
|
{
|
2014-06-13 20:19:23 +08:00
|
|
|
int clean;
|
2017-10-06 04:32:04 +08:00
|
|
|
struct lock_file lock = LOCK_INIT;
|
2008-08-25 22:25:57 +08:00
|
|
|
struct commit *head_commit = get_ref(head, o->branch1);
|
|
|
|
struct commit *next_commit = get_ref(merge, o->branch2);
|
2008-08-13 04:13:59 +08:00
|
|
|
struct commit_list *ca = NULL;
|
|
|
|
|
|
|
|
if (base_list) {
|
|
|
|
int i;
|
2008-08-25 22:25:57 +08:00
|
|
|
for (i = 0; i < num_base_list; ++i) {
|
2008-08-13 04:13:59 +08:00
|
|
|
struct commit *base;
|
2016-06-25 07:09:28 +08:00
|
|
|
if (!(base = get_ref(base_list[i], oid_to_hex(base_list[i]))))
|
2016-08-01 19:44:37 +08:00
|
|
|
return err(o, _("Could not parse object '%s'"),
|
2016-06-25 07:09:28 +08:00
|
|
|
oid_to_hex(base_list[i]));
|
2008-08-13 04:13:59 +08:00
|
|
|
commit_list_insert(base, &ca);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-06 04:32:04 +08:00
|
|
|
hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
|
2008-08-25 22:25:57 +08:00
|
|
|
clean = merge_recursive(o, head_commit, next_commit, ca,
|
|
|
|
result);
|
2018-03-01 03:07:56 +08:00
|
|
|
if (clean < 0) {
|
|
|
|
rollback_lock_file(&lock);
|
2016-07-27 00:06:07 +08:00
|
|
|
return clean;
|
2018-03-01 03:07:56 +08:00
|
|
|
}
|
2016-07-27 00:06:07 +08:00
|
|
|
|
2018-03-02 04:40:20 +08:00
|
|
|
if (write_locked_index(&the_index, &lock,
|
|
|
|
COMMIT_LOCK | SKIP_IF_UNCHANGED))
|
2016-08-01 19:44:37 +08:00
|
|
|
return err(o, _("Unable to write index."));
|
2008-08-13 04:13:59 +08:00
|
|
|
|
|
|
|
return clean ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
2014-08-13 16:22:01 +08:00
|
|
|
static void merge_recursive_config(struct merge_options *o)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2018-05-03 00:01:14 +08:00
|
|
|
char *value = NULL;
|
2014-08-13 16:22:01 +08:00
|
|
|
git_config_get_int("merge.verbosity", &o->verbosity);
|
|
|
|
git_config_get_int("diff.renamelimit", &o->diff_rename_limit);
|
|
|
|
git_config_get_int("merge.renamelimit", &o->merge_rename_limit);
|
2018-05-03 00:01:14 +08:00
|
|
|
if (!git_config_get_string("diff.renames", &value)) {
|
|
|
|
o->diff_detect_rename = git_config_rename("diff.renames", value);
|
|
|
|
free(value);
|
|
|
|
}
|
|
|
|
if (!git_config_get_string("merge.renames", &value)) {
|
|
|
|
o->merge_detect_rename = git_config_rename("merge.renames", value);
|
|
|
|
free(value);
|
|
|
|
}
|
2014-08-13 16:22:01 +08:00
|
|
|
git_config(git_xmerge_config, NULL);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
void init_merge_options(struct merge_options *o)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2017-10-31 17:09:13 +08:00
|
|
|
const char *merge_verbosity;
|
2008-08-25 22:25:57 +08:00
|
|
|
memset(o, 0, sizeof(struct merge_options));
|
|
|
|
o->verbosity = 2;
|
|
|
|
o->buffer_output = 1;
|
|
|
|
o->diff_rename_limit = -1;
|
|
|
|
o->merge_rename_limit = -1;
|
2010-08-05 19:32:41 +08:00
|
|
|
o->renormalize = 0;
|
2018-05-03 00:01:14 +08:00
|
|
|
o->diff_detect_rename = -1;
|
|
|
|
o->merge_detect_rename = -1;
|
2014-08-13 16:22:01 +08:00
|
|
|
merge_recursive_config(o);
|
2017-10-31 17:09:13 +08:00
|
|
|
merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
|
|
|
|
if (merge_verbosity)
|
|
|
|
o->verbosity = strtol(merge_verbosity, NULL, 10);
|
2008-08-25 22:25:57 +08:00
|
|
|
if (o->verbosity >= 5)
|
|
|
|
o->buffer_output = 0;
|
2008-09-03 08:30:03 +08:00
|
|
|
strbuf_init(&o->obuf, 0);
|
2014-07-18 17:19:00 +08:00
|
|
|
string_list_init(&o->df_conflict_file_set, 1);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
2010-08-26 13:47:58 +08:00
|
|
|
|
|
|
|
int parse_merge_opt(struct merge_options *o, const char *s)
|
|
|
|
{
|
2014-06-19 03:48:29 +08:00
|
|
|
const char *arg;
|
|
|
|
|
2010-08-26 13:47:58 +08:00
|
|
|
if (!s || !*s)
|
|
|
|
return -1;
|
|
|
|
if (!strcmp(s, "ours"))
|
|
|
|
o->recursive_variant = MERGE_RECURSIVE_OURS;
|
|
|
|
else if (!strcmp(s, "theirs"))
|
|
|
|
o->recursive_variant = MERGE_RECURSIVE_THEIRS;
|
|
|
|
else if (!strcmp(s, "subtree"))
|
|
|
|
o->subtree_shift = "";
|
2014-06-19 03:48:29 +08:00
|
|
|
else if (skip_prefix(s, "subtree=", &arg))
|
|
|
|
o->subtree_shift = arg;
|
2010-08-26 13:50:45 +08:00
|
|
|
else if (!strcmp(s, "patience"))
|
2012-02-20 07:36:55 +08:00
|
|
|
o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
|
2011-07-12 14:10:25 +08:00
|
|
|
else if (!strcmp(s, "histogram"))
|
2012-02-20 07:36:55 +08:00
|
|
|
o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
|
2014-06-19 03:48:29 +08:00
|
|
|
else if (skip_prefix(s, "diff-algorithm=", &arg)) {
|
|
|
|
long value = parse_algorithm_value(arg);
|
2013-01-16 15:51:58 +08:00
|
|
|
if (value < 0)
|
|
|
|
return -1;
|
|
|
|
/* clear out previous settings */
|
|
|
|
DIFF_XDL_CLR(o, NEED_MINIMAL);
|
|
|
|
o->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
|
|
|
|
o->xdl_opts |= value;
|
|
|
|
}
|
2010-08-26 13:51:47 +08:00
|
|
|
else if (!strcmp(s, "ignore-space-change"))
|
2017-06-30 06:19:32 +08:00
|
|
|
DIFF_XDL_SET(o, IGNORE_WHITESPACE_CHANGE);
|
2010-08-26 13:51:47 +08:00
|
|
|
else if (!strcmp(s, "ignore-all-space"))
|
2017-06-30 06:19:32 +08:00
|
|
|
DIFF_XDL_SET(o, IGNORE_WHITESPACE);
|
2010-08-26 13:51:47 +08:00
|
|
|
else if (!strcmp(s, "ignore-space-at-eol"))
|
2017-06-30 06:19:32 +08:00
|
|
|
DIFF_XDL_SET(o, IGNORE_WHITESPACE_AT_EOL);
|
2017-10-26 14:32:27 +08:00
|
|
|
else if (!strcmp(s, "ignore-cr-at-eol"))
|
|
|
|
DIFF_XDL_SET(o, IGNORE_CR_AT_EOL);
|
2010-08-26 13:47:58 +08:00
|
|
|
else if (!strcmp(s, "renormalize"))
|
|
|
|
o->renormalize = 1;
|
|
|
|
else if (!strcmp(s, "no-renormalize"))
|
|
|
|
o->renormalize = 0;
|
2016-02-17 11:15:25 +08:00
|
|
|
else if (!strcmp(s, "no-renames"))
|
2018-05-03 00:01:14 +08:00
|
|
|
o->merge_detect_rename = 0;
|
2016-02-22 06:59:05 +08:00
|
|
|
else if (!strcmp(s, "find-renames")) {
|
2018-05-03 00:01:14 +08:00
|
|
|
o->merge_detect_rename = 1;
|
2016-02-22 06:59:05 +08:00
|
|
|
o->rename_score = 0;
|
|
|
|
}
|
2016-02-17 11:15:26 +08:00
|
|
|
else if (skip_prefix(s, "find-renames=", &arg) ||
|
|
|
|
skip_prefix(s, "rename-threshold=", &arg)) {
|
2014-06-19 03:48:29 +08:00
|
|
|
if ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
|
2010-09-28 07:58:25 +08:00
|
|
|
return -1;
|
2018-05-03 00:01:14 +08:00
|
|
|
o->merge_detect_rename = 1;
|
2010-09-28 07:58:25 +08:00
|
|
|
}
|
2010-08-26 13:47:58 +08:00
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|