コード例 #1
0
ファイル: vc-utils.c プロジェクト: 2bj/seafile
int
update_worktree (struct unpack_trees_options *o,
                 gboolean recover_merge,
                 const char *conflict_head_id,
                 const char *default_conflict_suffix,
                 int *finished_entries)
{
    struct index_state *result = &o->result;
    int i;
    struct cache_entry *ce;
    char *conflict_suffix = NULL;
    int errs = 0;

    for (i = 0; i < result->cache_nr; ++i) {
        ce = result->cache[i];
        if (ce->ce_flags & CE_WT_REMOVE)
            errs |= unlink_entry (ce, o);
    }

    for (i = 0; i < result->cache_nr; ++i) {
        ce = result->cache[i];
        if (ce->ce_flags & CE_UPDATE) {
            if (conflict_head_id) {
                conflict_suffix = get_last_changer_of_file (conflict_head_id,
                                                            ce->name);
                if (!conflict_suffix)
                    conflict_suffix = g_strdup(default_conflict_suffix);
            }
            errs |= checkout_entry (ce, o, recover_merge, conflict_suffix);
            g_free (conflict_suffix);
        }
        if (finished_entries)
            *finished_entries = *finished_entries + 1;
    }

    return errs != 0;
}
コード例 #2
0
ファイル: checkout.c プロジェクト: Noffica/git
static int checkout_paths(const struct checkout_opts *opts,
			  const char *revision)
{
	int pos;
	struct checkout state = CHECKOUT_INIT;
	static char *ps_matched;
	struct object_id rev;
	struct commit *head;
	int errs = 0;
	struct lock_file lock_file = LOCK_INIT;
	int nr_checkouts = 0, nr_unmerged = 0;

	trace2_cmd_mode(opts->patch_mode ? "patch" : "path");

	if (opts->track != BRANCH_TRACK_UNSPECIFIED)
		die(_("'%s' cannot be used with updating paths"), "--track");

	if (opts->new_branch_log)
		die(_("'%s' cannot be used with updating paths"), "-l");

	if (opts->force && opts->patch_mode)
		die(_("'%s' cannot be used with updating paths"), "-f");

	if (opts->force_detach)
		die(_("'%s' cannot be used with updating paths"), "--detach");

	if (opts->merge && opts->patch_mode)
		die(_("'%s' cannot be used with %s"), "--merge", "--patch");

	if (opts->force && opts->merge)
		die(_("'%s' cannot be used with %s"), "-f", "-m");

	if (opts->new_branch)
		die(_("Cannot update paths and switch to branch '%s' at the same time."),
		    opts->new_branch);

	if (opts->patch_mode)
		return run_add_interactive(revision, "--patch=checkout",
					   &opts->pathspec);

	repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
	if (read_cache_preload(&opts->pathspec) < 0)
		return error(_("index file corrupt"));

	if (opts->source_tree)
		read_tree_some(opts->source_tree, &opts->pathspec);

	ps_matched = xcalloc(opts->pathspec.nr, 1);

	/*
	 * Make sure all pathspecs participated in locating the paths
	 * to be checked out.
	 */
	for (pos = 0; pos < active_nr; pos++)
		if (opts->overlay_mode)
			mark_ce_for_checkout_overlay(active_cache[pos],
						     ps_matched,
						     opts);
		else
			mark_ce_for_checkout_no_overlay(active_cache[pos],
							ps_matched,
							opts);

	if (report_path_error(ps_matched, &opts->pathspec, opts->prefix)) {
		free(ps_matched);
		return 1;
	}
	free(ps_matched);

	/* "checkout -m path" to recreate conflicted state */
	if (opts->merge)
		unmerge_marked_index(&the_index);

	/* Any unmerged paths? */
	for (pos = 0; pos < active_nr; pos++) {
		const struct cache_entry *ce = active_cache[pos];
		if (ce->ce_flags & CE_MATCHED) {
			if (!ce_stage(ce))
				continue;
			if (opts->force) {
				warning(_("path '%s' is unmerged"), ce->name);
			} else if (opts->writeout_stage) {
				errs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);
			} else if (opts->merge) {
				errs |= check_stages((1<<2) | (1<<3), ce, pos);
			} else {
				errs = 1;
				error(_("path '%s' is unmerged"), ce->name);
			}
			pos = skip_same_name(ce, pos) - 1;
		}
	}
	if (errs)
		return 1;

	/* Now we are committed to check them out */
	state.force = 1;
	state.refresh_cache = 1;
	state.istate = &the_index;

	enable_delayed_checkout(&state);
	for (pos = 0; pos < active_nr; pos++) {
		struct cache_entry *ce = active_cache[pos];
		if (ce->ce_flags & CE_MATCHED) {
			if (!ce_stage(ce)) {
				errs |= checkout_entry(ce, &state,
						       NULL, &nr_checkouts);
				continue;
			}
			if (opts->writeout_stage)
				errs |= checkout_stage(opts->writeout_stage,
						       ce, pos,
						       &state,
						       &nr_checkouts, opts->overlay_mode);
			else if (opts->merge)
				errs |= checkout_merged(pos, &state,
							&nr_unmerged);
			pos = skip_same_name(ce, pos) - 1;
		}
	}
	remove_marked_cache_entries(&the_index, 1);
	remove_scheduled_dirs();
	errs |= finish_delayed_checkout(&state, &nr_checkouts);

	if (opts->count_checkout_paths) {
		if (nr_unmerged)
			fprintf_ln(stderr, Q_("Recreated %d merge conflict",
					      "Recreated %d merge conflicts",
					      nr_unmerged),
				   nr_unmerged);
		if (opts->source_tree)
			fprintf_ln(stderr, Q_("Updated %d path from %s",
					      "Updated %d paths from %s",
					      nr_checkouts),
				   nr_checkouts,
				   find_unique_abbrev(&opts->source_tree->object.oid,
						      DEFAULT_ABBREV));
		else if (!nr_unmerged || nr_checkouts)
			fprintf_ln(stderr, Q_("Updated %d path from the index",
					      "Updated %d paths from the index",
					      nr_checkouts),
				   nr_checkouts);
	}

	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
		die(_("unable to write new index file"));

	read_ref_full("HEAD", 0, &rev, NULL);
	head = lookup_commit_reference_gently(the_repository, &rev, 1);

	errs |= post_checkout_hook(head, head, 0);
	return errs;
}
コード例 #3
0
ファイル: difftool.c プロジェクト: dscho/git
static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                        int argc, const char **argv)
{
    char tmpdir[PATH_MAX];
    struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT;
    struct strbuf rpath = STRBUF_INIT, buf = STRBUF_INIT;
    struct strbuf ldir = STRBUF_INIT, rdir = STRBUF_INIT;
    struct strbuf wtdir = STRBUF_INIT;
    size_t ldir_len, rdir_len, wtdir_len;
    struct cache_entry *ce = xcalloc(1, sizeof(ce) + PATH_MAX + 1);
    const char *workdir, *tmp;
    int ret = 0, i;
    FILE *fp;
    struct hashmap working_tree_dups, submodules, symlinks2;
    struct hashmap_iter iter;
    struct pair_entry *entry;
    enum object_type type;
    unsigned long size;
    struct index_state wtindex;
    struct checkout lstate, rstate;
    int rc, flags = RUN_GIT_CMD, err = 0;
    struct child_process child = CHILD_PROCESS_INIT;
    const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL };
    struct hashmap wt_modified, tmp_modified;
    int indices_loaded = 0;

    workdir = get_git_work_tree();

    /* Setup temp directories */
    tmp = getenv("TMPDIR");
    xsnprintf(tmpdir, sizeof(tmpdir), "%s/git-difftool.XXXXXX", tmp ? tmp : "/tmp");
    if (!mkdtemp(tmpdir))
        return error("could not create '%s'", tmpdir);
    strbuf_addf(&ldir, "%s/left/", tmpdir);
    strbuf_addf(&rdir, "%s/right/", tmpdir);
    strbuf_addstr(&wtdir, workdir);
    if (!wtdir.len || !is_dir_sep(wtdir.buf[wtdir.len - 1]))
        strbuf_addch(&wtdir, '/');
    mkdir(ldir.buf, 0700);
    mkdir(rdir.buf, 0700);

    memset(&wtindex, 0, sizeof(wtindex));

    memset(&lstate, 0, sizeof(lstate));
    lstate.base_dir = ldir.buf;
    lstate.base_dir_len = ldir.len;
    lstate.force = 1;
    memset(&rstate, 0, sizeof(rstate));
    rstate.base_dir = rdir.buf;
    rstate.base_dir_len = rdir.len;
    rstate.force = 1;

    ldir_len = ldir.len;
    rdir_len = rdir.len;
    wtdir_len = wtdir.len;

    hashmap_init(&working_tree_dups,
                 (hashmap_cmp_fn)working_tree_entry_cmp, 0);
    hashmap_init(&submodules, (hashmap_cmp_fn)pair_cmp, 0);
    hashmap_init(&symlinks2, (hashmap_cmp_fn)pair_cmp, 0);

    child.no_stdin = 1;
    child.git_cmd = 1;
    child.use_shell = 0;
    child.clean_on_exit = 1;
    child.dir = prefix;
    child.out = -1;
    argv_array_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
                     NULL);
    for (i = 0; i < argc; i++)
        argv_array_push(&child.args, argv[i]);
    if (start_command(&child))
        die("could not obtain raw diff");
    fp = xfdopen(child.out, "r");

    /* Build index info for left and right sides of the diff */
    i = 0;
    while (!strbuf_getline_nul(&info, fp)) {
        int lmode, rmode;
        struct object_id loid, roid;
        char status;
        const char *src_path, *dst_path;
        size_t src_path_len, dst_path_len;

        if (starts_with(info.buf, "::"))
            die(N_("combined diff formats('-c' and '--cc') are "
                   "not supported in\n"
                   "directory diff mode('-d' and '--dir-diff')."));

        if (parse_index_info(info.buf, &lmode, &rmode, &loid, &roid,
                             &status))
            break;
        if (strbuf_getline_nul(&lpath, fp))
            break;
        src_path = lpath.buf;
        src_path_len = lpath.len;

        i++;
        if (status != 'C' && status != 'R') {
            dst_path = src_path;
            dst_path_len = src_path_len;
        } else {
            if (strbuf_getline_nul(&rpath, fp))
                break;
            dst_path = rpath.buf;
            dst_path_len = rpath.len;
        }

        if (S_ISGITLINK(lmode) || S_ISGITLINK(rmode)) {
            strbuf_reset(&buf);
            strbuf_addf(&buf, "Subproject commit %s",
                        oid_to_hex(&loid));
            add_left_or_right(&submodules, src_path, buf.buf, 0);
            strbuf_reset(&buf);
            strbuf_addf(&buf, "Subproject commit %s",
                        oid_to_hex(&roid));
            if (!oidcmp(&loid, &roid))
                strbuf_addstr(&buf, "-dirty");
            add_left_or_right(&submodules, dst_path, buf.buf, 1);
            continue;
        }

        if (S_ISLNK(lmode)) {
            char *content = read_sha1_file(loid.hash, &type, &size);
            add_left_or_right(&symlinks2, src_path, content, 0);
            free(content);
        }

        if (S_ISLNK(rmode)) {
            char *content = read_sha1_file(roid.hash, &type, &size);
            add_left_or_right(&symlinks2, dst_path, content, 1);
            free(content);
        }

        if (lmode && status != 'C') {
            ce->ce_mode = lmode;
            oidcpy(&ce->oid, &loid);
            strcpy(ce->name, src_path);
            ce->ce_namelen = src_path_len;
            if (checkout_entry(ce, &lstate, NULL))
                return error("could not write '%s'", src_path);
        }

        if (rmode) {
            struct working_tree_entry *entry;

            /* Avoid duplicate working_tree entries */
            FLEX_ALLOC_STR(entry, path, dst_path);
            hashmap_entry_init(entry, strhash(dst_path));
            if (hashmap_get(&working_tree_dups, entry, NULL)) {
                free(entry);
                continue;
            }
            hashmap_add(&working_tree_dups, entry);

            if (!use_wt_file(workdir, dst_path, &roid)) {
                ce->ce_mode = rmode;
                oidcpy(&ce->oid, &roid);
                strcpy(ce->name, dst_path);
                ce->ce_namelen = dst_path_len;
                if (checkout_entry(ce, &rstate, NULL))
                    return error("could not write '%s'",
                                 dst_path);
            } else if (!is_null_oid(&roid)) {
                /*
                 * Changes in the working tree need special
                 * treatment since they are not part of the
                 * index.
                 */
                struct cache_entry *ce2 =
                    make_cache_entry(rmode, roid.hash,
                                     dst_path, 0, 0);
                ce_mode_from_stat(ce2, rmode);

                add_index_entry(&wtindex, ce2,
                                ADD_CACHE_JUST_APPEND);

                add_path(&wtdir, wtdir_len, dst_path);
                add_path(&rdir, rdir_len, dst_path);
                if (ensure_leading_directories(rdir.buf))
                    return error("could not create "
                                 "directory for '%s'",
                                 dst_path);
                if (symlinks) {
                    if (symlink(wtdir.buf, rdir.buf)) {
                        ret = error_errno("could not symlink '%s' to '%s'", wtdir.buf, rdir.buf);
                        goto finish;
                    }
                } else {
                    struct stat st;
                    if (stat(wtdir.buf, &st))
                        st.st_mode = 0644;
                    if (copy_file(rdir.buf, wtdir.buf,
                                  st.st_mode)) {
                        ret = error("could not copy '%s' to '%s'", wtdir.buf, rdir.buf);
                        goto finish;
                    }
                }
            }
        }
    }

    if (finish_command(&child)) {
        ret = error("error occurred running diff --raw");
        goto finish;
    }

    if (!i)
        return 0;

    /*
     * Changes to submodules require special treatment.This loop writes a
     * temporary file to both the left and right directories to show the
     * change in the recorded SHA1 for the submodule.
     */
    hashmap_iter_init(&submodules, &iter);
    while ((entry = hashmap_iter_next(&iter))) {
        if (*entry->left) {
            add_path(&ldir, ldir_len, entry->path);
            ensure_leading_directories(ldir.buf);
            write_file(ldir.buf, "%s", entry->left);
        }
        if (*entry->right) {
            add_path(&rdir, rdir_len, entry->path);
            ensure_leading_directories(rdir.buf);
            write_file(rdir.buf, "%s", entry->right);
        }
    }

    /*
     * Symbolic links require special treatment.The standard "git diff"
     * shows only the link itself, not the contents of the link target.
     * This loop replicates that behavior.
     */
    hashmap_iter_init(&symlinks2, &iter);
    while ((entry = hashmap_iter_next(&iter))) {
        if (*entry->left) {
            add_path(&ldir, ldir_len, entry->path);
            ensure_leading_directories(ldir.buf);
            write_file(ldir.buf, "%s", entry->left);
        }
        if (*entry->right) {
            add_path(&rdir, rdir_len, entry->path);
            ensure_leading_directories(rdir.buf);
            write_file(rdir.buf, "%s", entry->right);
        }
    }

    strbuf_release(&buf);

    strbuf_setlen(&ldir, ldir_len);
    helper_argv[1] = ldir.buf;
    strbuf_setlen(&rdir, rdir_len);
    helper_argv[2] = rdir.buf;

    if (extcmd) {
        helper_argv[0] = extcmd;
        flags = 0;
    } else
        setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1);
    rc = run_command_v_opt(helper_argv, flags);

    /*
     * If the diff includes working copy files and those
     * files were modified during the diff, then the changes
     * should be copied back to the working tree.
     * Do not copy back files when symlinks are used and the
     * external tool did not replace the original link with a file.
     *
     * These hashes are loaded lazily since they aren't needed
     * in the common case of --symlinks and the difftool updating
     * files through the symlink.
     */
    hashmap_init(&wt_modified, (hashmap_cmp_fn)path_entry_cmp,
                 wtindex.cache_nr);
    hashmap_init(&tmp_modified, (hashmap_cmp_fn)path_entry_cmp,
                 wtindex.cache_nr);

    for (i = 0; i < wtindex.cache_nr; i++) {
        struct hashmap_entry dummy;
        const char *name = wtindex.cache[i]->name;
        struct stat st;

        add_path(&rdir, rdir_len, name);
        if (lstat(rdir.buf, &st))
            continue;

        if ((symlinks && S_ISLNK(st.st_mode)) || !S_ISREG(st.st_mode))
            continue;

        if (!indices_loaded) {
            static struct lock_file lock;
            strbuf_reset(&buf);
            strbuf_addf(&buf, "%s/wtindex", tmpdir);
            if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 ||
                    write_locked_index(&wtindex, &lock, COMMIT_LOCK)) {
                ret = error("could not write %s", buf.buf);
                rollback_lock_file(&lock);
                goto finish;
            }
            changed_files(&wt_modified, buf.buf, workdir);
            strbuf_setlen(&rdir, rdir_len);
            changed_files(&tmp_modified, buf.buf, rdir.buf);
            add_path(&rdir, rdir_len, name);
            indices_loaded = 1;
        }

        hashmap_entry_init(&dummy, strhash(name));
        if (hashmap_get(&tmp_modified, &dummy, name)) {
            add_path(&wtdir, wtdir_len, name);
            if (hashmap_get(&wt_modified, &dummy, name)) {
                warning(_("both files modified: '%s' and '%s'."),
                        wtdir.buf, rdir.buf);
                warning(_("working tree file has been left."));
                warning("");
                err = 1;
            } else if (unlink(wtdir.buf) ||
                       copy_file(wtdir.buf, rdir.buf, st.st_mode))
                warning_errno(_("could not copy '%s' to '%s'"),
                              rdir.buf, wtdir.buf);
        }
    }

    if (err) {
        warning(_("temporary files exist in '%s'."), tmpdir);
        warning(_("you may want to cleanup or recover these."));
        exit(1);
    } else
        exit_cleanup(tmpdir, rc);

finish:
    free(ce);
    strbuf_release(&ldir);
    strbuf_release(&rdir);
    strbuf_release(&wtdir);
    strbuf_release(&buf);

    return ret;
}
コード例 #4
0
ファイル: checkout.c プロジェクト: Noffica/git
static int checkout_merged(int pos, const struct checkout *state, int *nr_checkouts)
{
	struct cache_entry *ce = active_cache[pos];
	const char *path = ce->name;
	mmfile_t ancestor, ours, theirs;
	int status;
	struct object_id oid;
	mmbuffer_t result_buf;
	struct object_id threeway[3];
	unsigned mode = 0;

	memset(threeway, 0, sizeof(threeway));
	while (pos < active_nr) {
		int stage;
		stage = ce_stage(ce);
		if (!stage || strcmp(path, ce->name))
			break;
		oidcpy(&threeway[stage - 1], &ce->oid);
		if (stage == 2)
			mode = create_ce_mode(ce->ce_mode);
		pos++;
		ce = active_cache[pos];
	}
	if (is_null_oid(&threeway[1]) || is_null_oid(&threeway[2]))
		return error(_("path '%s' does not have necessary versions"), path);

	read_mmblob(&ancestor, &threeway[0]);
	read_mmblob(&ours, &threeway[1]);
	read_mmblob(&theirs, &threeway[2]);

	/*
	 * NEEDSWORK: re-create conflicts from merges with
	 * merge.renormalize set, too
	 */
	status = ll_merge(&result_buf, path, &ancestor, "base",
			  &ours, "ours", &theirs, "theirs",
			  state->istate, NULL);
	free(ancestor.ptr);
	free(ours.ptr);
	free(theirs.ptr);
	if (status < 0 || !result_buf.ptr) {
		free(result_buf.ptr);
		return error(_("path '%s': cannot merge"), path);
	}

	/*
	 * NEEDSWORK:
	 * There is absolutely no reason to write this as a blob object
	 * and create a phony cache entry.  This hack is primarily to get
	 * to the write_entry() machinery that massages the contents to
	 * work-tree format and writes out which only allows it for a
	 * cache entry.  The code in write_entry() needs to be refactored
	 * to allow us to feed a <buffer, size, mode> instead of a cache
	 * entry.  Such a refactoring would help merge_recursive as well
	 * (it also writes the merge result to the object database even
	 * when it may contain conflicts).
	 */
	if (write_object_file(result_buf.ptr, result_buf.size, blob_type, &oid))
		die(_("Unable to add merge result for '%s'"), path);
	free(result_buf.ptr);
	ce = make_transient_cache_entry(mode, &oid, path, 2);
	if (!ce)
		die(_("make_cache_entry failed for path '%s'"), path);
	status = checkout_entry(ce, state, NULL, nr_checkouts);
	discard_cache_entry(ce);
	return status;
}
コード例 #5
0
ファイル: checkout.c プロジェクト: ovmine/git
static int checkout_paths(const struct checkout_opts *opts,
			  const char *revision)
{
	int pos;
	struct checkout state = CHECKOUT_INIT;
	static char *ps_matched;
	struct object_id rev;
	struct commit *head;
	int errs = 0;
	struct lock_file lock_file = LOCK_INIT;

	if (opts->track != BRANCH_TRACK_UNSPECIFIED)
		die(_("'%s' cannot be used with updating paths"), "--track");

	if (opts->new_branch_log)
		die(_("'%s' cannot be used with updating paths"), "-l");

	if (opts->force && opts->patch_mode)
		die(_("'%s' cannot be used with updating paths"), "-f");

	if (opts->force_detach)
		die(_("'%s' cannot be used with updating paths"), "--detach");

	if (opts->merge && opts->patch_mode)
		die(_("'%s' cannot be used with %s"), "--merge", "--patch");

	if (opts->force && opts->merge)
		die(_("'%s' cannot be used with %s"), "-f", "-m");

	if (opts->new_branch)
		die(_("Cannot update paths and switch to branch '%s' at the same time."),
		    opts->new_branch);

	if (opts->patch_mode)
		return run_add_interactive(revision, "--patch=checkout",
					   &opts->pathspec);

	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
	if (read_cache_preload(&opts->pathspec) < 0)
		return error(_("index file corrupt"));

	if (opts->source_tree)
		read_tree_some(opts->source_tree, &opts->pathspec);

	ps_matched = xcalloc(opts->pathspec.nr, 1);

	/*
	 * Make sure all pathspecs participated in locating the paths
	 * to be checked out.
	 */
	for (pos = 0; pos < active_nr; pos++) {
		struct cache_entry *ce = active_cache[pos];
		ce->ce_flags &= ~CE_MATCHED;
		if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
			continue;
		if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
			/*
			 * "git checkout tree-ish -- path", but this entry
			 * is in the original index; it will not be checked
			 * out to the working tree and it does not matter
			 * if pathspec matched this entry.  We will not do
			 * anything to this entry at all.
			 */
			continue;
		/*
		 * Either this entry came from the tree-ish we are
		 * checking the paths out of, or we are checking out
		 * of the index.
		 *
		 * If it comes from the tree-ish, we already know it
		 * matches the pathspec and could just stamp
		 * CE_MATCHED to it from update_some(). But we still
		 * need ps_matched and read_tree_recursive (and
		 * eventually tree_entry_interesting) cannot fill
		 * ps_matched yet. Once it can, we can avoid calling
		 * match_pathspec() for _all_ entries when
		 * opts->source_tree != NULL.
		 */
		if (ce_path_match(ce, &opts->pathspec, ps_matched))
			ce->ce_flags |= CE_MATCHED;
	}

	if (report_path_error(ps_matched, &opts->pathspec, opts->prefix)) {
		free(ps_matched);
		return 1;
	}
	free(ps_matched);

	/* "checkout -m path" to recreate conflicted state */
	if (opts->merge)
		unmerge_marked_index(&the_index);

	/* Any unmerged paths? */
	for (pos = 0; pos < active_nr; pos++) {
		const struct cache_entry *ce = active_cache[pos];
		if (ce->ce_flags & CE_MATCHED) {
			if (!ce_stage(ce))
				continue;
			if (opts->force) {
				warning(_("path '%s' is unmerged"), ce->name);
			} else if (opts->writeout_stage) {
				errs |= check_stage(opts->writeout_stage, ce, pos);
			} else if (opts->merge) {
				errs |= check_stages((1<<2) | (1<<3), ce, pos);
			} else {
				errs = 1;
				error(_("path '%s' is unmerged"), ce->name);
			}
			pos = skip_same_name(ce, pos) - 1;
		}
	}
	if (errs)
		return 1;

	/* Now we are committed to check them out */
	state.force = 1;
	state.refresh_cache = 1;
	state.istate = &the_index;

	enable_delayed_checkout(&state);
	for (pos = 0; pos < active_nr; pos++) {
		struct cache_entry *ce = active_cache[pos];
		if (ce->ce_flags & CE_MATCHED) {
			if (!ce_stage(ce)) {
				errs |= checkout_entry(ce, &state, NULL);
				continue;
			}
			if (opts->writeout_stage)
				errs |= checkout_stage(opts->writeout_stage, ce, pos, &state);
			else if (opts->merge)
				errs |= checkout_merged(pos, &state);
			pos = skip_same_name(ce, pos) - 1;
		}
	}
	errs |= finish_delayed_checkout(&state);

	if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
		die(_("unable to write new index file"));

	read_ref_full("HEAD", 0, &rev, NULL);
	head = lookup_commit_reference_gently(&rev, 1);

	errs |= post_checkout_hook(head, head, 0);
	return errs;
}