Esempio n. 1
0
int
diff_merge (SeafCommit *merge, GList **results)
{
    SeafCommit *parent1, *parent2;
    struct tree_desc t[3];
    struct unpack_trees_options opts;
    struct index_state istate;

    g_assert (*results == NULL);
    g_assert (merge->parent_id != NULL && merge->second_parent_id != NULL);

    parent1 = seaf_commit_manager_get_commit (seaf->commit_mgr,
                                              merge->parent_id);
    if (!parent1) {
        seaf_warning ("failed to find commit %s.\n", merge->parent_id);
        return -1;
    }

    parent2 = seaf_commit_manager_get_commit (seaf->commit_mgr,
                                              merge->second_parent_id);
    if (!parent2) {
        seaf_warning ("failed to find commit %s.\n", merge->second_parent_id);
        seaf_commit_unref (parent1);
        return -1;
    }

    fill_tree_descriptor(&t[0], merge->root_id);
    fill_tree_descriptor(&t[1], parent1->root_id);
    fill_tree_descriptor(&t[2], parent2->root_id);

    seaf_commit_unref (parent1);
    seaf_commit_unref (parent2);

    /* Empty index */
    memset(&istate, 0, sizeof(istate));
    memset(&opts, 0, sizeof(opts));

    opts.head_idx = -1;
    opts.index_only = 1;
    opts.merge = 1;
    opts.fn = threeway_diff;
    opts.unpack_data = results;
    opts.src_index = &istate;
    opts.dst_index = NULL;

    if (unpack_trees(3, t, &opts) < 0) {
        seaf_warning ("failed to unpack trees.\n");
        return -1;
    }

    if (*results != NULL)
        diff_resolve_renames (results);

    tree_desc_free (&t[0]);
    tree_desc_free (&t[1]);
    tree_desc_free (&t[2]);

    return 0;
}
Esempio n. 2
0
int
diff_commits (SeafCommit *commit1, SeafCommit *commit2, GList **results)
{
    struct tree_desc t[2];
    struct unpack_trees_options opts;
    struct index_state istate;

    g_assert (*results == NULL);

    if (strcmp (commit1->commit_id, commit2->commit_id) == 0)
        return 0;

    if (strcmp (commit1->root_id, EMPTY_SHA1) != 0) {
        fill_tree_descriptor(&t[0], commit1->root_id);
    } else {
        fill_tree_descriptor(&t[0], NULL);
    }

    if (strcmp (commit2->root_id, EMPTY_SHA1) != 0) {
        fill_tree_descriptor(&t[1], commit2->root_id);
    } else {
        fill_tree_descriptor(&t[1], NULL);
    }

    /* Empty index */
    memset(&istate, 0, sizeof(istate));
    memset(&opts, 0, sizeof(opts));

    opts.head_idx = -1;
    opts.index_only = 1;
    opts.merge = 1;
    opts.fn = twoway_diff;
    opts.unpack_data = results;
    opts.src_index = &istate;
    opts.dst_index = NULL;

    if (unpack_trees(2, t, &opts) < 0) {
        seaf_warning ("failed to unpack trees.\n");
        return -1;
    }

    if (results != NULL)
        diff_resolve_empty_dirs (results);

    if (*results != NULL)
        diff_resolve_renames (results);

    tree_desc_free (&t[0]);
    tree_desc_free (&t[1]);

    return 0;
}
Esempio n. 3
0
static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet)
{
	int nr = 1;
	int newfd;
	struct tree_desc desc[2];
	struct unpack_trees_options opts;
	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));

	memset(&opts, 0, sizeof(opts));
	opts.head_idx = 1;
	opts.src_index = &the_index;
	opts.dst_index = &the_index;
	opts.fn = oneway_merge;
	opts.merge = 1;
	if (!quiet)
		opts.verbose_update = 1;
	switch (reset_type) {
	case KEEP:
	case MERGE:
		opts.update = 1;
		break;
	case HARD:
		opts.update = 1;
		/* fallthrough */
	default:
		opts.reset = 1;
	}

	newfd = hold_locked_index(lock, 1);

	read_cache_unmerged();

	if (reset_type == KEEP) {
		unsigned char head_sha1[20];
		if (get_sha1("HEAD", head_sha1))
			return error(_("You do not have a valid HEAD."));
		if (!fill_tree_descriptor(desc, head_sha1))
			return error(_("Failed to find tree of HEAD."));
		nr++;
		opts.fn = twoway_merge;
	}

	if (!fill_tree_descriptor(desc + nr - 1, sha1))
		return error(_("Failed to find tree of %s."), sha1_to_hex(sha1));
	if (unpack_trees(nr, desc, &opts))
		return -1;
	if (write_cache(newfd, active_cache, active_nr) ||
	    commit_locked_index(lock))
		return error(_("Could not write new index file."));

	return 0;
}
Esempio n. 4
0
static int reset_index(const unsigned char *sha1, int reset_type, int quiet)
{
	int nr = 1;
	struct tree_desc desc[2];
	struct tree *tree;
	struct unpack_trees_options opts;

	memset(&opts, 0, sizeof(opts));
	opts.head_idx = 1;
	opts.src_index = &the_index;
	opts.dst_index = &the_index;
	opts.fn = oneway_merge;
	opts.merge = 1;
	if (!quiet)
		opts.verbose_update = 1;
	switch (reset_type) {
	case KEEP:
	case MERGE:
		opts.update = 1;
		break;
	case HARD:
		opts.update = 1;
		/* fallthrough */
	default:
		opts.reset = 1;
	}

	read_cache_unmerged();

	if (reset_type == KEEP) {
		unsigned char head_sha1[20];
		if (get_sha1("HEAD", head_sha1))
			return error(_("You do not have a valid HEAD."));
		if (!fill_tree_descriptor(desc, head_sha1))
			return error(_("Failed to find tree of HEAD."));
		nr++;
		opts.fn = twoway_merge;
	}

	if (!fill_tree_descriptor(desc + nr - 1, sha1))
		return error(_("Failed to find tree of %s."), sha1_to_hex(sha1));
	if (unpack_trees(nr, desc, &opts))
		return -1;

	if (reset_type == MIXED || reset_type == HARD) {
		tree = parse_tree_indirect(sha1);
		prime_cache_tree(&active_cache_tree, tree);
	}

	return 0;
}
Esempio n. 5
0
/*
 * Get the new blocks that need to be checked out if we ff to @remote.
 */
static int
get_new_blocks_ff (SeafRepo *repo, 
                   SeafCommit *head, 
                   SeafCommit *remote, 
                   BlockList **bl)
{
    SeafRepoManager *mgr = repo->manager;
    char index_path[SEAF_PATH_MAX];
    struct tree_desc trees[2];
    struct unpack_trees_options topts;
    struct index_state istate;
    int ret = 0;

    memset (&istate, 0, sizeof(istate));
    snprintf (index_path, SEAF_PATH_MAX, "%s/%s", mgr->index_dir, repo->id);
    if (read_index_from (&istate, index_path) < 0) {
        g_warning ("Failed to load index.\n");
        return -1;
    }

    fill_tree_descriptor (&trees[0], head->root_id);
    fill_tree_descriptor (&trees[1], remote->root_id);

    memset(&topts, 0, sizeof(topts));
    topts.base = repo->worktree;
    topts.head_idx = -1;
    topts.src_index = &istate;
    topts.update = 1;
    topts.merge = 1;
    topts.fn = twoway_merge;

    /* unpack_trees() doesn't update index or worktree. */
    if (unpack_trees (2, trees, &topts) < 0) {
        g_warning ("Failed to ff to commit %s.\n", remote->commit_id);
        ret = -1;
        goto out;
    }

    *bl = block_list_new ();
    collect_new_blocks_from_index (&topts.result, *bl);

out:
    tree_desc_free (&trees[0]);
    tree_desc_free (&trees[1]);
    discard_index (&istate);
    discard_index (&topts.result);

    return ret;
}
Esempio n. 6
0
static int seafile_merge_trees(struct merge_options *o,
                               struct unpack_trees_options *opts,
                               SeafDir *common,
                               SeafDir *head,
                               SeafDir *merge,
                               char **error)
{
    int rc;
    struct tree_desc t[3];

    memset(opts, 0, sizeof(*opts));
    if (o->call_depth)
        opts->index_only = 1;
    else
        opts->update = 1;
    opts->merge = 1;
    opts->head_idx = 2;
    opts->base = o->worktree;
    opts->fn = threeway_merge;
    opts->src_index = o->index;
    opts->dst_index = o->index;
    if (o->crypt)
        opts->crypt = o->crypt;

    fill_tree_descriptor(t+0, common->dir_id);
    fill_tree_descriptor(t+1, head->dir_id);
    fill_tree_descriptor(t+2, merge->dir_id);

    rc = unpack_trees(3, t, opts);

    if (rc == 0) {
        discard_index(o->index);
        *(o->index) = opts->result;

        if (o->collect_blocks_only)
            collect_new_blocks_from_index (o->index, o->bl);
    }

    tree_desc_free (t);
    tree_desc_free (t+1);
    tree_desc_free (t+2);

    return rc;
}
Esempio n. 7
0
int diff_index(struct index_state *istate, SeafDir *root, GList **results)
{
    struct tree_desc t;
    struct unpack_trees_options opts;

    memset(&opts, 0, sizeof(opts));
    opts.head_idx = 1;
    opts.index_only = 1;
    /* Unmerged entries are handled in diff worktree. */
    opts.skip_unmerged = 1;
    opts.merge = 1;
    opts.fn = oneway_diff;
    opts.unpack_data = results;
    opts.src_index = istate;
    opts.dst_index = NULL;

    fill_tree_descriptor(&t, root->dir_id);
    int ret = unpack_trees(1, &t, &opts);

    tree_desc_free (&t);
    return ret;
}
Esempio n. 8
0
static int traverse_trees_recursive(int n, unsigned long dirmask,
				    unsigned long df_conflicts,
				    struct name_entry *names,
				    struct traverse_info *info)
{
	int i, ret, bottom;
	struct tree_desc t[MAX_UNPACK_TREES];
	void *buf[MAX_UNPACK_TREES];
	struct traverse_info newinfo;
	struct name_entry *p;

	p = names;
	while (!p->mode)
		p++;

	newinfo = *info;
	newinfo.prev = info;
	newinfo.pathspec = info->pathspec;
	newinfo.name = *p;
	newinfo.pathlen += tree_entry_len(p) + 1;
	newinfo.df_conflicts |= df_conflicts;

	for (i = 0; i < n; i++, dirmask >>= 1) {
		const unsigned char *sha1 = NULL;
		if (dirmask & 1)
			sha1 = names[i].sha1;
		buf[i] = fill_tree_descriptor(t+i, sha1);
	}

	bottom = switch_cache_bottom(&newinfo);
	ret = traverse_trees(n, t, &newinfo);
	restore_cache_bottom(&newinfo, bottom);

	for (i = 0; i < n; i++)
		free(buf[i]);

	return ret;
}
Esempio n. 9
0
File: notes.c Progetto: Noffica/git
static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
		struct int_node *node, unsigned int n)
{
	struct object_id object_oid;
	size_t prefix_len;
	void *buf;
	struct tree_desc desc;
	struct name_entry entry;

	buf = fill_tree_descriptor(&desc, &subtree->val_oid);
	if (!buf)
		die("Could not read %s for notes-index",
		     oid_to_hex(&subtree->val_oid));

	prefix_len = subtree->key_oid.hash[KEY_INDEX];
	if (prefix_len >= GIT_SHA1_RAWSZ)
		BUG("prefix_len (%"PRIuMAX") is out of range", (uintmax_t)prefix_len);
	if (prefix_len * 2 < n)
		BUG("prefix_len (%"PRIuMAX") is too small", (uintmax_t)prefix_len);
	memcpy(object_oid.hash, subtree->key_oid.hash, prefix_len);
	while (tree_entry(&desc, &entry)) {
		unsigned char type;
		struct leaf_node *l;
		size_t path_len = strlen(entry.path);

		if (path_len == 2 * (GIT_SHA1_RAWSZ - prefix_len)) {
			/* This is potentially the remainder of the SHA-1 */

			if (!S_ISREG(entry.mode))
				/* notes must be blobs */
				goto handle_non_note;

			if (hex_to_bytes(object_oid.hash + prefix_len, entry.path,
					 GIT_SHA1_RAWSZ - prefix_len))
				goto handle_non_note; /* entry.path is not a SHA1 */

			type = PTR_TYPE_NOTE;
		} else if (path_len == 2) {
			/* This is potentially an internal node */
			size_t len = prefix_len;

			if (!S_ISDIR(entry.mode))
				/* internal nodes must be trees */
				goto handle_non_note;

			if (hex_to_bytes(object_oid.hash + len++, entry.path, 1))
				goto handle_non_note; /* entry.path is not a SHA1 */

			/*
			 * Pad the rest of the SHA-1 with zeros,
			 * except for the last byte, where we write
			 * the length:
			 */
			memset(object_oid.hash + len, 0, GIT_SHA1_RAWSZ - len - 1);
			object_oid.hash[KEY_INDEX] = (unsigned char)len;

			type = PTR_TYPE_SUBTREE;
		} else {
			/* This can't be part of a note */
			goto handle_non_note;
		}

		l = xcalloc(1, sizeof(*l));
		oidcpy(&l->key_oid, &object_oid);
		oidcpy(&l->val_oid, &entry.oid);
		if (note_tree_insert(t, node, n, l, type,
				     combine_notes_concatenate))
			die("Failed to load %s %s into notes tree "
			    "from %s",
			    type == PTR_TYPE_NOTE ? "note" : "subtree",
			    oid_to_hex(&l->key_oid), t->ref);

		continue;

handle_non_note:
		/*
		 * Determine full path for this non-note entry. The
		 * filename is already found in entry.path, but the
		 * directory part of the path must be deduced from the
		 * subtree containing this entry based on our
		 * knowledge that the overall notes tree follows a
		 * strict byte-based progressive fanout structure
		 * (i.e. using 2/38, 2/2/36, etc. fanouts).
		 */
		{
			struct strbuf non_note_path = STRBUF_INIT;
			const char *q = oid_to_hex(&subtree->key_oid);
			size_t i;
			for (i = 0; i < prefix_len; i++) {
				strbuf_addch(&non_note_path, *q++);
				strbuf_addch(&non_note_path, *q++);
				strbuf_addch(&non_note_path, '/');
			}
			strbuf_addstr(&non_note_path, entry.path);
			add_non_note(t, strbuf_detach(&non_note_path, NULL),
				     entry.mode, entry.oid.hash);
		}
	}
	free(buf);
}
Esempio n. 10
0
static int
fast_forward_checkout (SeafRepo *repo, SeafCommit *head, CloneTask *task)
{
    SeafRepoManager *mgr = repo->manager;
    char index_path[SEAF_PATH_MAX];
    struct tree_desc trees[2];
    struct unpack_trees_options topts;
    struct index_state istate;
    int ret = 0;

    if (strcmp (head->root_id, task->root_id) == 0)
        return 0;

    memset (&istate, 0, sizeof(istate));
    snprintf (index_path, SEAF_PATH_MAX, "%s/%s", mgr->index_dir, repo->id);
    if (read_index_from (&istate, index_path) < 0) {
        seaf_warning ("Failed to load index.\n");
        return -1;
    }
    repo->index_corrupted = FALSE;

    fill_tree_descriptor (&trees[0], task->root_id);
    fill_tree_descriptor (&trees[1], head->root_id);

    memset(&topts, 0, sizeof(topts));
    topts.base = task->worktree;
    topts.head_idx = -1;
    topts.src_index = &istate;
    topts.update = 1;
    topts.merge = 1;
    topts.fn = twoway_merge;
    if (repo->encrypted) {
        topts.crypt = seafile_crypt_new (repo->enc_version, 
                                         repo->enc_key, 
                                         repo->enc_iv);
    }

    if (unpack_trees (2, trees, &topts) < 0) {
        seaf_warning ("Failed to merge commit %s with work tree.\n", head->commit_id);
        ret = -1;
        goto out;
    }

    if (update_worktree (&topts, FALSE,
                         head->commit_id,
                         head->creator_name,
                         NULL) < 0) {
        seaf_warning ("Failed to update worktree.\n");
        ret = -1;
        goto out;
    }

    discard_index (&istate);
    istate = topts.result;

    if (update_index (&istate, index_path) < 0) {
        seaf_warning ("Failed to update index.\n");
    }

out:
    tree_desc_free (&trees[0]);
    tree_desc_free (&trees[1]);

    g_free (topts.crypt);

    discard_index (&istate);

    return ret;
}
Esempio n. 11
0
File: notes.c Progetto: sanj/git
static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,
		struct int_node *node, unsigned int n)
{
	unsigned char object_sha1[20];
	unsigned int prefix_len;
	void *buf;
	struct tree_desc desc;
	struct name_entry entry;
	int len, path_len;
	unsigned char type;
	struct leaf_node *l;

	buf = fill_tree_descriptor(&desc, subtree->val_sha1);
	if (!buf)
		die("Could not read %s for notes-index",
		     sha1_to_hex(subtree->val_sha1));

	prefix_len = subtree->key_sha1[19];
	assert(prefix_len * 2 >= n);
	memcpy(object_sha1, subtree->key_sha1, prefix_len);
	while (tree_entry(&desc, &entry)) {
		path_len = strlen(entry.path);
		len = get_sha1_hex_segment(entry.path, path_len,
				object_sha1 + prefix_len, 20 - prefix_len);
		if (len < 0)
			goto handle_non_note; /* entry.path is not a SHA1 */
		len += prefix_len;

		/*
		 * If object SHA1 is complete (len == 20), assume note object
		 * If object SHA1 is incomplete (len < 20), and current
		 * component consists of 2 hex chars, assume note subtree
		 */
		if (len <= 20) {
			type = PTR_TYPE_NOTE;
			l = (struct leaf_node *)
				xcalloc(sizeof(struct leaf_node), 1);
			hashcpy(l->key_sha1, object_sha1);
			hashcpy(l->val_sha1, entry.sha1);
			if (len < 20) {
				if (!S_ISDIR(entry.mode) || path_len != 2)
					goto handle_non_note; /* not subtree */
				l->key_sha1[19] = (unsigned char) len;
				type = PTR_TYPE_SUBTREE;
			}
			note_tree_insert(t, node, n, l, type,
					 combine_notes_concatenate);
		}
		continue;

handle_non_note:
		/*
		 * Determine full path for this non-note entry:
		 * The filename is already found in entry.path, but the
		 * directory part of the path must be deduced from the subtree
		 * containing this entry. We assume here that the overall notes
		 * tree follows a strict byte-based progressive fanout
		 * structure (i.e. using 2/38, 2/2/36, etc. fanouts, and not
		 * e.g. 4/36 fanout). This means that if a non-note is found at
		 * path "dead/beef", the following code will register it as
		 * being found on "de/ad/beef".
		 * On the other hand, if you use such non-obvious non-note
		 * paths in the middle of a notes tree, you deserve what's
		 * coming to you ;). Note that for non-notes that are not
		 * SHA1-like at the top level, there will be no problems.
		 *
		 * To conclude, it is strongly advised to make sure non-notes
		 * have at least one non-hex character in the top-level path
		 * component.
		 */
		{
			char non_note_path[PATH_MAX];
			char *p = non_note_path;
			const char *q = sha1_to_hex(subtree->key_sha1);
			int i;
			for (i = 0; i < prefix_len; i++) {
				*p++ = *q++;
				*p++ = *q++;
				*p++ = '/';
			}
			strcpy(p, entry.path);
			add_non_note(t, non_note_path, entry.mode, entry.sha1);
		}
	}
	free(buf);
}
Esempio n. 12
0
static struct combine_diff_path *ll_diff_tree_paths(
	struct combine_diff_path *p, const unsigned char *sha1,
	const unsigned char **parents_sha1, int nparent,
	struct strbuf *base, struct diff_options *opt)
{
	struct tree_desc t, *tp;
	void *ttree, **tptree;
	int i;

	FAST_ARRAY_ALLOC(tp, nparent);
	FAST_ARRAY_ALLOC(tptree, nparent);

	/*
	 * load parents first, as they are probably already cached.
	 *
	 * ( log_tree_diff() parses commit->parent before calling here via
	 *   diff_tree_sha1(parent, commit) )
	 */
	for (i = 0; i < nparent; ++i)
		tptree[i] = fill_tree_descriptor(&tp[i], parents_sha1[i]);
	ttree = fill_tree_descriptor(&t, sha1);

	/* Enable recursion indefinitely */
	opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);

	for (;;) {
		int imin, cmp;

		if (diff_can_quit_early(opt))
			break;

		if (opt->pathspec.nr) {
			skip_uninteresting(&t, base, opt);
			for (i = 0; i < nparent; i++)
				skip_uninteresting(&tp[i], base, opt);
		}

		/* comparing is finished when all trees are done */
		if (!t.size) {
			int done = 1;
			for (i = 0; i < nparent; ++i)
				if (tp[i].size) {
					done = 0;
					break;
				}
			if (done)
				break;
		}

		/*
		 * lookup imin = argmin(p1...pn),
		 * mark entries whether they =p[imin] along the way
		 */
		imin = 0;
		tp[0].entry.mode &= ~S_IFXMIN_NEQ;

		for (i = 1; i < nparent; ++i) {
			cmp = tree_entry_pathcmp(&tp[i], &tp[imin]);
			if (cmp < 0) {
				imin = i;
				tp[i].entry.mode &= ~S_IFXMIN_NEQ;
			}
			else if (cmp == 0) {
				tp[i].entry.mode &= ~S_IFXMIN_NEQ;
			}
			else {
				tp[i].entry.mode |= S_IFXMIN_NEQ;
			}
		}

		/* fixup markings for entries before imin */
		for (i = 0; i < imin; ++i)
			tp[i].entry.mode |= S_IFXMIN_NEQ;	/* pi > p[imin] */



		/* compare t vs p[imin] */
		cmp = tree_entry_pathcmp(&t, &tp[imin]);

		/* t = p[imin] */
		if (cmp == 0) {
			/* are either pi > p[imin] or diff(t,pi) != ø ? */
			if (!DIFF_OPT_TST(opt, FIND_COPIES_HARDER)) {
				for (i = 0; i < nparent; ++i) {
					/* p[i] > p[imin] */
					if (tp[i].entry.mode & S_IFXMIN_NEQ)
						continue;

					/* diff(t,pi) != ø */
					if (oidcmp(t.entry.oid, tp[i].entry.oid) ||
					    (t.entry.mode != tp[i].entry.mode))
						continue;

					goto skip_emit_t_tp;
				}
			}

			/* D += {δ(t,pi) if pi=p[imin];  "+a" if pi > p[imin]} */
			p = emit_path(p, base, opt, nparent,
					&t, tp, imin);

		skip_emit_t_tp:
			/* t↓,  ∀ pi=p[imin]  pi↓ */
			update_tree_entry(&t);
			update_tp_entries(tp, nparent);
		}

		/* t < p[imin] */
		else if (cmp < 0) {
			/* D += "+t" */
			p = emit_path(p, base, opt, nparent,
					&t, /*tp=*/NULL, -1);

			/* t↓ */
			update_tree_entry(&t);
		}

		/* t > p[imin] */
		else {
			/* ∀i pi=p[imin] -> D += "-p[imin]" */
			if (!DIFF_OPT_TST(opt, FIND_COPIES_HARDER)) {
				for (i = 0; i < nparent; ++i)
					if (tp[i].entry.mode & S_IFXMIN_NEQ)
						goto skip_emit_tp;
			}

			p = emit_path(p, base, opt, nparent,
					/*t=*/NULL, tp, imin);

		skip_emit_tp:
			/* ∀ pi=p[imin]  pi↓ */
			update_tp_entries(tp, nparent);
		}
	}

	free(ttree);
	for (i = nparent-1; i >= 0; i--)
		free(tptree[i]);
	FAST_ARRAY_FREE(tptree, nparent);
	FAST_ARRAY_FREE(tp, nparent);

	return p;
}
Esempio n. 13
0
File: reset.c Progetto: guban/git
static int reset_index(const struct object_id *oid, int reset_type, int quiet)
{
	int i, nr = 0;
	struct tree_desc desc[2];
	struct tree *tree;
	struct unpack_trees_options opts;
	int ret = -1;

	memset(&opts, 0, sizeof(opts));
	opts.head_idx = 1;
	opts.src_index = &the_index;
	opts.dst_index = &the_index;
	opts.fn = oneway_merge;
	opts.merge = 1;
	if (!quiet)
		opts.verbose_update = 1;
	switch (reset_type) {
	case KEEP:
	case MERGE:
		opts.update = 1;
		break;
	case HARD:
		opts.update = 1;
		/* fallthrough */
	default:
		opts.reset = 1;
	}

	read_cache_unmerged();

	if (reset_type == KEEP) {
		struct object_id head_oid;
		if (get_oid("HEAD", &head_oid))
			return error(_("You do not have a valid HEAD."));
		if (!fill_tree_descriptor(desc + nr, &head_oid))
			return error(_("Failed to find tree of HEAD."));
		nr++;
		opts.fn = twoway_merge;
	}

	if (!fill_tree_descriptor(desc + nr, oid)) {
		error(_("Failed to find tree of %s."), oid_to_hex(oid));
		goto out;
	}
	nr++;

	if (unpack_trees(nr, desc, &opts))
		goto out;

	if (reset_type == MIXED || reset_type == HARD) {
		tree = parse_tree_indirect(oid);
		prime_cache_tree(&the_index, tree);
	}

	ret = 0;

out:
	for (i = 0; i < nr; i++)
		free((void *)desc[i].buffer);
	return ret;
}