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
|
|
|
|
*/
|
2009-11-23 06:26:17 +08:00
|
|
|
#include "advice.h"
|
2008-08-13 00:45:14 +08:00
|
|
|
#include "cache.h"
|
|
|
|
#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"
|
2008-08-13 00:45:14 +08:00
|
|
|
|
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
|
|
|
{
|
|
|
|
unsigned char shifted[20];
|
|
|
|
|
2008-07-01 13:18:57 +08:00
|
|
|
if (!*subtree_shift) {
|
|
|
|
shift_tree(one->object.sha1, two->object.sha1, shifted, 0);
|
|
|
|
} else {
|
|
|
|
shift_tree_by(one->object.sha1, two->object.sha1, shifted,
|
|
|
|
subtree_shift);
|
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
if (!hashcmp(two->object.sha1, shifted))
|
|
|
|
return two;
|
|
|
|
return lookup_tree(shifted);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2008-09-06 01:26:42 +08:00
|
|
|
* A virtual commit has (const char *)commit->util set to the name.
|
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
|
|
|
{
|
|
|
|
struct commit *commit = xcalloc(1, sizeof(struct commit));
|
|
|
|
commit->tree = tree;
|
|
|
|
commit->util = (void*)comment;
|
|
|
|
/* avoid warnings */
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
static int sha_eq(const unsigned char *a, const unsigned char *b)
|
|
|
|
{
|
|
|
|
if (!a && !b)
|
|
|
|
return 2;
|
|
|
|
return a && b && hashcmp(a, b) == 0;
|
|
|
|
}
|
|
|
|
|
2010-09-20 16:28:53 +08:00
|
|
|
enum rename_type {
|
|
|
|
RENAME_NORMAL = 0,
|
|
|
|
RENAME_DELETE,
|
|
|
|
RENAME_ONE_FILE_TO_TWO
|
|
|
|
};
|
|
|
|
|
|
|
|
struct rename_df_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;
|
|
|
|
struct stage_data *dst_entry2;
|
|
|
|
};
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
struct stage_data
|
|
|
|
{
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
unsigned mode;
|
|
|
|
unsigned char sha[20];
|
|
|
|
} stages[4];
|
2010-09-20 16:28:53 +08:00
|
|
|
struct rename_df_conflict_info *rename_df_conflict_info;
|
2008-08-13 00:45:14 +08:00
|
|
|
unsigned processed:1;
|
|
|
|
};
|
|
|
|
|
2010-09-20 16:28:53 +08:00
|
|
|
static inline void setup_rename_df_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,
|
|
|
|
struct stage_data *dst_entry2)
|
|
|
|
{
|
|
|
|
struct rename_df_conflict_info *ci = xcalloc(1, sizeof(struct rename_df_conflict_info));
|
|
|
|
ci->rename_type = rename_type;
|
|
|
|
ci->pair1 = pair1;
|
|
|
|
ci->branch1 = branch1;
|
|
|
|
ci->branch2 = branch2;
|
|
|
|
|
|
|
|
ci->dst_entry1 = dst_entry1;
|
|
|
|
dst_entry1->rename_df_conflict_info = ci;
|
|
|
|
dst_entry1->processed = 0;
|
|
|
|
|
|
|
|
assert(!pair2 == !dst_entry2);
|
|
|
|
if (dst_entry2) {
|
|
|
|
ci->dst_entry2 = dst_entry2;
|
|
|
|
ci->pair2 = pair2;
|
|
|
|
dst_entry2->rename_df_conflict_info = ci;
|
|
|
|
dst_entry2->processed = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2008-09-03 08:30:03 +08:00
|
|
|
static void flush_output(struct merge_options *o)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2008-09-03 08:30:03 +08:00
|
|
|
if (o->obuf.len) {
|
|
|
|
fputs(o->obuf.buf, stdout);
|
|
|
|
strbuf_reset(&o->obuf);
|
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
|
|
|
{
|
|
|
|
int len;
|
|
|
|
va_list ap;
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
if (!show(o, v))
|
2008-08-13 00:45:14 +08:00
|
|
|
return;
|
|
|
|
|
2008-09-03 08:30:03 +08:00
|
|
|
strbuf_grow(&o->obuf, o->call_depth * 2 + 2);
|
|
|
|
memset(o->obuf.buf + o->obuf.len, ' ', o->call_depth * 2);
|
|
|
|
strbuf_setlen(&o->obuf, o->obuf.len + o->call_depth * 2);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
va_start(ap, fmt);
|
2008-09-03 08:30:03 +08:00
|
|
|
len = vsnprintf(o->obuf.buf + o->obuf.len, strbuf_avail(&o->obuf), fmt, ap);
|
2008-08-13 00:45:14 +08:00
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
if (len < 0)
|
|
|
|
len = 0;
|
2008-09-03 08:30:03 +08:00
|
|
|
if (len >= strbuf_avail(&o->obuf)) {
|
|
|
|
strbuf_grow(&o->obuf, len + 2);
|
2008-08-13 00:45:14 +08:00
|
|
|
va_start(ap, fmt);
|
2008-09-03 08:30:03 +08:00
|
|
|
len = vsnprintf(o->obuf.buf + o->obuf.len, strbuf_avail(&o->obuf), fmt, ap);
|
2008-08-13 00:45:14 +08:00
|
|
|
va_end(ap);
|
2008-09-03 08:30:03 +08:00
|
|
|
if (len >= strbuf_avail(&o->obuf)) {
|
2008-08-13 00:45:14 +08:00
|
|
|
die("this should not happen, your snprintf is broken");
|
|
|
|
}
|
|
|
|
}
|
2008-09-03 08:30:03 +08:00
|
|
|
strbuf_setlen(&o->obuf, o->obuf.len + len);
|
|
|
|
strbuf_add(&o->obuf, "\n", 1);
|
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
|
|
|
{
|
|
|
|
int i;
|
2008-09-03 08:30:03 +08:00
|
|
|
flush_output(o);
|
2008-09-03 05:30:09 +08:00
|
|
|
for (i = o->call_depth; i--;)
|
2008-08-13 00:45:14 +08:00
|
|
|
fputs(" ", stdout);
|
|
|
|
if (commit->util)
|
|
|
|
printf("virtual %s\n", (char *)commit->util);
|
|
|
|
else {
|
|
|
|
printf("%s ", find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
|
|
|
|
if (parse_commit(commit) != 0)
|
|
|
|
printf("(bad commit)\n");
|
|
|
|
else {
|
2010-07-22 21:18:34 +08:00
|
|
|
const char *title;
|
|
|
|
int len = find_commit_subject(commit->buffer, &title);
|
|
|
|
if (len)
|
|
|
|
printf("%.*s\n", len, title);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
|
|
|
|
const char *path, int stage, int refresh, int options)
|
|
|
|
{
|
|
|
|
struct cache_entry *ce;
|
|
|
|
ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
|
|
|
|
if (!ce)
|
|
|
|
return error("addinfo_cache failed for path '%s'", path);
|
|
|
|
return add_cache_entry(ce, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int git_merge_trees(int index_only,
|
|
|
|
struct tree *common,
|
|
|
|
struct tree *head,
|
|
|
|
struct tree *merge)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
struct tree_desc t[3];
|
|
|
|
struct unpack_trees_options opts;
|
|
|
|
|
|
|
|
memset(&opts, 0, sizeof(opts));
|
|
|
|
if (index_only)
|
|
|
|
opts.index_only = 1;
|
|
|
|
else
|
|
|
|
opts.update = 1;
|
|
|
|
opts.merge = 1;
|
|
|
|
opts.head_idx = 2;
|
|
|
|
opts.fn = threeway_merge;
|
|
|
|
opts.src_index = &the_index;
|
|
|
|
opts.dst_index = &the_index;
|
2010-09-02 19:57:34 +08:00
|
|
|
setup_unpack_trees_porcelain(&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);
|
|
|
|
|
|
|
|
rc = unpack_trees(3, t, &opts);
|
|
|
|
cache_tree_free(&active_cache_tree);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
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++) {
|
|
|
|
struct cache_entry *ce = active_cache[i];
|
|
|
|
if (ce_stage(ce))
|
2010-01-22 08:38:56 +08:00
|
|
|
fprintf(stderr, "BUG: %d %.*s", ce_stage(ce),
|
|
|
|
(int)ce_namelen(ce), ce->name);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
2010-01-22 08:38:56 +08:00
|
|
|
die("Bug 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) &&
|
|
|
|
cache_tree_update(active_cache_tree,
|
|
|
|
active_cache, active_nr, 0, 0) < 0)
|
|
|
|
die("error building trees");
|
|
|
|
|
|
|
|
result = lookup_tree(active_cache_tree->sha1);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int save_files_dirs(const unsigned char *sha1,
|
|
|
|
const char *base, int baselen, const char *path,
|
|
|
|
unsigned int mode, int stage, void *context)
|
|
|
|
{
|
|
|
|
int len = strlen(path);
|
|
|
|
char *newpath = xmalloc(baselen + len + 1);
|
2008-09-04 01:08:56 +08:00
|
|
|
struct merge_options *o = context;
|
|
|
|
|
2008-08-13 00:45:14 +08:00
|
|
|
memcpy(newpath, base, baselen);
|
|
|
|
memcpy(newpath + baselen, path, len);
|
|
|
|
newpath[baselen + len] = '\0';
|
|
|
|
|
|
|
|
if (S_ISDIR(mode))
|
2010-06-26 07:41:35 +08:00
|
|
|
string_list_insert(&o->current_directory_set, newpath);
|
2008-08-13 00:45:14 +08:00
|
|
|
else
|
2010-06-26 07:41:35 +08:00
|
|
|
string_list_insert(&o->current_file_set, newpath);
|
2008-08-13 00:45:14 +08:00
|
|
|
free(newpath);
|
|
|
|
|
2009-01-25 08:52:05 +08:00
|
|
|
return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
2008-09-04 01:08:56 +08:00
|
|
|
static int get_files_dirs(struct merge_options *o, struct tree *tree)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
|
|
|
int n;
|
2008-09-04 01:08:56 +08:00
|
|
|
if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs, o))
|
2008-08-13 00:45:14 +08:00
|
|
|
return 0;
|
2008-09-04 01:08:56 +08:00
|
|
|
n = o->current_file_set.nr + o->current_directory_set.nr;
|
2008-08-13 00:45:14 +08:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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));
|
|
|
|
get_tree_entry(o->object.sha1, path,
|
|
|
|
e->stages[1].sha, &e->stages[1].mode);
|
|
|
|
get_tree_entry(a->object.sha1, path,
|
|
|
|
e->stages[2].sha, &e->stages[2].mode);
|
|
|
|
get_tree_entry(b->object.sha1, path,
|
|
|
|
e->stages[3].sha, &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;
|
|
|
|
struct cache_entry *ce = active_cache[i];
|
|
|
|
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;
|
|
|
|
hashcpy(e->stages[ce_stage(ce)].sha, ce->sha1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return unmerged;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rename
|
|
|
|
{
|
|
|
|
struct diff_filepair *pair;
|
|
|
|
struct stage_data *src_entry;
|
|
|
|
struct stage_data *dst_entry;
|
|
|
|
unsigned processed:1;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get information of all renames which occurred between 'o_tree' and
|
|
|
|
* 'tree'. 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.
|
|
|
|
*/
|
2008-08-25 22:25:57 +08:00
|
|
|
static struct string_list *get_renames(struct merge_options *o,
|
|
|
|
struct tree *tree,
|
|
|
|
struct tree *o_tree,
|
|
|
|
struct tree *a_tree,
|
|
|
|
struct tree *b_tree,
|
|
|
|
struct string_list *entries)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct string_list *renames;
|
|
|
|
struct diff_options opts;
|
|
|
|
|
|
|
|
renames = xcalloc(1, sizeof(struct string_list));
|
|
|
|
diff_setup(&opts);
|
|
|
|
DIFF_OPT_SET(&opts, RECURSIVE);
|
|
|
|
opts.detect_rename = DIFF_DETECT_RENAME;
|
2008-08-25 22:25:57 +08:00
|
|
|
opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
|
|
|
|
o->diff_rename_limit >= 0 ? o->diff_rename_limit :
|
2008-08-13 00:45:14 +08:00
|
|
|
500;
|
|
|
|
opts.warn_on_too_large_rename = 1;
|
|
|
|
opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
|
|
|
if (diff_setup_done(&opts) < 0)
|
|
|
|
die("diff setup failed");
|
|
|
|
diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts);
|
|
|
|
diffcore_std(&opts);
|
|
|
|
for (i = 0; i < diff_queued_diff.nr; ++i) {
|
|
|
|
struct string_list_item *item;
|
|
|
|
struct rename *re;
|
|
|
|
struct diff_filepair *pair = diff_queued_diff.queue[i];
|
|
|
|
if (pair->status != 'R') {
|
|
|
|
diff_free_filepair(pair);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
re = xmalloc(sizeof(*re));
|
|
|
|
re->processed = 0;
|
|
|
|
re->pair = pair;
|
2010-06-26 07:41:37 +08:00
|
|
|
item = string_list_lookup(entries, re->pair->one->path);
|
2008-08-13 00:45:14 +08:00
|
|
|
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;
|
|
|
|
|
2010-06-26 07:41:37 +08:00
|
|
|
item = string_list_lookup(entries, re->pair->two->path);
|
2008-08-13 00:45:14 +08:00
|
|
|
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;
|
2010-06-26 07:41:35 +08:00
|
|
|
item = string_list_insert(renames, pair->one->path);
|
2008-08-13 00:45:14 +08:00
|
|
|
item->util = re;
|
|
|
|
}
|
|
|
|
opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
|
|
|
diff_queued_diff.nr = 0;
|
|
|
|
diff_flush(&opts);
|
|
|
|
return renames;
|
|
|
|
}
|
|
|
|
|
2010-09-20 16:28:54 +08:00
|
|
|
static int update_stages_options(const char *path, struct diff_filespec *o,
|
2008-08-13 00:45:14 +08:00
|
|
|
struct diff_filespec *a, struct diff_filespec *b,
|
2010-09-20 16:28:54 +08:00
|
|
|
int clear, int options)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
|
|
|
if (clear)
|
|
|
|
if (remove_file_from_cache(path))
|
|
|
|
return -1;
|
|
|
|
if (o)
|
|
|
|
if (add_cacheinfo(o->mode, o->sha1, path, 1, 0, options))
|
|
|
|
return -1;
|
|
|
|
if (a)
|
|
|
|
if (add_cacheinfo(a->mode, a->sha1, path, 2, 0, options))
|
|
|
|
return -1;
|
|
|
|
if (b)
|
|
|
|
if (add_cacheinfo(b->mode, b->sha1, path, 3, 0, options))
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-09-20 16:28:54 +08:00
|
|
|
static int update_stages(const char *path, struct diff_filespec *o,
|
|
|
|
struct diff_filespec *a, struct diff_filespec *b,
|
|
|
|
int clear)
|
|
|
|
{
|
|
|
|
int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
|
|
|
|
return update_stages_options(path, o, a, b, clear, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int update_stages_and_entry(const char *path,
|
|
|
|
struct stage_data *entry,
|
|
|
|
struct diff_filespec *o,
|
|
|
|
struct diff_filespec *a,
|
|
|
|
struct diff_filespec *b,
|
|
|
|
int clear)
|
|
|
|
{
|
|
|
|
int options;
|
|
|
|
|
|
|
|
entry->processed = 0;
|
|
|
|
entry->stages[1].mode = o->mode;
|
|
|
|
entry->stages[2].mode = a->mode;
|
|
|
|
entry->stages[3].mode = b->mode;
|
|
|
|
hashcpy(entry->stages[1].sha, o->sha1);
|
|
|
|
hashcpy(entry->stages[2].sha, a->sha1);
|
|
|
|
hashcpy(entry->stages[3].sha, b->sha1);
|
|
|
|
options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_SKIP_DFCHECK;
|
|
|
|
return update_stages_options(path, o, a, b, clear, options);
|
|
|
|
}
|
|
|
|
|
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) {
|
2010-03-26 23:25:35 +08:00
|
|
|
if (remove_path(path))
|
2008-08-13 00:45:14 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);
|
|
|
|
int suffix = 0;
|
|
|
|
struct stat st;
|
|
|
|
char *p = newpath + strlen(path);
|
|
|
|
strcpy(newpath, path);
|
|
|
|
*(p++) = '~';
|
|
|
|
strcpy(p, branch);
|
|
|
|
for (; *p; ++p)
|
|
|
|
if ('/' == *p)
|
|
|
|
*p = '_';
|
2008-09-04 01:08:56 +08:00
|
|
|
while (string_list_has_string(&o->current_file_set, newpath) ||
|
|
|
|
string_list_has_string(&o->current_directory_set, newpath) ||
|
2008-08-13 00:45:14 +08:00
|
|
|
lstat(newpath, &st) == 0)
|
|
|
|
sprintf(p, "_%d", suffix++);
|
|
|
|
|
2010-06-26 07:41:35 +08:00
|
|
|
string_list_insert(&o->current_file_set, newpath);
|
2008-08-13 00:45:14 +08:00
|
|
|
return newpath;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void flush_buffer(int fd, const char *buf, unsigned long size)
|
|
|
|
{
|
|
|
|
while (size > 0) {
|
|
|
|
long ret = write_in_full(fd, buf, size);
|
|
|
|
if (ret < 0) {
|
|
|
|
/* Ignore epipe */
|
|
|
|
if (errno == EPIPE)
|
|
|
|
break;
|
2009-06-27 23:58:46 +08:00
|
|
|
die_errno("merge-recursive");
|
2008-08-13 00:45:14 +08:00
|
|
|
} else if (!ret) {
|
|
|
|
die("merge-recursive: disk full?");
|
|
|
|
}
|
|
|
|
size -= ret;
|
|
|
|
buf += ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-15 18:41:24 +08:00
|
|
|
static int would_lose_untracked(const char *path)
|
|
|
|
{
|
|
|
|
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-08-13 00:45:14 +08:00
|
|
|
static int make_room_for_path(const char *path)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
const char *msg = "failed to create path '%s'%s";
|
|
|
|
|
|
|
|
status = safe_create_leading_directories_const(path);
|
|
|
|
if (status) {
|
|
|
|
if (status == -3) {
|
|
|
|
/* something else exists */
|
|
|
|
error(msg, path, ": perhaps a D/F conflict?");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
die(msg, path, "");
|
|
|
|
}
|
|
|
|
|
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))
|
|
|
|
return error("refusing to lose untracked file at '%s'",
|
|
|
|
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?) */
|
|
|
|
return error(msg, path, ": perhaps a D/F conflict?");
|
|
|
|
}
|
|
|
|
|
2008-09-03 05:53:47 +08:00
|
|
|
static void update_file_flags(struct merge_options *o,
|
|
|
|
const unsigned char *sha,
|
2008-08-13 00:45:14 +08:00
|
|
|
unsigned mode,
|
|
|
|
const char *path,
|
|
|
|
int update_cache,
|
|
|
|
int update_wd)
|
|
|
|
{
|
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
|
|
|
|
|
|
|
buf = read_sha1_file(sha, &type, &size);
|
|
|
|
if (!buf)
|
|
|
|
die("cannot read object %s '%s'", sha1_to_hex(sha), path);
|
|
|
|
if (type != OBJ_BLOB)
|
|
|
|
die("blob expected for %s '%s'", sha1_to_hex(sha), path);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (make_room_for_path(path) < 0) {
|
|
|
|
update_wd = 0;
|
|
|
|
free(buf);
|
|
|
|
goto update_index;
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
if (fd < 0)
|
2009-06-27 23:58:46 +08:00
|
|
|
die_errno("failed to open '%s'", path);
|
2008-08-13 00:45:14 +08:00
|
|
|
flush_buffer(fd, buf, size);
|
|
|
|
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))
|
2009-06-27 23:58:46 +08:00
|
|
|
die_errno("failed to symlink '%s'", path);
|
2008-08-13 00:45:14 +08:00
|
|
|
free(lnk);
|
|
|
|
} else
|
|
|
|
die("do not know what to do with %06o %s '%s'",
|
|
|
|
mode, sha1_to_hex(sha), path);
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
update_index:
|
|
|
|
if (update_cache)
|
|
|
|
add_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);
|
|
|
|
}
|
|
|
|
|
2008-09-03 05:53:47 +08:00
|
|
|
static void update_file(struct merge_options *o,
|
|
|
|
int clean,
|
2008-08-13 00:45:14 +08:00
|
|
|
const unsigned char *sha,
|
|
|
|
unsigned mode,
|
|
|
|
const char *path)
|
|
|
|
{
|
2008-09-03 05:53:47 +08:00
|
|
|
update_file_flags(o, sha, mode, path, o->call_depth || clean, !o->call_depth);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Low level file merging, update and removal */
|
|
|
|
|
|
|
|
struct merge_file_info
|
|
|
|
{
|
|
|
|
unsigned char sha[20];
|
|
|
|
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,
|
|
|
|
struct diff_filespec *one,
|
2008-08-13 00:45:14 +08:00
|
|
|
struct diff_filespec *a,
|
|
|
|
struct diff_filespec *b,
|
|
|
|
const char *branch1,
|
|
|
|
const char *branch2)
|
|
|
|
{
|
|
|
|
mmfile_t orig, src1, src2;
|
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
|
|
|
int favor;
|
|
|
|
|
|
|
|
if (o->call_depth)
|
|
|
|
favor = 0;
|
|
|
|
else {
|
|
|
|
switch (o->recursive_variant) {
|
|
|
|
case MERGE_RECURSIVE_OURS:
|
|
|
|
favor = XDL_MERGE_FAVOR_OURS;
|
|
|
|
break;
|
|
|
|
case MERGE_RECURSIVE_THEIRS:
|
|
|
|
favor = XDL_MERGE_FAVOR_THEIRS;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
favor = 0;
|
|
|
|
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 :
|
|
|
|
xstrdup(mkpath("%s:%s", o->ancestor, one->path));
|
2009-07-02 04:18:04 +08:00
|
|
|
name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
|
|
|
|
name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
|
|
|
|
} else {
|
2010-03-21 08:41:38 +08:00
|
|
|
base_name = o->ancestor == NULL ? NULL :
|
|
|
|
xstrdup(mkpath("%s", o->ancestor));
|
2009-07-02 04:18:04 +08:00
|
|
|
name1 = xstrdup(mkpath("%s", branch1));
|
|
|
|
name2 = xstrdup(mkpath("%s", branch2));
|
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2010-02-17 07:42:55 +08:00
|
|
|
read_mmblob(&orig, one->sha1);
|
|
|
|
read_mmblob(&src1, a->sha1);
|
|
|
|
read_mmblob(&src2, b->sha1);
|
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,
|
2008-08-13 00:45:14 +08:00
|
|
|
&src1, name1, &src2, name2,
|
2010-08-05 19:17:38 +08:00
|
|
|
((o->call_depth ? LL_OPT_VIRTUAL_ANCESTOR : 0) |
|
2010-08-05 19:24:58 +08:00
|
|
|
(o->renormalize ? LL_OPT_RENORMALIZE : 0) |
|
2010-08-05 19:17:38 +08:00
|
|
|
create_ll_flag(favor)));
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
free(name1);
|
|
|
|
free(name2);
|
|
|
|
free(orig.ptr);
|
|
|
|
free(src1.ptr);
|
|
|
|
free(src2.ptr);
|
|
|
|
return merge_status;
|
|
|
|
}
|
|
|
|
|
2008-09-03 05:53:47 +08:00
|
|
|
static struct merge_file_info merge_file(struct merge_options *o,
|
|
|
|
struct diff_filespec *one,
|
|
|
|
struct diff_filespec *a,
|
|
|
|
struct diff_filespec *b,
|
|
|
|
const char *branch1,
|
|
|
|
const char *branch2)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
|
|
|
struct merge_file_info result;
|
|
|
|
result.merge = 0;
|
|
|
|
result.clean = 1;
|
|
|
|
|
|
|
|
if ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {
|
|
|
|
result.clean = 0;
|
|
|
|
if (S_ISREG(a->mode)) {
|
|
|
|
result.mode = a->mode;
|
|
|
|
hashcpy(result.sha, a->sha1);
|
|
|
|
} else {
|
|
|
|
result.mode = b->mode;
|
|
|
|
hashcpy(result.sha, b->sha1);
|
|
|
|
}
|
|
|
|
} else {
|
2008-09-03 05:53:47 +08:00
|
|
|
if (!sha_eq(a->sha1, one->sha1) && !sha_eq(b->sha1, one->sha1))
|
2008-08-13 00:45:14 +08:00
|
|
|
result.merge = 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Merge modes
|
|
|
|
*/
|
2008-09-03 05:53:47 +08:00
|
|
|
if (a->mode == b->mode || a->mode == one->mode)
|
2008-08-13 00:45:14 +08:00
|
|
|
result.mode = b->mode;
|
|
|
|
else {
|
|
|
|
result.mode = a->mode;
|
2008-09-03 05:53:47 +08:00
|
|
|
if (b->mode != one->mode) {
|
2008-08-13 00:45:14 +08:00
|
|
|
result.clean = 0;
|
|
|
|
result.merge = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-03 05:53:47 +08:00
|
|
|
if (sha_eq(a->sha1, b->sha1) || sha_eq(a->sha1, one->sha1))
|
2008-08-13 00:45:14 +08:00
|
|
|
hashcpy(result.sha, b->sha1);
|
2008-09-03 05:53:47 +08:00
|
|
|
else if (sha_eq(b->sha1, one->sha1))
|
2008-08-13 00:45:14 +08:00
|
|
|
hashcpy(result.sha, a->sha1);
|
|
|
|
else if (S_ISREG(a->mode)) {
|
|
|
|
mmbuffer_t result_buf;
|
|
|
|
int merge_status;
|
|
|
|
|
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)
|
|
|
|
die("Failed to execute internal merge");
|
|
|
|
|
|
|
|
if (write_sha1_file(result_buf.ptr, result_buf.size,
|
|
|
|
blob_type, result.sha))
|
|
|
|
die("Unable to add %s to database",
|
|
|
|
a->path);
|
|
|
|
|
|
|
|
free(result_buf.ptr);
|
|
|
|
result.clean = (merge_status == 0);
|
|
|
|
} else if (S_ISGITLINK(a->mode)) {
|
2010-07-07 21:39:13 +08:00
|
|
|
result.clean = merge_submodule(result.sha, one->path, one->sha1,
|
|
|
|
a->sha1, b->sha1);
|
2008-08-13 00:45:14 +08:00
|
|
|
} else if (S_ISLNK(a->mode)) {
|
|
|
|
hashcpy(result.sha, a->sha1);
|
|
|
|
|
|
|
|
if (!sha_eq(a->sha1, b->sha1))
|
|
|
|
result.clean = 0;
|
|
|
|
} else {
|
|
|
|
die("unsupported object type in the tree");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2010-09-20 16:28:50 +08:00
|
|
|
static void conflict_rename_delete(struct merge_options *o,
|
|
|
|
struct diff_filepair *pair,
|
|
|
|
const char *rename_branch,
|
|
|
|
const char *other_branch)
|
|
|
|
{
|
|
|
|
char *dest_name = pair->two->path;
|
|
|
|
|
|
|
|
output(o, 1, "CONFLICT (rename/delete): Rename %s->%s in %s "
|
|
|
|
"and deleted in %s",
|
|
|
|
pair->one->path, pair->two->path, rename_branch,
|
|
|
|
other_branch);
|
|
|
|
if (!o->call_depth)
|
|
|
|
update_stages(dest_name, NULL,
|
|
|
|
rename_branch == o->branch1 ? pair->two : NULL,
|
|
|
|
rename_branch == o->branch1 ? NULL : pair->two,
|
|
|
|
1);
|
|
|
|
update_file(o, 0, pair->two->sha1, pair->two->mode, dest_name);
|
|
|
|
}
|
|
|
|
|
2010-09-20 16:28:48 +08:00
|
|
|
static void conflict_rename_rename_1to2(struct merge_options *o,
|
2010-09-20 16:28:57 +08:00
|
|
|
struct diff_filepair *pair1,
|
2010-09-20 16:28:48 +08:00
|
|
|
const char *branch1,
|
2010-09-20 16:28:57 +08:00
|
|
|
struct diff_filepair *pair2,
|
2010-09-20 16:28:48 +08:00
|
|
|
const char *branch2)
|
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. */
|
2008-08-13 00:45:14 +08:00
|
|
|
char *del[2];
|
|
|
|
int delp = 0;
|
2010-09-20 16:28:57 +08:00
|
|
|
const char *ren1_dst = pair1->two->path;
|
|
|
|
const char *ren2_dst = pair2->two->path;
|
2008-08-13 00:45:14 +08:00
|
|
|
const char *dst_name1 = ren1_dst;
|
|
|
|
const char *dst_name2 = ren2_dst;
|
2008-09-04 01:08:56 +08:00
|
|
|
if (string_list_has_string(&o->current_directory_set, ren1_dst)) {
|
|
|
|
dst_name1 = del[delp++] = unique_path(o, ren1_dst, branch1);
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 1, "%s is a directory in %s adding as %s instead",
|
2008-08-13 00:45:14 +08:00
|
|
|
ren1_dst, branch2, dst_name1);
|
2008-09-03 05:53:47 +08:00
|
|
|
remove_file(o, 0, ren1_dst, 0);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
2008-09-04 01:08:56 +08:00
|
|
|
if (string_list_has_string(&o->current_directory_set, ren2_dst)) {
|
|
|
|
dst_name2 = del[delp++] = unique_path(o, ren2_dst, branch2);
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 1, "%s is a directory in %s adding as %s instead",
|
2008-08-13 00:45:14 +08:00
|
|
|
ren2_dst, branch1, dst_name2);
|
2008-09-03 05:53:47 +08:00
|
|
|
remove_file(o, 0, ren2_dst, 0);
|
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
|
|
|
remove_file_from_cache(dst_name1);
|
|
|
|
remove_file_from_cache(dst_name2);
|
|
|
|
/*
|
|
|
|
* Uncomment to leave the conflicting names in the resulting tree
|
|
|
|
*
|
2010-09-20 16:28:57 +08:00
|
|
|
* update_file(o, 0, pair1->two->sha1, pair1->two->mode, dst_name1);
|
|
|
|
* update_file(o, 0, pair2->two->sha1, pair2->two->mode, dst_name2);
|
2008-08-13 00:45:14 +08:00
|
|
|
*/
|
|
|
|
} else {
|
2010-09-20 16:28:57 +08:00
|
|
|
update_stages(dst_name1, NULL, pair1->two, NULL, 1);
|
|
|
|
update_stages(dst_name2, NULL, NULL, pair2->two, 1);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
while (delp--)
|
|
|
|
free(del[delp]);
|
|
|
|
}
|
|
|
|
|
2010-09-20 16:28:48 +08:00
|
|
|
static void conflict_rename_rename_2to1(struct merge_options *o,
|
|
|
|
struct rename *ren1,
|
|
|
|
const char *branch1,
|
|
|
|
struct rename *ren2,
|
|
|
|
const char *branch2)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2010-09-20 16:28:48 +08:00
|
|
|
/* Two files were renamed to the same thing. */
|
2008-09-04 01:08:56 +08:00
|
|
|
char *new_path1 = unique_path(o, ren1->pair->two->path, branch1);
|
|
|
|
char *new_path2 = unique_path(o, ren2->pair->two->path, branch2);
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 1, "Renaming %s to %s and %s to %s instead",
|
2008-08-13 00:45:14 +08:00
|
|
|
ren1->pair->one->path, new_path1,
|
|
|
|
ren2->pair->one->path, new_path2);
|
2008-09-03 05:53:47 +08:00
|
|
|
remove_file(o, 0, ren1->pair->two->path, 0);
|
|
|
|
update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path1);
|
|
|
|
update_file(o, 0, ren2->pair->two->sha1, ren2->pair->two->mode, new_path2);
|
2008-08-13 00:45:14 +08:00
|
|
|
free(new_path2);
|
|
|
|
free(new_path1);
|
|
|
|
}
|
|
|
|
|
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
|
2008-08-13 00:45:14 +08:00
|
|
|
= sre->dst_entry;
|
|
|
|
}
|
|
|
|
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
|
2008-08-13 00:45:14 +08:00
|
|
|
= sre->dst_entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
|
|
|
|
char *src;
|
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;
|
|
|
|
|
|
|
|
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 {
|
|
|
|
struct rename *tmp;
|
|
|
|
renames1 = b_renames;
|
|
|
|
renames2Dst = &a_by_dst;
|
2008-08-25 22:25:57 +08:00
|
|
|
branch1 = o->branch2;
|
|
|
|
branch2 = o->branch1;
|
2008-08-13 00:45:14 +08:00
|
|
|
tmp = ren2;
|
|
|
|
ren2 = ren1;
|
|
|
|
ren1 = tmp;
|
|
|
|
}
|
|
|
|
src = ren1->pair->one->path;
|
|
|
|
|
|
|
|
ren1->dst_entry->processed = 1;
|
|
|
|
ren1->src_entry->processed = 1;
|
|
|
|
|
|
|
|
if (ren1->processed)
|
|
|
|
continue;
|
|
|
|
ren1->processed = 1;
|
|
|
|
|
|
|
|
ren1_src = ren1->pair->one->path;
|
|
|
|
ren1_dst = ren1->pair->two->path;
|
|
|
|
|
|
|
|
if (ren2) {
|
|
|
|
const char *ren2_src = ren2->pair->one->path;
|
|
|
|
const char *ren2_dst = ren2->pair->two->path;
|
|
|
|
/* Renamed in 1 and renamed in 2 */
|
|
|
|
if (strcmp(ren1_src, ren2_src) != 0)
|
|
|
|
die("ren1.src != ren2.src");
|
|
|
|
ren2->dst_entry->processed = 1;
|
|
|
|
ren2->processed = 1;
|
|
|
|
if (strcmp(ren1_dst, ren2_dst) != 0) {
|
|
|
|
clean_merge = 0;
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 1, "CONFLICT (rename/rename): "
|
2008-08-13 00:45:14 +08:00
|
|
|
"Rename \"%s\"->\"%s\" in branch \"%s\" "
|
|
|
|
"rename \"%s\"->\"%s\" in \"%s\"%s",
|
|
|
|
src, ren1_dst, branch1,
|
|
|
|
src, ren2_dst, branch2,
|
2008-09-03 05:53:47 +08:00
|
|
|
o->call_depth ? " (left unresolved)": "");
|
|
|
|
if (o->call_depth) {
|
2008-08-13 00:45:14 +08:00
|
|
|
remove_file_from_cache(src);
|
2008-09-03 05:53:47 +08:00
|
|
|
update_file(o, 0, ren1->pair->one->sha1,
|
2008-08-13 00:45:14 +08:00
|
|
|
ren1->pair->one->mode, src);
|
|
|
|
}
|
2010-09-20 16:28:57 +08:00
|
|
|
conflict_rename_rename_1to2(o, ren1->pair, branch1, ren2->pair, branch2);
|
2008-08-13 00:45:14 +08:00
|
|
|
} else {
|
|
|
|
struct merge_file_info mfi;
|
2008-09-03 05:53:47 +08:00
|
|
|
remove_file(o, 1, ren1_src, 1);
|
|
|
|
mfi = merge_file(o,
|
|
|
|
ren1->pair->one,
|
2008-08-13 00:45:14 +08:00
|
|
|
ren1->pair->two,
|
|
|
|
ren2->pair->two,
|
|
|
|
branch1,
|
|
|
|
branch2);
|
|
|
|
if (mfi.merge || !mfi.clean)
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 1, "Renaming %s->%s", src, ren1_dst);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
if (mfi.merge)
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 2, "Auto-merging %s", ren1_dst);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
if (!mfi.clean) {
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 1, "CONFLICT (content): merge conflict in %s",
|
2008-08-13 00:45:14 +08:00
|
|
|
ren1_dst);
|
|
|
|
clean_merge = 0;
|
|
|
|
|
2008-09-03 05:53:47 +08:00
|
|
|
if (!o->call_depth)
|
2008-08-13 00:45:14 +08:00
|
|
|
update_stages(ren1_dst,
|
|
|
|
ren1->pair->one,
|
|
|
|
ren1->pair->two,
|
|
|
|
ren2->pair->two,
|
|
|
|
1 /* clear */);
|
|
|
|
}
|
2008-09-03 05:53:47 +08:00
|
|
|
update_file(o, mfi.clean, mfi.sha, mfi.mode, ren1_dst);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Renamed in 1, maybe changed in 2 */
|
|
|
|
struct string_list_item *item;
|
|
|
|
/* 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
|
|
|
|
2010-09-20 16:28:47 +08:00
|
|
|
remove_file(o, 1, ren1_src, o->call_depth || renamed_stage == 2);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
2010-09-20 16:28:47 +08:00
|
|
|
hashcpy(src_other.sha1, ren1->src_entry->stages[other_stage].sha);
|
|
|
|
src_other.mode = ren1->src_entry->stages[other_stage].mode;
|
|
|
|
hashcpy(dst_other.sha1, ren1->dst_entry->stages[other_stage].sha);
|
|
|
|
dst_other.mode = ren1->dst_entry->stages[other_stage].mode;
|
2008-08-13 00:45:14 +08:00
|
|
|
try_merge = 0;
|
|
|
|
|
merge-recursive: D/F conflicts where was_a_dir/file -> was_a_dir
In merge-recursive.c, whenever there was a rename where a file name on one
side of the rename matches a directory name on the other side of the merge,
then the very first check that
string_list_has_string(&o->current_directory_set, ren1_dst)
would trigger forcing it into marking it as a rename/directory conflict.
However, if the path is only renamed on one side and a simple three-way
merge between the separate files resolves cleanly, then we don't need to
mark it as a rename/directory conflict. So, we can simply move the check
for rename/directory conflicts after we've verified that there isn't a
rename/rename conflict and that a threeway content merge doesn't work.
This changes the particular error message one gets in the case where the
directory name that a file on one side of the rename matches is not also
part of the rename pair. For example, with commits containing the files:
COMMON -> (HEAD, MERGE )
--------- --------------- -------
sub/file1 -> (sub/file1, newsub)
<NULL> -> (newsub/newfile, <NULL>)
then previously when one tried to merge MERGE into HEAD, one would get
CONFLICT (rename/directory): Rename sub/file1->newsub in HEAD directory newsub added in merge
Renaming sub/file1 to newsub~HEAD instead
Adding newsub/newfile
Automatic merge failed; fix conflicts and then commit the result.
After this patch, the error message will instead become:
Removing newsub
Adding newsub/newfile
CONFLICT (file/directory): There is a directory with name newsub in merge. Adding newsub as newsub~HEAD
Automatic merge failed; fix conflicts and then commit the result.
That makes more sense to me, because git can't know that there's a conflict
until after it's tried resolving paths involving newsub/newfile to see if
they are still in the way at the end (and if newsub/newfile is not in the
way at the end, there should be no conflict at all, which did not hold with
git previously).
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-09-08 14:40:41 +08:00
|
|
|
if (sha_eq(src_other.sha1, null_sha1)) {
|
2008-08-13 00:45:14 +08:00
|
|
|
clean_merge = 0;
|
2010-09-20 16:28:50 +08:00
|
|
|
conflict_rename_delete(o, ren1->pair, branch1, branch2);
|
2010-09-02 04:15:32 +08:00
|
|
|
} else if ((dst_other.mode == ren1->pair->two->mode) &&
|
|
|
|
sha_eq(dst_other.sha1, ren1->pair->two->sha1)) {
|
|
|
|
/* Added file on the other side
|
|
|
|
identical to the file being
|
|
|
|
renamed: clean merge */
|
|
|
|
update_file(o, 1, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
|
2008-08-13 00:45:14 +08:00
|
|
|
} else if (!sha_eq(dst_other.sha1, null_sha1)) {
|
|
|
|
const char *new_path;
|
|
|
|
clean_merge = 0;
|
|
|
|
try_merge = 1;
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 1, "CONFLICT (rename/add): Rename %s->%s in %s. "
|
2008-08-13 00:45:14 +08:00
|
|
|
"%s added in %s",
|
|
|
|
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;
|
|
|
|
struct diff_filespec one, a, b;
|
|
|
|
|
|
|
|
one.path = a.path = b.path =
|
|
|
|
(char *)ren1_dst;
|
|
|
|
hashcpy(one.sha1, null_sha1);
|
|
|
|
one.mode = 0;
|
|
|
|
hashcpy(a.sha1, ren1->pair->two->sha1);
|
|
|
|
a.mode = ren1->pair->two->mode;
|
|
|
|
hashcpy(b.sha1, dst_other.sha1);
|
|
|
|
b.mode = dst_other.mode;
|
|
|
|
mfi = merge_file(o, &one, &a, &b,
|
|
|
|
branch1,
|
|
|
|
branch2);
|
|
|
|
output(o, 1, "Adding merged %s", ren1_dst);
|
|
|
|
update_file(o, 0,
|
|
|
|
mfi.sha,
|
|
|
|
mfi.mode,
|
|
|
|
ren1_dst);
|
|
|
|
} else {
|
|
|
|
new_path = unique_path(o, ren1_dst, branch2);
|
|
|
|
output(o, 1, "Adding as %s instead", new_path);
|
|
|
|
update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
|
|
|
|
}
|
2010-06-26 07:41:37 +08:00
|
|
|
} else if ((item = string_list_lookup(renames2Dst, ren1_dst))) {
|
2008-08-13 00:45:14 +08:00
|
|
|
ren2 = item->util;
|
|
|
|
clean_merge = 0;
|
|
|
|
ren2->processed = 1;
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 1, "CONFLICT (rename/rename): "
|
|
|
|
"Rename %s->%s in %s. "
|
2008-08-13 00:45:14 +08:00
|
|
|
"Rename %s->%s in %s",
|
|
|
|
ren1_src, ren1_dst, branch1,
|
|
|
|
ren2->pair->one->path, ren2->pair->two->path, branch2);
|
2010-09-20 16:28:48 +08:00
|
|
|
conflict_rename_rename_2to1(o, ren1, branch1, ren2, branch2);
|
2008-08-13 00:45:14 +08:00
|
|
|
} else
|
|
|
|
try_merge = 1;
|
|
|
|
|
|
|
|
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
|
|
|
struct merge_file_info mfi;
|
|
|
|
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;
|
|
|
|
}
|
2008-09-03 05:53:47 +08:00
|
|
|
mfi = merge_file(o, one, a, b,
|
2008-08-25 22:25:57 +08:00
|
|
|
o->branch1, o->branch2);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
if (mfi.clean &&
|
|
|
|
sha_eq(mfi.sha, ren1->pair->two->sha1) &&
|
2010-07-09 21:10:54 +08:00
|
|
|
mfi.mode == ren1->pair->two->mode) {
|
2008-08-13 00:45:14 +08:00
|
|
|
/*
|
2010-08-18 07:52:27 +08:00
|
|
|
* This message is part of
|
2008-08-13 00:45:14 +08:00
|
|
|
* t6022 test. If you change
|
|
|
|
* it update the test too.
|
|
|
|
*/
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 3, "Skipped %s (merged same as existing)", ren1_dst);
|
2010-07-09 21:10:54 +08:00
|
|
|
|
|
|
|
/* There may be higher stage entries left
|
|
|
|
* in the index (e.g. due to a D/F
|
|
|
|
* conflict) that need to be resolved.
|
|
|
|
*/
|
2010-08-20 20:55:40 +08:00
|
|
|
if (!ren1->dst_entry->stages[2].mode !=
|
|
|
|
!ren1->dst_entry->stages[3].mode)
|
2010-07-09 21:10:54 +08:00
|
|
|
ren1->dst_entry->processed = 0;
|
|
|
|
} else {
|
2008-08-13 00:45:14 +08:00
|
|
|
if (mfi.merge || !mfi.clean)
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 1, "Renaming %s => %s", ren1_src, ren1_dst);
|
2008-08-13 00:45:14 +08:00
|
|
|
if (mfi.merge)
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 2, "Auto-merging %s", ren1_dst);
|
2008-08-13 00:45:14 +08:00
|
|
|
if (!mfi.clean) {
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 1, "CONFLICT (rename/modify): Merge conflict in %s",
|
2008-08-13 00:45:14 +08:00
|
|
|
ren1_dst);
|
|
|
|
clean_merge = 0;
|
|
|
|
|
2008-09-03 05:53:47 +08:00
|
|
|
if (!o->call_depth)
|
2008-08-13 00:45:14 +08:00
|
|
|
update_stages(ren1_dst,
|
2008-08-25 22:25:57 +08:00
|
|
|
one, a, b, 1);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
2008-09-03 05:53:47 +08:00
|
|
|
update_file(o, mfi.clean, mfi.sha, mfi.mode, ren1_dst);
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned char *stage_sha(const unsigned char *sha, unsigned mode)
|
|
|
|
{
|
|
|
|
return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;
|
|
|
|
}
|
|
|
|
|
2010-07-03 03:20:48 +08:00
|
|
|
static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst)
|
|
|
|
{
|
|
|
|
void *buf;
|
|
|
|
enum object_type type;
|
|
|
|
unsigned long size;
|
|
|
|
buf = read_sha1_file(sha1, &type, &size);
|
|
|
|
if (!buf)
|
|
|
|
return error("cannot read object %s", sha1_to_hex(sha1));
|
|
|
|
if (type != OBJ_BLOB) {
|
|
|
|
free(buf);
|
|
|
|
return error("object %s is not a blob", sha1_to_hex(sha1));
|
|
|
|
}
|
|
|
|
strbuf_attach(dst, buf, size, size + 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int blob_unchanged(const unsigned char *o_sha,
|
|
|
|
const unsigned char *a_sha,
|
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 */
|
|
|
|
|
|
|
|
if (sha_eq(o_sha, a_sha))
|
|
|
|
return 1;
|
2010-08-05 19:13:49 +08:00
|
|
|
if (!renormalize)
|
2010-07-03 03:20:48 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
assert(o_sha && a_sha);
|
|
|
|
if (read_sha1_strbuf(o_sha, &o) || read_sha1_strbuf(a_sha, &a))
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
if (renormalize_buffer(path, o.buf, o.len, &o) |
|
|
|
|
renormalize_buffer(path, a.buf, o.len, &a))
|
|
|
|
ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));
|
|
|
|
|
|
|
|
error_return:
|
|
|
|
strbuf_release(&o);
|
|
|
|
strbuf_release(&a);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-09-20 16:28:51 +08:00
|
|
|
static void handle_delete_modify(struct merge_options *o,
|
|
|
|
const char *path,
|
|
|
|
unsigned char *a_sha, int a_mode,
|
|
|
|
unsigned char *b_sha, int b_mode)
|
|
|
|
{
|
|
|
|
if (!a_sha) {
|
|
|
|
output(o, 1, "CONFLICT (delete/modify): %s deleted in %s "
|
|
|
|
"and modified in %s. Version %s of %s left in tree.",
|
|
|
|
path, o->branch1,
|
|
|
|
o->branch2, o->branch2, path);
|
|
|
|
update_file(o, 0, b_sha, b_mode, path);
|
|
|
|
} else {
|
|
|
|
output(o, 1, "CONFLICT (delete/modify): %s deleted in %s "
|
|
|
|
"and modified in %s. Version %s of %s left in tree.",
|
|
|
|
path, o->branch2,
|
|
|
|
o->branch1, o->branch1, path);
|
|
|
|
update_file(o, 0, a_sha, a_mode, path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-20 16:28:52 +08:00
|
|
|
|
|
|
|
static int merge_content(struct merge_options *o,
|
|
|
|
const char *path,
|
|
|
|
unsigned char *o_sha, int o_mode,
|
|
|
|
unsigned char *a_sha, int a_mode,
|
2010-09-20 16:28:58 +08:00
|
|
|
unsigned char *b_sha, int b_mode,
|
|
|
|
const char *df_rename_conflict_branch)
|
2010-09-20 16:28:52 +08:00
|
|
|
{
|
|
|
|
const char *reason = "content";
|
|
|
|
struct merge_file_info mfi;
|
|
|
|
struct diff_filespec one, a, b;
|
|
|
|
|
|
|
|
if (!o_sha) {
|
|
|
|
reason = "add/add";
|
|
|
|
o_sha = (unsigned char *)null_sha1;
|
|
|
|
}
|
|
|
|
one.path = a.path = b.path = (char *)path;
|
|
|
|
hashcpy(one.sha1, o_sha);
|
|
|
|
one.mode = o_mode;
|
|
|
|
hashcpy(a.sha1, a_sha);
|
|
|
|
a.mode = a_mode;
|
|
|
|
hashcpy(b.sha1, b_sha);
|
|
|
|
b.mode = b_mode;
|
|
|
|
|
|
|
|
mfi = merge_file(o, &one, &a, &b, o->branch1, o->branch2);
|
|
|
|
if (mfi.clean && sha_eq(mfi.sha, a_sha) && mfi.mode == a.mode)
|
|
|
|
output(o, 3, "Skipped %s (merged same as existing)", path);
|
|
|
|
else
|
|
|
|
output(o, 2, "Auto-merging %s", path);
|
|
|
|
|
|
|
|
if (!mfi.clean) {
|
|
|
|
if (S_ISGITLINK(mfi.mode))
|
|
|
|
reason = "submodule";
|
|
|
|
output(o, 1, "CONFLICT (%s): Merge conflict in %s",
|
|
|
|
reason, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
update_file(o, mfi.clean, mfi.sha, mfi.mode, path);
|
|
|
|
return mfi.clean;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
/*
|
|
|
|
printf("processing entry, clean cache: %s\n", index_only ? "yes": "no");
|
|
|
|
print_index_entry("\tpath: ", entry);
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
unsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode);
|
|
|
|
unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
|
|
|
|
unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
|
|
|
|
|
2010-09-20 16:28:55 +08:00
|
|
|
if (entry->rename_df_conflict_info)
|
|
|
|
return 1; /* Such cases are handled elsewhere. */
|
|
|
|
|
2010-07-09 21:10:53 +08:00
|
|
|
entry->processed = 1;
|
2008-08-13 00:45:14 +08:00
|
|
|
if (o_sha && (!a_sha || !b_sha)) {
|
|
|
|
/* Case A: Deleted in one */
|
|
|
|
if ((!a_sha && !b_sha) ||
|
2010-08-05 19:13:49 +08:00
|
|
|
(!b_sha && blob_unchanged(o_sha, a_sha, normalize, path)) ||
|
|
|
|
(!a_sha && blob_unchanged(o_sha, b_sha, normalize, path))) {
|
2008-08-13 00:45:14 +08:00
|
|
|
/* Deleted in both or deleted in one and
|
|
|
|
* unchanged in the other */
|
|
|
|
if (a_sha)
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 2, "Removing %s", path);
|
2008-08-13 00:45:14 +08:00
|
|
|
/* do not touch working file if it did not exist */
|
2008-09-03 05:53:47 +08:00
|
|
|
remove_file(o, 1, path, !a_sha);
|
2008-08-13 00:45:14 +08:00
|
|
|
} else {
|
|
|
|
/* Deleted in one and changed in the other */
|
|
|
|
clean_merge = 0;
|
2010-09-20 16:28:51 +08:00
|
|
|
handle_delete_modify(o, path,
|
|
|
|
a_sha, a_mode, b_sha, b_mode);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
} else if ((!o_sha && a_sha && !b_sha) ||
|
|
|
|
(!o_sha && !a_sha && b_sha)) {
|
|
|
|
/* Case B: Added in one. */
|
|
|
|
unsigned mode;
|
|
|
|
const unsigned char *sha;
|
|
|
|
|
|
|
|
if (a_sha) {
|
|
|
|
mode = a_mode;
|
|
|
|
sha = a_sha;
|
|
|
|
} else {
|
|
|
|
mode = b_mode;
|
|
|
|
sha = b_sha;
|
|
|
|
}
|
2008-09-04 01:08:56 +08:00
|
|
|
if (string_list_has_string(&o->current_directory_set, path)) {
|
2010-07-09 21:10:53 +08:00
|
|
|
/* Handle D->F conflicts after all subfiles */
|
|
|
|
entry->processed = 0;
|
|
|
|
/* But get any file out of the way now, so conflicted
|
|
|
|
* entries below the directory of the same name can
|
|
|
|
* be put in the working directory.
|
|
|
|
*/
|
|
|
|
if (a_sha)
|
|
|
|
output(o, 2, "Removing %s", path);
|
|
|
|
/* do not touch working file if it did not exist */
|
|
|
|
remove_file(o, 0, path, !a_sha);
|
|
|
|
return 1; /* Assume clean till processed */
|
2008-08-13 00:45:14 +08:00
|
|
|
} else {
|
2008-08-25 22:25:57 +08:00
|
|
|
output(o, 2, "Adding %s", path);
|
2008-09-03 05:53:47 +08:00
|
|
|
update_file(o, 1, sha, mode, path);
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|
|
|
|
} else if (a_sha && b_sha) {
|
|
|
|
/* Case C: Added in both (check for same permissions) and */
|
|
|
|
/* case D: Modified in both, but differently. */
|
2010-09-20 16:28:52 +08:00
|
|
|
clean_merge = merge_content(o, path,
|
2010-09-20 16:28:58 +08:00
|
|
|
o_sha, o_mode, a_sha, a_mode, b_sha, b_mode,
|
|
|
|
NULL);
|
2008-08-13 00:45:14 +08:00
|
|
|
} else if (!o_sha && !a_sha && !b_sha) {
|
|
|
|
/*
|
|
|
|
* this entry was deleted altogether. a_mode == 0 means
|
|
|
|
* we had that path and want to actively remove it.
|
|
|
|
*/
|
2008-09-03 05:53:47 +08:00
|
|
|
remove_file(o, 1, path, !a_mode);
|
2008-08-13 00:45:14 +08:00
|
|
|
} else
|
|
|
|
die("Fatal merge failure, shouldn't happen.");
|
|
|
|
|
|
|
|
return clean_merge;
|
|
|
|
}
|
|
|
|
|
2010-07-09 21:10:53 +08:00
|
|
|
/*
|
2010-09-20 16:28:56 +08:00
|
|
|
* Per entry merge function for D/F (and/or rename) conflicts. In the
|
|
|
|
* cases we can cleanly resolve D/F conflicts, process_entry() can
|
|
|
|
* clean out all the files below the directory for us. All D/F
|
|
|
|
* conflict cases must be handled here at the end to make sure any
|
|
|
|
* directories that can be cleaned out, are.
|
|
|
|
*
|
|
|
|
* Some rename conflicts may also be handled here that don't necessarily
|
|
|
|
* involve D/F conflicts, since the code to handle them is generic enough
|
|
|
|
* to handle those rename conflicts with or without D/F conflicts also
|
|
|
|
* being involved.
|
2010-07-09 21:10:53 +08:00
|
|
|
*/
|
|
|
|
static int process_df_entry(struct merge_options *o,
|
2010-09-20 16:28:56 +08:00
|
|
|
const char *path, struct stage_data *entry)
|
2010-07-09 21:10:53 +08:00
|
|
|
{
|
|
|
|
int clean_merge = 1;
|
|
|
|
unsigned o_mode = entry->stages[1].mode;
|
|
|
|
unsigned a_mode = entry->stages[2].mode;
|
|
|
|
unsigned b_mode = entry->stages[3].mode;
|
|
|
|
unsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode);
|
|
|
|
unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
|
|
|
|
unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
entry->processed = 1;
|
2010-09-20 16:28:56 +08:00
|
|
|
if (entry->rename_df_conflict_info) {
|
|
|
|
die("Not yet implemented.");
|
|
|
|
} else if (!o_sha && !!a_sha != !!b_sha) {
|
|
|
|
/* directory -> (directory, file) */
|
|
|
|
const char *add_branch;
|
|
|
|
const char *other_branch;
|
|
|
|
unsigned mode;
|
|
|
|
const unsigned char *sha;
|
|
|
|
const char *conf;
|
2010-07-09 21:10:53 +08:00
|
|
|
|
2010-09-20 16:28:56 +08:00
|
|
|
if (a_sha) {
|
|
|
|
add_branch = o->branch1;
|
|
|
|
other_branch = o->branch2;
|
|
|
|
mode = a_mode;
|
|
|
|
sha = a_sha;
|
|
|
|
conf = "file/directory";
|
|
|
|
} else {
|
|
|
|
add_branch = o->branch2;
|
|
|
|
other_branch = o->branch1;
|
|
|
|
mode = b_mode;
|
|
|
|
sha = b_sha;
|
|
|
|
conf = "directory/file";
|
|
|
|
}
|
|
|
|
if (lstat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
|
|
|
const char *new_path = unique_path(o, path, add_branch);
|
|
|
|
clean_merge = 0;
|
|
|
|
output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. "
|
|
|
|
"Adding %s as %s",
|
|
|
|
conf, path, other_branch, path, new_path);
|
|
|
|
remove_file(o, 0, path, 0);
|
|
|
|
update_file(o, 0, sha, mode, new_path);
|
|
|
|
} else {
|
|
|
|
output(o, 2, "Adding %s", path);
|
|
|
|
update_file(o, 1, sha, mode, path);
|
|
|
|
}
|
2010-07-09 21:10:53 +08:00
|
|
|
} else {
|
2010-09-20 16:28:56 +08:00
|
|
|
entry->processed = 0;
|
|
|
|
return 1; /* not handled; assume clean until processed */
|
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
|
|
|
}
|
|
|
|
|
|
|
|
if (sha_eq(common->object.sha1, merge->object.sha1)) {
|
2010-07-10 04:27:49 +08:00
|
|
|
output(o, 0, "Already up-to-date!");
|
2008-08-13 00:45:14 +08:00
|
|
|
*result = head;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2008-09-03 05:53:47 +08:00
|
|
|
code = git_merge_trees(o->call_depth, 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)
|
|
|
|
die("merging of trees %s and %s failed",
|
|
|
|
sha1_to_hex(head->object.sha1),
|
|
|
|
sha1_to_hex(merge->object.sha1));
|
|
|
|
else
|
|
|
|
exit(128);
|
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
if (unmerged_cache()) {
|
|
|
|
struct string_list *entries, *re_head, *re_merge;
|
|
|
|
int i;
|
2008-09-04 01:08:56 +08:00
|
|
|
string_list_clear(&o->current_file_set, 1);
|
|
|
|
string_list_clear(&o->current_directory_set, 1);
|
|
|
|
get_files_dirs(o, head);
|
|
|
|
get_files_dirs(o, merge);
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
entries = get_unmerged();
|
2008-08-25 22:25:57 +08:00
|
|
|
re_head = get_renames(o, head, common, head, merge, entries);
|
|
|
|
re_merge = get_renames(o, merge, common, head, merge, entries);
|
|
|
|
clean = process_renames(o, re_head, re_merge);
|
2008-08-13 00:45:14 +08:00
|
|
|
for (i = 0; i < entries->nr; i++) {
|
|
|
|
const char *path = entries->items[i].string;
|
|
|
|
struct stage_data *e = entries->items[i].util;
|
|
|
|
if (!e->processed
|
2008-08-25 22:25:57 +08:00
|
|
|
&& !process_entry(o, path, e))
|
2008-08-13 00:45:14 +08:00
|
|
|
clean = 0;
|
|
|
|
}
|
2010-07-09 21:10:53 +08:00
|
|
|
for (i = 0; i < entries->nr; i++) {
|
|
|
|
const char *path = entries->items[i].string;
|
|
|
|
struct stage_data *e = entries->items[i].util;
|
|
|
|
if (!e->processed
|
|
|
|
&& !process_df_entry(o, path, e))
|
|
|
|
clean = 0;
|
|
|
|
}
|
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)
|
|
|
|
die("Unprocessed path??? %s",
|
|
|
|
entries->items[i].string);
|
|
|
|
}
|
2008-08-13 00:45:14 +08:00
|
|
|
|
|
|
|
string_list_clear(re_merge, 0);
|
|
|
|
string_list_clear(re_head, 0);
|
|
|
|
string_list_clear(entries, 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
clean = 1;
|
|
|
|
|
2008-09-03 05:53:47 +08:00
|
|
|
if (o->call_depth)
|
2008-08-25 22:25:57 +08:00
|
|
|
*result = write_tree_from_memory(o);
|
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;
|
|
|
|
struct tree *mrtree = mrtree;
|
|
|
|
int clean;
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
if (show(o, 4)) {
|
|
|
|
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) {
|
|
|
|
ca = get_merge_bases(h1, h2, 1);
|
|
|
|
ca = reverse_commit_list(ca);
|
|
|
|
}
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
if (show(o, 5)) {
|
|
|
|
output(o, 5, "found %u common ancestor(s):", commit_list_count(ca));
|
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) {
|
|
|
|
/* if there is no common ancestor, make an empty tree */
|
|
|
|
struct tree *tree = xcalloc(1, sizeof(struct tree));
|
|
|
|
|
|
|
|
tree->object.parsed = 1;
|
|
|
|
tree->object.type = OBJ_TREE;
|
|
|
|
pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1);
|
|
|
|
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
|
|
|
|
* ignored, it was never actually used, as result of
|
|
|
|
* merge_trees has always overwritten it: the committed
|
|
|
|
* "conflicts" were already resolved.
|
|
|
|
*/
|
|
|
|
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";
|
|
|
|
merge_recursive(o, merged_common_ancestors, iter->item,
|
|
|
|
NULL, &merged_common_ancestors);
|
|
|
|
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)
|
|
|
|
die("merge returned no commit");
|
|
|
|
}
|
|
|
|
|
|
|
|
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";
|
2008-08-25 22:25:57 +08:00
|
|
|
clean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,
|
|
|
|
&mrtree);
|
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);
|
2008-08-13 00:45:14 +08:00
|
|
|
return clean;
|
|
|
|
}
|
|
|
|
|
2008-08-13 04:13:59 +08:00
|
|
|
static struct commit *get_ref(const unsigned char *sha1, const char *name)
|
|
|
|
{
|
|
|
|
struct object *object;
|
|
|
|
|
|
|
|
object = deref_tag(parse_object(sha1), name, strlen(name));
|
|
|
|
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,
|
|
|
|
const unsigned char *head,
|
|
|
|
const unsigned char *merge,
|
|
|
|
int num_base_list,
|
|
|
|
const unsigned char **base_list,
|
|
|
|
struct commit **result)
|
2008-08-13 04:13:59 +08:00
|
|
|
{
|
|
|
|
int clean, index_fd;
|
|
|
|
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
|
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;
|
2008-08-25 22:25:57 +08:00
|
|
|
if (!(base = get_ref(base_list[i], sha1_to_hex(base_list[i]))))
|
2008-08-13 04:13:59 +08:00
|
|
|
return error("Could not parse object '%s'",
|
2008-08-25 22:25:57 +08:00
|
|
|
sha1_to_hex(base_list[i]));
|
2008-08-13 04:13:59 +08:00
|
|
|
commit_list_insert(base, &ca);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
index_fd = hold_locked_index(lock, 1);
|
2008-08-25 22:25:57 +08:00
|
|
|
clean = merge_recursive(o, head_commit, next_commit, ca,
|
|
|
|
result);
|
2008-08-13 04:13:59 +08:00
|
|
|
if (active_cache_changed &&
|
|
|
|
(write_cache(index_fd, active_cache, active_nr) ||
|
|
|
|
commit_locked_index(lock)))
|
|
|
|
return error("Unable to write index.");
|
|
|
|
|
|
|
|
return clean ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
2008-08-25 22:25:57 +08:00
|
|
|
static int merge_recursive_config(const char *var, const char *value, void *cb)
|
2008-08-13 00:45:14 +08:00
|
|
|
{
|
2008-08-25 22:25:57 +08:00
|
|
|
struct merge_options *o = cb;
|
2008-08-13 00:45:14 +08:00
|
|
|
if (!strcasecmp(var, "merge.verbosity")) {
|
2008-08-25 22:25:57 +08:00
|
|
|
o->verbosity = git_config_int(var, value);
|
2008-08-13 00:45:14 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcasecmp(var, "diff.renamelimit")) {
|
2008-08-25 22:25:57 +08:00
|
|
|
o->diff_rename_limit = git_config_int(var, value);
|
2008-08-13 00:45:14 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcasecmp(var, "merge.renamelimit")) {
|
2008-08-25 22:25:57 +08:00
|
|
|
o->merge_rename_limit = git_config_int(var, value);
|
2008-08-13 00:45:14 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2008-10-19 08:40:50 +08:00
|
|
|
return git_xmerge_config(var, value, cb);
|
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
|
|
|
{
|
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;
|
2008-08-25 22:25:57 +08:00
|
|
|
git_config(merge_recursive_config, o);
|
2008-08-13 00:45:14 +08:00
|
|
|
if (getenv("GIT_MERGE_VERBOSITY"))
|
2008-08-25 22:25:57 +08:00
|
|
|
o->verbosity =
|
2008-08-13 00:45:14 +08:00
|
|
|
strtol(getenv("GIT_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);
|
2008-09-04 01:08:56 +08:00
|
|
|
memset(&o->current_file_set, 0, sizeof(struct string_list));
|
|
|
|
o->current_file_set.strdup_strings = 1;
|
|
|
|
memset(&o->current_directory_set, 0, sizeof(struct string_list));
|
|
|
|
o->current_directory_set.strdup_strings = 1;
|
2008-08-13 00:45:14 +08:00
|
|
|
}
|