#include "builtin.h" #include "advice.h" #include "branch.h" #include "cache-tree.h" #include "checkout.h" #include "commit.h" #include "config.h" #include "diff.h" #include "dir.h" #include "environment.h" #include "gettext.h" #include "hex.h" #include "hook.h" #include "merge-ll.h" #include "lockfile.h" #include "mem-pool.h" #include "merge-recursive.h" #include "object-name.h" #include "object-store-ll.h" #include "parse-options.h" #include "path.h" #include "preload-index.h" #include "read-cache.h" #include "refs.h" #include "remote.h" #include "resolve-undo.h" #include "revision.h" #include "setup.h" #include "submodule.h" #include "symlinks.h" #include "trace2.h" #include "tree.h" #include "tree-walk.h" #include "unpack-trees.h" #include "wt-status.h" #include "xdiff-interface.h" #include "entry.h" #include "parallel-checkout.h" #include "add-interactive.h" static const char * const checkout_usage[] = { N_("git checkout [] "), N_("git checkout [] [] -- ..."), NULL, }; static const char * const switch_branch_usage[] = { N_("git switch [] []"), NULL, }; static const char * const restore_usage[] = { N_("git restore [] [--source=] ..."), NULL, }; struct checkout_opts { int patch_mode; int quiet; int merge; int force; int force_detach; int implicit_detach; int writeout_stage; int overwrite_ignore; int ignore_skipworktree; int ignore_other_worktrees; int show_progress; int count_checkout_paths; int overlay_mode; int dwim_new_local_branch; int discard_changes; int accept_ref; int accept_pathspec; int switch_branch_doing_nothing_is_ok; int only_merge_on_switching_branches; int can_switch_when_in_progress; int orphan_from_empty_tree; int empty_pathspec_ok; int checkout_index; int checkout_worktree; const char *ignore_unmerged_opt; int ignore_unmerged; int pathspec_file_nul; char *pathspec_from_file; const char *new_branch; const char *new_branch_force; const char *new_orphan_branch; int new_branch_log; enum branch_track track; struct diff_options diff_options; int conflict_style; int branch_exists; const char *prefix; struct pathspec pathspec; const char *from_treeish; struct tree *source_tree; }; #define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 } struct branch_info { char *name; /* The short name used */ char *path; /* The full name of a real branch */ struct commit *commit; /* The named commit */ char *refname; /* The full name of the ref being checked out. */ struct object_id oid; /* The object ID of the commit being checked out. */ /* * if not null the branch is detached because it's already * checked out in this checkout */ char *checkout; }; static void branch_info_release(struct branch_info *info) { free(info->name); free(info->path); free(info->refname); free(info->checkout); } static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit, int changed) { return run_hooks_l("post-checkout", oid_to_hex(old_commit ? &old_commit->object.oid : null_oid()), oid_to_hex(new_commit ? &new_commit->object.oid : null_oid()), changed ? "1" : "0", NULL); /* "new_commit" can be NULL when checking out from the index before a commit exists. */ } static int update_some(const struct object_id *oid, struct strbuf *base, const char *pathname, unsigned mode, void *context UNUSED) { int len; struct cache_entry *ce; int pos; if (S_ISDIR(mode)) return READ_TREE_RECURSIVE; len = base->len + strlen(pathname); ce = make_empty_cache_entry(the_repository->index, len); oidcpy(&ce->oid, oid); memcpy(ce->name, base->buf, base->len); memcpy(ce->name + base->len, pathname, len - base->len); ce->ce_flags = create_ce_flags(0) | CE_UPDATE; ce->ce_namelen = len; ce->ce_mode = create_ce_mode(mode); /* * If the entry is the same as the current index, we can leave the old * entry in place. Whether it is UPTODATE or not, checkout_entry will * do the right thing. */ pos = index_name_pos(the_repository->index, ce->name, ce->ce_namelen); if (pos >= 0) { struct cache_entry *old = the_repository->index->cache[pos]; if (ce->ce_mode == old->ce_mode && !ce_intent_to_add(old) && oideq(&ce->oid, &old->oid)) { old->ce_flags |= CE_UPDATE; discard_cache_entry(ce); return 0; } } add_index_entry(the_repository->index, ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); return 0; } static int read_tree_some(struct tree *tree, const struct pathspec *pathspec) { read_tree(the_repository, tree, pathspec, update_some, NULL); /* update the index with the given tree's info * for all args, expanding wildcards, and exit * with any non-zero return code. */ return 0; } static int skip_same_name(const struct cache_entry *ce, int pos) { while (++pos < the_repository->index->cache_nr && !strcmp(the_repository->index->cache[pos]->name, ce->name)) ; /* skip */ return pos; } static int check_stage(int stage, const struct cache_entry *ce, int pos, int overlay_mode) { while (pos < the_repository->index->cache_nr && !strcmp(the_repository->index->cache[pos]->name, ce->name)) { if (ce_stage(the_repository->index->cache[pos]) == stage) return 0; pos++; } if (!overlay_mode) return 0; if (stage == 2) return error(_("path '%s' does not have our version"), ce->name); else return error(_("path '%s' does not have their version"), ce->name); } static int check_stages(unsigned stages, const struct cache_entry *ce, int pos) { unsigned seen = 0; const char *name = ce->name; while (pos < the_repository->index->cache_nr) { ce = the_repository->index->cache[pos]; if (strcmp(name, ce->name)) break; seen |= (1 << ce_stage(ce)); pos++; } if ((stages & seen) != stages) return error(_("path '%s' does not have all necessary versions"), name); return 0; } static int checkout_stage(int stage, const struct cache_entry *ce, int pos, const struct checkout *state, int *nr_checkouts, int overlay_mode) { while (pos < the_repository->index->cache_nr && !strcmp(the_repository->index->cache[pos]->name, ce->name)) { if (ce_stage(the_repository->index->cache[pos]) == stage) return checkout_entry(the_repository->index->cache[pos], state, NULL, nr_checkouts); pos++; } if (!overlay_mode) { unlink_entry(ce, NULL); return 0; } if (stage == 2) return error(_("path '%s' does not have our version"), ce->name); else return error(_("path '%s' does not have their version"), ce->name); } static int checkout_merged(int pos, const struct checkout *state, int *nr_checkouts, struct mem_pool *ce_mem_pool, int conflict_style) { struct cache_entry *ce = the_repository->index->cache[pos]; const char *path = ce->name; mmfile_t ancestor, ours, theirs; enum ll_merge_result merge_status; int status; struct object_id oid; mmbuffer_t result_buf; struct object_id threeway[3]; unsigned mode = 0; struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT; int renormalize = 0; memset(threeway, 0, sizeof(threeway)); while (pos < the_repository->index->cache_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 = the_repository->index->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]); git_config_get_bool("merge.renormalize", &renormalize); ll_opts.renormalize = renormalize; ll_opts.conflict_style = conflict_style; merge_status = ll_merge(&result_buf, path, &ancestor, "base", &ours, "ours", &theirs, "theirs", state->istate, &ll_opts); free(ancestor.ptr); free(ours.ptr); free(theirs.ptr); if (merge_status == LL_MERGE_BINARY_CONFLICT) warning("Cannot merge binary files: %s (%s vs. %s)", path, "ours", "theirs"); if (merge_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 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, OBJ_BLOB, &oid)) die(_("Unable to add merge result for '%s'"), path); free(result_buf.ptr); ce = make_transient_cache_entry(mode, &oid, path, 2, ce_mem_pool); if (!ce) die(_("make_cache_entry failed for path '%s'"), path); status = checkout_entry(ce, state, NULL, nr_checkouts); return status; } static void mark_ce_for_checkout_overlay(struct cache_entry *ce, char *ps_matched, const struct checkout_opts *opts) { ce->ce_flags &= ~CE_MATCHED; if (!opts->ignore_skipworktree && ce_skip_worktree(ce)) return; if (opts->source_tree && !(ce->ce_flags & CE_UPDATE)) /* * "git checkout tree-ish -- path", but this entry * is in the original index but is not in tree-ish * or does not match the pathspec; it will not be * checked out to the working tree. We will not do * anything to this entry at all. */ return; /* * 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 (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(the_repository->index, ce, &opts->pathspec, ps_matched)) ce->ce_flags |= CE_MATCHED; } static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce, char *ps_matched, const struct checkout_opts *opts) { ce->ce_flags &= ~CE_MATCHED; if (!opts->ignore_skipworktree && ce_skip_worktree(ce)) return; if (ce_path_match(the_repository->index, ce, &opts->pathspec, ps_matched)) { ce->ce_flags |= CE_MATCHED; if (opts->source_tree && !(ce->ce_flags & CE_UPDATE)) /* * In overlay mode, but the path is not in * tree-ish, which means we should remove it * from the index and the working tree. */ ce->ce_flags |= CE_REMOVE | CE_WT_REMOVE; } } static int checkout_worktree(const struct checkout_opts *opts, const struct branch_info *info) { struct checkout state = CHECKOUT_INIT; int nr_checkouts = 0, nr_unmerged = 0; int errs = 0; int pos; int pc_workers, pc_threshold; struct mem_pool ce_mem_pool; state.force = 1; state.refresh_cache = 1; state.istate = the_repository->index; mem_pool_init(&ce_mem_pool, 0); get_parallel_checkout_configs(&pc_workers, &pc_threshold); init_checkout_metadata(&state.meta, info->refname, info->commit ? &info->commit->object.oid : &info->oid, NULL); enable_delayed_checkout(&state); if (pc_workers > 1) init_parallel_checkout(); enable_fscache(the_repository->index->cache_nr); for (pos = 0; pos < the_repository->index->cache_nr; pos++) { struct cache_entry *ce = the_repository->index->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, &ce_mem_pool, opts->conflict_style); pos = skip_same_name(ce, pos) - 1; } } if (pc_workers > 1) errs |= run_parallel_checkout(&state, pc_workers, pc_threshold, NULL, NULL); mem_pool_discard(&ce_mem_pool, should_validate_cache_entries()); disable_fscache(); remove_marked_cache_entries(the_repository->index, 1); remove_scheduled_dirs(); errs |= finish_delayed_checkout(&state, opts->show_progress); 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, repo_find_unique_abbrev(the_repository, &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); } return errs; } static int checkout_paths(const struct checkout_opts *opts, const struct branch_info *new_branch_info) { int pos; static char *ps_matched; struct object_id rev; struct commit *head; int errs = 0; struct lock_file lock_file = LOCK_INIT; int checkout_index; 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->ignore_unmerged && opts->patch_mode) die(_("'%s' cannot be used with updating paths"), opts->ignore_unmerged_opt); if (opts->force_detach) die(_("'%s' cannot be used with updating paths"), "--detach"); if (opts->merge && opts->patch_mode) die(_("options '%s' and '%s' cannot be used together"), "--merge", "--patch"); if (opts->ignore_unmerged && opts->merge) die(_("options '%s' and '%s' cannot be used together"), opts->ignore_unmerged_opt, "-m"); if (opts->new_branch) die(_("Cannot update paths and switch to branch '%s' at the same time."), opts->new_branch); if (!opts->checkout_worktree && !opts->checkout_index) die(_("neither '%s' or '%s' is specified"), "--staged", "--worktree"); if (!opts->checkout_worktree && !opts->from_treeish) die(_("'%s' must be used when '%s' is not specified"), "--worktree", "--source"); /* * Reject --staged option to the restore command when combined with * merge-related options. Use the accept_ref flag to distinguish it * from the checkout command, which does not accept --staged anyway. * * `restore --ours|--theirs --worktree --staged` could mean resolving * conflicted paths to one side in both the worktree and the index, * but does not currently. * * `restore --merge|--conflict=