static int refresh(struct refresh_params *o, unsigned int flag) { setup_work_tree(); read_cache_preload(NULL); *o->has_errors |= refresh_cache(o->flags | flag); return 0; }
static int builtin_diff_index(struct rev_info *revs, int argc, const char **argv) { int cached = 0; while (1 < argc) { const char *arg = argv[1]; if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged")) cached = 1; else usage(builtin_diff_usage); argv++; argc--; } if (!cached) setup_work_tree(); /* * Make sure there is one revision (i.e. pending object), * and there is no revision filtering parameters. */ if (revs->pending.nr != 1 || revs->max_count != -1 || revs->min_age != -1 || revs->max_age != -1) usage(builtin_diff_usage); if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) { perror("read_cache_preload"); return -1; } return run_diff_index(revs, cached); }
static int stash_working_tree(struct stash_info *info, struct pathspec ps) { int ret = 0; struct rev_info rev; struct child_process cp_upd_index = CHILD_PROCESS_INIT; struct strbuf diff_output = STRBUF_INIT; struct index_state istate = { NULL }; init_revisions(&rev, NULL); set_alternate_index_output(stash_index_path.buf); if (reset_tree(&info->i_tree, 0, 0)) { ret = -1; goto done; } set_alternate_index_output(NULL); rev.prune_data = ps; rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = add_diff_to_buf; rev.diffopt.format_callback_data = &diff_output; if (read_cache_preload(&rev.diffopt.pathspec) < 0) { ret = -1; goto done; } add_pending_object(&rev, parse_object(the_repository, &info->b_commit), ""); if (run_diff_index(&rev, 0)) { ret = -1; goto done; } cp_upd_index.git_cmd = 1; argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add", "--remove", "--stdin", NULL); argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s", stash_index_path.buf); if (pipe_command(&cp_upd_index, diff_output.buf, diff_output.len, NULL, 0, NULL, 0)) { ret = -1; goto done; } if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0, NULL)) { ret = -1; goto done; } done: discard_index(&istate); UNLEAK(rev); object_array_clear(&rev.pending); strbuf_release(&diff_output); remove_path(stash_index_path.buf); return ret; }
int cmd_diff_files(int argc, const char **argv, const char *prefix) { struct rev_info rev; int result; unsigned options = 0; if (argc == 2 && !strcmp(argv[1], "-h")) usage(diff_files_usage); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ init_revisions(&rev, prefix); rev.abbrev = 0; precompose_argv(argc, argv); argc = setup_revisions(argc, argv, &rev, NULL); while (1 < argc && argv[1][0] == '-') { if (!strcmp(argv[1], "--base")) rev.max_count = 1; else if (!strcmp(argv[1], "--ours")) rev.max_count = 2; else if (!strcmp(argv[1], "--theirs")) rev.max_count = 3; else if (!strcmp(argv[1], "-q")) options |= DIFF_SILENT_ON_REMOVED; else usage(diff_files_usage); argv++; argc--; } if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; /* * Make sure there are NO revision (i.e. pending object) parameter, * rev.max_count is reasonable (0 <= n <= 3), and * there is no other revision filtering parameters. */ if (rev.pending.nr || rev.min_age != -1 || rev.max_age != -1 || 3 < rev.max_count) usage(diff_files_usage); /* * "diff-files --base -p" should not combine merges because it * was not asked to. "diff-files -c -p" should not densify * (the user should ask with "diff-files --cc" explicitly). */ if (rev.max_count == -1 && !rev.combine_merges && (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) rev.combine_merges = rev.dense_combined_merges = 1; if (read_cache_preload(&rev.diffopt.pathspec) < 0) { perror("read_cache_preload"); return -1; } result = run_diff_files(&rev, options); return diff_result_code(&rev.diffopt, result); }
static int switch_branches(const struct checkout_opts *opts, struct branch_info *new_branch_info) { int ret = 0; struct branch_info old_branch_info; void *path_to_free; struct object_id rev; int flag, writeout_error = 0; trace2_cmd_mode("branch"); memset(&old_branch_info, 0, sizeof(old_branch_info)); old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag); if (old_branch_info.path) old_branch_info.commit = lookup_commit_reference_gently(the_repository, &rev, 1); if (!(flag & REF_ISSYMREF)) old_branch_info.path = NULL; if (old_branch_info.path) skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name); if (!new_branch_info->name) { new_branch_info->name = "HEAD"; new_branch_info->commit = old_branch_info.commit; if (!new_branch_info->commit) die(_("You are on a branch yet to be born")); parse_commit_or_die(new_branch_info->commit); } /* optimize the "checkout -b <new_branch> path */ if (skip_merge_working_tree(opts, &old_branch_info, new_branch_info)) { if (!checkout_optimize_new_branch && !opts->quiet) { if (read_cache_preload(NULL) < 0) return error(_("index file corrupt")); show_local_changes(&new_branch_info->commit->object, &opts->diff_options); } } else { ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error); if (ret) { free(path_to_free); return ret; } } if (!opts->quiet && !old_branch_info.path && old_branch_info.commit && new_branch_info->commit != old_branch_info.commit) orphaned_commit_warning(old_branch_info.commit, new_branch_info->commit); update_refs_for_switch(opts, &old_branch_info, new_branch_info); ret = post_checkout_hook(old_branch_info.commit, new_branch_info->commit, 1); free(path_to_free); return ret || writeout_error; }
int cmd_diff_index(int argc, const char **argv, const char *prefix) { struct rev_info rev; int cached = 0; int i; int result; if (argc == 2 && !strcmp(argv[1], "-h")) usage(diff_cache_usage); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ init_revisions(&rev, prefix); rev.abbrev = 0; precompose_argv(argc, argv); argc = setup_revisions(argc, argv, &rev, NULL); for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--cached")) cached = 1; else usage(diff_cache_usage); } if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; /* * Make sure there is one revision (i.e. pending object), * and there is no revision filtering parameters. */ if (rev.pending.nr != 1 || rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1) usage(diff_cache_usage); if (!cached) { setup_work_tree(); if (read_cache_preload(&rev.diffopt.pathspec) < 0) { perror("read_cache_preload"); return -1; } } else if (read_cache() < 0) { perror("read_cache"); return -1; } result = run_diff_index(&rev, cached); UNLEAK(rev); return diff_result_code(&rev.diffopt, result); }
static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv) { int result; unsigned int options = 0; while (1 < argc && argv[1][0] == '-') { if (!strcmp(argv[1], "--base")) revs->max_count = 1; else if (!strcmp(argv[1], "--ours")) revs->max_count = 2; else if (!strcmp(argv[1], "--theirs")) revs->max_count = 3; else if (!strcmp(argv[1], "-q")) options |= DIFF_SILENT_ON_REMOVED; else if (!strcmp(argv[1], "-h")) usage(builtin_diff_usage); else return error("invalid option: %s", argv[1]); argv++; argc--; } /* * "diff --base" should not combine merges because it was not * asked to. "diff -c" should not densify (if the user wants * dense one, --cc can be explicitly asked for, or just rely * on the default). */ if (revs->max_count == -1 && !revs->combine_merges && (revs->diffopt.output_format & DIFF_FORMAT_PATCH)) revs->combine_merges = revs->dense_combined_merges = 1; setup_work_tree(); if (read_cache_preload(revs->diffopt.paths) < 0) { perror("read_cache_preload"); return -1; } result = run_diff_files(revs, options); return diff_result_code(&revs->diffopt, result); }
static int reset_tree(struct object_id *i_tree, int update, int reset) { int nr_trees = 1; struct unpack_trees_options opts; struct tree_desc t[MAX_UNPACK_TREES]; struct tree *tree; struct lock_file lock_file = LOCK_INIT; read_cache_preload(NULL); if (refresh_cache(REFRESH_QUIET)) return -1; hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); memset(&opts, 0, sizeof(opts)); tree = parse_tree_indirect(i_tree); if (parse_tree(tree)) return -1; init_tree_desc(t, tree->buffer, tree->size); opts.head_idx = 1; opts.src_index = &the_index; opts.dst_index = &the_index; opts.merge = 1; opts.reset = reset; opts.update = update; opts.fn = oneway_merge; if (unpack_trees(nr_trees, t, &opts)) return -1; if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) return error(_("unable to write new index file")); return 0; }
static char *prepare_index(int argc, const char **argv, const char *prefix, const struct commit *current_head, int is_status) { int fd; struct string_list partial; struct pathspec pathspec; int refresh_flags = REFRESH_QUIET; if (is_status) refresh_flags |= REFRESH_UNMERGED; parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_FULL, prefix, argv); if (read_cache_preload(&pathspec) < 0) die(_("index file corrupt")); if (interactive) { char *old_index_env = NULL; fd = hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die(_("unable to create temporary index")); old_index_env = getenv(INDEX_ENVIRONMENT); setenv(INDEX_ENVIRONMENT, index_lock.filename, 1); if (interactive_add(argc, argv, prefix, patch_interactive) != 0) die(_("interactive add failed")); if (old_index_env && *old_index_env) setenv(INDEX_ENVIRONMENT, old_index_env, 1); else unsetenv(INDEX_ENVIRONMENT); discard_cache(); read_cache_from(index_lock.filename); commit_style = COMMIT_NORMAL; return index_lock.filename; } /* * Non partial, non as-is commit. * * (1) get the real index; * (2) update the_index as necessary; * (3) write the_index out to the real index (still locked); * (4) return the name of the locked index file. * * The caller should run hooks on the locked real index, and * (A) if all goes well, commit the real index; * (B) on failure, rollback the real index. */ if (all || (also && pathspec.nr)) { fd = hold_locked_index(&index_lock, 1); add_files_to_cache(also ? prefix : NULL, &pathspec, 0); refresh_cache_or_die(refresh_flags); update_main_cache_tree(WRITE_TREE_SILENT); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die(_("unable to write new_index file")); commit_style = COMMIT_NORMAL; return index_lock.filename; } /* * As-is commit. * * (1) return the name of the real index file. * * The caller should run hooks on the real index, * and create commit from the_index. * We still need to refresh the index here. */ if (!only && !pathspec.nr) { fd = hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); if (active_cache_changed) { update_main_cache_tree(WRITE_TREE_SILENT); if (write_cache(fd, active_cache, active_nr) || commit_locked_index(&index_lock)) die(_("unable to write new_index file")); } else { rollback_lock_file(&index_lock); } commit_style = COMMIT_AS_IS; return get_index_file(); } /* * A partial commit. * * (0) find the set of affected paths; * (1) get lock on the real index file; * (2) update the_index with the given paths; * (3) write the_index out to the real index (still locked); * (4) get lock on the false index file; * (5) reset the_index from HEAD; * (6) update the_index the same way as (2); * (7) write the_index out to the false index file; * (8) return the name of the false index file (still locked); * * The caller should run hooks on the locked false index, and * create commit from it. Then * (A) if all goes well, commit the real index; * (B) on failure, rollback the real index; * In either case, rollback the false index. */ commit_style = COMMIT_PARTIAL; if (whence != FROM_COMMIT) { if (whence == FROM_MERGE) die(_("cannot do a partial commit during a merge.")); else if (whence == FROM_CHERRY_PICK) die(_("cannot do a partial commit during a cherry-pick.")); } memset(&partial, 0, sizeof(partial)); partial.strdup_strings = 1; if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec)) exit(1); discard_cache(); if (read_cache() < 0) die(_("cannot read the index")); fd = hold_locked_index(&index_lock, 1); add_remove_files(&partial); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die(_("unable to write new_index file")); fd = hold_lock_file_for_update(&false_lock, git_path("next-index-%"PRIuMAX, (uintmax_t) getpid()), LOCK_DIE_ON_ERROR); create_base_index(current_head); add_remove_files(&partial); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&false_lock)) die(_("unable to write temporary index file")); discard_cache(); read_cache_from(false_lock.filename); return false_lock.filename; }
int cmd_status(int argc, const char **argv, const char *prefix) { static struct wt_status s; int fd; unsigned char sha1[20]; static struct option builtin_status_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT_SET_INT('s', "short", &status_format, N_("show status concisely"), STATUS_FORMAT_SHORT), OPT_BOOL('b', "branch", &s.show_branch, N_("show branch information")), OPT_SET_INT(0, "porcelain", &status_format, N_("machine-readable output"), STATUS_FORMAT_PORCELAIN), OPT_SET_INT(0, "long", &status_format, N_("show status in long format (default)"), STATUS_FORMAT_LONG), OPT_BOOL('z', "null", &s.null_termination, N_("terminate entries with NUL")), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, OPT_BOOL(0, "ignored", &show_ignored_in_status, N_("show ignored files")), { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"), N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, OPT_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")), OPT_END(), }; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_status_usage, builtin_status_options); status_init_config(&s, git_status_config); argc = parse_options(argc, argv, prefix, builtin_status_options, builtin_status_usage, 0); finalize_colopts(&s.colopts, -1); finalize_deferred_config(&s); handle_untracked_files_arg(&s); if (show_ignored_in_status) s.show_ignored_files = 1; parse_pathspec(&s.pathspec, 0, PATHSPEC_PREFER_FULL, prefix, argv); read_cache_preload(&s.pathspec); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL); fd = hold_locked_index(&index_lock, 0); if (0 <= fd) update_index_if_able(&the_index, &index_lock); s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0; s.ignore_submodule_arg = ignore_submodule_arg; wt_status_collect(&s); if (s.relative_paths) s.prefix = prefix; switch (status_format) { case STATUS_FORMAT_SHORT: wt_shortstatus_print(&s); break; case STATUS_FORMAT_PORCELAIN: wt_porcelain_print(&s); break; case STATUS_FORMAT_UNSPECIFIED: die("BUG: finalize_deferred_config() should have been called"); break; case STATUS_FORMAT_NONE: case STATUS_FORMAT_LONG: s.verbose = verbose; s.ignore_submodule_arg = ignore_submodule_arg; wt_status_print(&s); break; } return 0; }
int cmd_status(int argc, const char **argv, const char *prefix) { struct wt_status s; unsigned char sha1[20]; static struct option builtin_status_options[] = { OPT__VERBOSE(&verbose), OPT_SET_INT('s', "short", &status_format, "show status concisely", STATUS_FORMAT_SHORT), OPT_SET_INT(0, "porcelain", &status_format, "show porcelain output format", STATUS_FORMAT_PORCELAIN), OPT_BOOLEAN('z', "null", &null_termination, "terminate entries with NUL"), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, OPT_END(), }; if (null_termination && status_format == STATUS_FORMAT_LONG) status_format = STATUS_FORMAT_PORCELAIN; wt_status_prepare(&s); git_config(git_status_config, &s); in_merge = file_exists(git_path("MERGE_HEAD")); argc = parse_options(argc, argv, prefix, builtin_status_options, builtin_status_usage, 0); handle_untracked_files_arg(&s); if (*argv) s.pathspec = get_pathspec(prefix, argv); read_cache_preload(s.pathspec); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL); s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0; s.in_merge = in_merge; wt_status_collect(&s); if (s.relative_paths) s.prefix = prefix; if (s.use_color == -1) s.use_color = git_use_color_default; if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; switch (status_format) { case STATUS_FORMAT_SHORT: wt_shortstatus_print(&s, null_termination); break; case STATUS_FORMAT_PORCELAIN: wt_porcelain_print(&s, null_termination); break; case STATUS_FORMAT_LONG: s.verbose = verbose; wt_status_print(&s); break; } return 0; }
int cmd_add(int argc, const char **argv, const char *prefix) { int exit_status = 0; struct pathspec pathspec; struct dir_struct dir; int flags; int add_new_files; int require_pathspec; char *seen = NULL; struct lock_file lock_file = LOCK_INIT; git_config(add_config, NULL); argc = parse_options(argc, argv, prefix, builtin_add_options, builtin_add_usage, PARSE_OPT_KEEP_ARGV0); if (patch_interactive) add_interactive = 1; if (add_interactive) exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive)); if (edit_interactive) return(edit_patch(argc, argv, prefix)); argc--; argv++; if (0 <= addremove_explicit) addremove = addremove_explicit; else if (take_worktree_changes && ADDREMOVE_DEFAULT) addremove = 0; /* "-u" was given but not "-A" */ if (addremove && take_worktree_changes) die(_("-A and -u are mutually incompatible")); if (!take_worktree_changes && addremove_explicit < 0 && argc) /* Turn "git add pathspec..." to "git add -A pathspec..." */ addremove = 1; if (!show_only && ignore_missing) die(_("Option --ignore-missing can only be used together with --dry-run")); if (chmod_arg && ((chmod_arg[0] != '-' && chmod_arg[0] != '+') || chmod_arg[1] != 'x' || chmod_arg[2])) die(_("--chmod param '%s' must be either -x or +x"), chmod_arg); add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize; require_pathspec = !(take_worktree_changes || (0 < addremove_explicit)); hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | (show_only ? ADD_CACHE_PRETEND : 0) | (intent_to_add ? ADD_CACHE_INTENT : 0) | (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | (!(addremove || take_worktree_changes) ? ADD_CACHE_IGNORE_REMOVAL : 0)); if (require_pathspec && argc == 0) { fprintf(stderr, _("Nothing specified, nothing added.\n")); fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n")); return 0; } /* * Check the "pathspec '%s' did not match any files" block * below before enabling new magic. */ parse_pathspec(&pathspec, PATHSPEC_ATTR, PATHSPEC_PREFER_FULL | PATHSPEC_SYMLINK_LEADING_PATH, prefix, argv); if (read_cache_preload(&pathspec) < 0) die(_("index file corrupt")); die_in_unpopulated_submodule(&the_index, prefix); die_path_inside_submodule(&the_index, &pathspec); if (add_new_files) { int baselen; /* Set up the default git porcelain excludes */ memset(&dir, 0, sizeof(dir)); if (!ignored_too) { dir.flags |= DIR_COLLECT_IGNORED; setup_standard_excludes(&dir); } /* This picks up the paths that are not tracked */ baselen = fill_directory(&dir, &the_index, &pathspec); if (pathspec.nr) seen = prune_directory(&dir, &pathspec, baselen); } if (refresh_only) { refresh(verbose, &pathspec); goto finish; } if (pathspec.nr) { int i; if (!seen) seen = find_pathspecs_matching_against_index(&pathspec, &the_index); /* * file_exists() assumes exact match */ GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL | PATHSPEC_GLOB | PATHSPEC_ICASE | PATHSPEC_EXCLUDE); for (i = 0; i < pathspec.nr; i++) { const char *path = pathspec.items[i].match; if (pathspec.items[i].magic & PATHSPEC_EXCLUDE) continue; if (!seen[i] && path[0] && ((pathspec.items[i].magic & (PATHSPEC_GLOB | PATHSPEC_ICASE)) || !file_exists(path))) { if (ignore_missing) { int dtype = DT_UNKNOWN; if (is_excluded(&dir, &the_index, path, &dtype)) dir_add_ignored(&dir, &the_index, path, pathspec.items[i].len); } else die(_("pathspec '%s' did not match any files"), pathspec.items[i].original); } } free(seen); } plug_bulk_checkin(); if (add_renormalize) exit_status |= renormalize_tracked_files(&pathspec, flags); else exit_status |= add_files_to_cache(prefix, &pathspec, flags); if (add_new_files) exit_status |= add_files(&dir, flags); if (chmod_arg && pathspec.nr) chmod_pathspec(&pathspec, chmod_arg[0]); unplug_bulk_checkin(); finish: if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("Unable to write new index file")); UNLEAK(pathspec); UNLEAK(dir); return exit_status; }
int cmd_status(int argc, const char **argv, const char *prefix) { struct wt_status s; int fd; unsigned char sha1[20]; static struct option builtin_status_options[] = { OPT__VERBOSE(&verbose, "be verbose"), OPT_SET_INT('s', "short", &status_format, "show status concisely", STATUS_FORMAT_SHORT), OPT_BOOLEAN('b', "branch", &status_show_branch, "show branch information"), OPT_SET_INT(0, "porcelain", &status_format, "show porcelain output format", STATUS_FORMAT_PORCELAIN), OPT_BOOLEAN('z', "null", &null_termination, "terminate entries with NUL"), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, OPT_BOOLEAN(0, "ignored", &show_ignored_in_status, "show ignored files"), { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when", "ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, OPT_END(), }; if (null_termination && status_format == STATUS_FORMAT_LONG) status_format = STATUS_FORMAT_PORCELAIN; wt_status_prepare(&s); gitmodules_config(); git_config(git_status_config, &s); in_merge = file_exists(git_path("MERGE_HEAD")); argc = parse_options(argc, argv, prefix, builtin_status_options, builtin_status_usage, 0); handle_untracked_files_arg(&s); if (show_ignored_in_status) s.show_ignored_files = 1; if (*argv) s.pathspec = get_pathspec(prefix, argv); read_cache_preload(s.pathspec); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL); fd = hold_locked_index(&index_lock, 0); if (0 <= fd) { if (active_cache_changed && !write_cache(fd, active_cache, active_nr)) commit_locked_index(&index_lock); else rollback_lock_file(&index_lock); } s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0; s.in_merge = in_merge; s.ignore_submodule_arg = ignore_submodule_arg; wt_status_collect(&s); if (s.relative_paths) s.prefix = prefix; if (s.use_color == -1) s.use_color = git_use_color_default; if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; switch (status_format) { case STATUS_FORMAT_SHORT: wt_shortstatus_print(&s, null_termination, status_show_branch); break; case STATUS_FORMAT_PORCELAIN: wt_porcelain_print(&s, null_termination); break; case STATUS_FORMAT_LONG: s.verbose = verbose; s.ignore_submodule_arg = ignore_submodule_arg; wt_status_print(&s); break; } return 0; }
int cmd_describe(int argc, const char **argv, const char *prefix) { int contains = 0; struct option options[] = { OPT_BOOLEAN(0, "contains", &contains, N_("find the tag that comes after the commit")), OPT_BOOLEAN(0, "debug", &debug, N_("debug search strategy on stderr")), OPT_BOOLEAN(0, "all", &all, N_("use any ref")), OPT_BOOLEAN(0, "tags", &tags, N_("use any tag, even unannotated")), OPT_BOOLEAN(0, "long", &longformat, N_("always use long format")), OPT__ABBREV(&abbrev), OPT_SET_INT(0, "exact-match", &max_candidates, N_("only output exact matches"), 0), OPT_INTEGER(0, "candidates", &max_candidates, N_("consider <n> most recent tags (default: 10)")), OPT_STRING(0, "match", &pattern, N_("pattern"), N_("only consider tags matching <pattern>")), OPT_BOOLEAN(0, "always", &always, N_("show abbreviated commit object as fallback")), {OPTION_STRING, 0, "dirty", &dirty, N_("mark"), N_("append <mark> on dirty working tree (default: \"-dirty\")"), PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"}, OPT_END(), }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, describe_usage, 0); if (abbrev < 0) abbrev = DEFAULT_ABBREV; if (max_candidates < 0) max_candidates = 0; else if (max_candidates > MAX_TAGS) max_candidates = MAX_TAGS; save_commit_buffer = 0; if (longformat && abbrev == 0) die(_("--long is incompatible with --abbrev=0")); if (contains) { const char **args = xmalloc((7 + argc) * sizeof(char *)); int i = 0; args[i++] = "name-rev"; args[i++] = "--name-only"; args[i++] = "--no-undefined"; if (always) args[i++] = "--always"; if (!all) { args[i++] = "--tags"; if (pattern) { char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1); sprintf(s, "--refs=refs/tags/%s", pattern); args[i++] = s; } } memcpy(args + i, argv, argc * sizeof(char *)); args[i + argc] = NULL; return cmd_name_rev(i + argc, args, prefix); } init_hash(&names); for_each_rawref(get_name, NULL); if (!names.nr && !always) die(_("No names found, cannot describe anything.")); if (argc == 0) { if (dirty) { static struct lock_file index_lock; int fd; read_cache_preload(NULL); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL); fd = hold_locked_index(&index_lock, 0); if (0 <= fd) update_index_if_able(&the_index, &index_lock); if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1, diff_index_args, prefix)) dirty = NULL; } describe("HEAD", 1); } else if (dirty) { die(_("--dirty is incompatible with committishes")); } else { while (argc-- > 0) { describe(*argv++, argc == 0); } } return 0; }
static int do_apply_stash(const char *prefix, struct stash_info *info, int index, int quiet) { int ret; int has_index = index; struct merge_options o; struct object_id c_tree; struct object_id index_tree; struct commit *result; const struct object_id *bases[1]; read_cache_preload(NULL); if (refresh_cache(REFRESH_QUIET)) return -1; if (write_cache_as_tree(&c_tree, 0, NULL)) return error(_("cannot apply a stash in the middle of a merge")); if (index) { if (oideq(&info->b_tree, &info->i_tree) || oideq(&c_tree, &info->i_tree)) { has_index = 0; } else { struct strbuf out = STRBUF_INIT; if (diff_tree_binary(&out, &info->w_commit)) { strbuf_release(&out); return error(_("could not generate diff %s^!."), oid_to_hex(&info->w_commit)); } ret = apply_cached(&out); strbuf_release(&out); if (ret) return error(_("conflicts in index." "Try without --index.")); discard_cache(); read_cache(); if (write_cache_as_tree(&index_tree, 0, NULL)) return error(_("could not save index tree")); reset_head(); } } if (info->has_u && restore_untracked(&info->u_tree)) return error(_("could not restore untracked files from stash")); init_merge_options(&o, the_repository); o.branch1 = "Updated upstream"; o.branch2 = "Stashed changes"; if (oideq(&info->b_tree, &c_tree)) o.branch1 = "Version stash was based on"; if (quiet) o.verbosity = 0; if (o.verbosity >= 3) printf_ln(_("Merging %s with %s"), o.branch1, o.branch2); bases[0] = &info->b_tree; ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases, &result); if (ret) { rerere(0); if (index) fprintf_ln(stderr, _("Index was not unstashed.")); return ret; } if (has_index) { if (reset_tree(&index_tree, 0, 0)) return -1; } else { struct strbuf out = STRBUF_INIT; if (get_newly_staged(&out, &c_tree)) { strbuf_release(&out); return -1; } if (reset_tree(&c_tree, 0, 1)) { strbuf_release(&out); return -1; } ret = update_index(&out); strbuf_release(&out); if (ret) return -1; discard_cache(); } if (quiet) { if (refresh_cache(REFRESH_QUIET)) warning("could not refresh index"); } else { struct child_process cp = CHILD_PROCESS_INIT; /* * Status is quite simple and could be replaced with calls to * wt_status in the future, but it adds complexities which may * require more tests. */ cp.git_cmd = 1; cp.dir = prefix; argv_array_push(&cp.args, "status"); run_command(&cp); } return 0; }
static int do_push_stash(struct pathspec ps, const char *stash_msg, int quiet, int keep_index, int patch_mode, int include_untracked) { int ret = 0; struct stash_info info; struct strbuf patch = STRBUF_INIT; struct strbuf stash_msg_buf = STRBUF_INIT; struct strbuf untracked_files = STRBUF_INIT; if (patch_mode && keep_index == -1) keep_index = 1; if (patch_mode && include_untracked) { fprintf_ln(stderr, _("Can't use --patch and --include-untracked" " or --all at the same time")); ret = -1; goto done; } read_cache_preload(NULL); if (!include_untracked && ps.nr) { int i; char *ps_matched = xcalloc(ps.nr, 1); for (i = 0; i < active_nr; i++) ce_path_match(&the_index, active_cache[i], &ps, ps_matched); if (report_path_error(ps_matched, &ps, NULL)) { fprintf_ln(stderr, _("Did you forget to 'git add'?")); ret = -1; free(ps_matched); goto done; } free(ps_matched); } if (refresh_cache(REFRESH_QUIET)) { ret = -1; goto done; } if (!check_changes(ps, include_untracked, &untracked_files)) { if (!quiet) printf_ln(_("No local changes to save")); goto done; } if (!reflog_exists(ref_stash) && do_clear_stash()) { ret = -1; if (!quiet) fprintf_ln(stderr, _("Cannot initialize stash")); goto done; } if (stash_msg) strbuf_addstr(&stash_msg_buf, stash_msg); if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, &info, &patch, quiet)) { ret = -1; goto done; } if (do_store_stash(&info.w_commit, stash_msg_buf.buf, 1)) { ret = -1; if (!quiet) fprintf_ln(stderr, _("Cannot save the current status")); goto done; } if (!quiet) printf_ln(_("Saved working directory and index state %s"), stash_msg_buf.buf); if (!patch_mode) { if (include_untracked && !ps.nr) { struct child_process cp = CHILD_PROCESS_INIT; cp.git_cmd = 1; argv_array_pushl(&cp.args, "clean", "--force", "--quiet", "-d", NULL); if (include_untracked == INCLUDE_ALL_FILES) argv_array_push(&cp.args, "-x"); if (run_command(&cp)) { ret = -1; goto done; } } discard_cache(); if (ps.nr) { struct child_process cp_add = CHILD_PROCESS_INIT; struct child_process cp_diff = CHILD_PROCESS_INIT; struct child_process cp_apply = CHILD_PROCESS_INIT; struct strbuf out = STRBUF_INIT; cp_add.git_cmd = 1; argv_array_push(&cp_add.args, "add"); if (!include_untracked) argv_array_push(&cp_add.args, "-u"); if (include_untracked == INCLUDE_ALL_FILES) argv_array_push(&cp_add.args, "--force"); argv_array_push(&cp_add.args, "--"); add_pathspecs(&cp_add.args, ps); if (run_command(&cp_add)) { ret = -1; goto done; } cp_diff.git_cmd = 1; argv_array_pushl(&cp_diff.args, "diff-index", "-p", "--cached", "--binary", "HEAD", "--", NULL); add_pathspecs(&cp_diff.args, ps); if (pipe_command(&cp_diff, NULL, 0, &out, 0, NULL, 0)) { ret = -1; goto done; } cp_apply.git_cmd = 1; argv_array_pushl(&cp_apply.args, "apply", "--index", "-R", NULL); if (pipe_command(&cp_apply, out.buf, out.len, NULL, 0, NULL, 0)) { ret = -1; goto done; } } else { struct child_process cp = CHILD_PROCESS_INIT; cp.git_cmd = 1; argv_array_pushl(&cp.args, "reset", "--hard", "-q", NULL); if (run_command(&cp)) { ret = -1; goto done; } } if (keep_index == 1 && !is_null_oid(&info.i_tree)) { struct child_process cp_ls = CHILD_PROCESS_INIT; struct child_process cp_checkout = CHILD_PROCESS_INIT; struct strbuf out = STRBUF_INIT; if (reset_tree(&info.i_tree, 0, 1)) { ret = -1; goto done; } cp_ls.git_cmd = 1; argv_array_pushl(&cp_ls.args, "ls-files", "-z", "--modified", "--", NULL); add_pathspecs(&cp_ls.args, ps); if (pipe_command(&cp_ls, NULL, 0, &out, 0, NULL, 0)) { ret = -1; goto done; } cp_checkout.git_cmd = 1; argv_array_pushl(&cp_checkout.args, "checkout-index", "-z", "--force", "--stdin", NULL); if (pipe_command(&cp_checkout, out.buf, out.len, NULL, 0, NULL, 0)) { ret = -1; goto done; } } goto done; } else { struct child_process cp = CHILD_PROCESS_INIT; cp.git_cmd = 1; argv_array_pushl(&cp.args, "apply", "-R", NULL); if (pipe_command(&cp, patch.buf, patch.len, NULL, 0, NULL, 0)) { if (!quiet) fprintf_ln(stderr, _("Cannot remove " "worktree changes")); ret = -1; goto done; } if (keep_index < 1) { struct child_process cp = CHILD_PROCESS_INIT; discard_cache(); cp.git_cmd = 1; argv_array_pushl(&cp.args, "reset", "-q", "--", NULL); add_pathspecs(&cp.args, ps); if (run_command(&cp)) { ret = -1; goto done; } } goto done; } done: strbuf_release(&stash_msg_buf); return ret; }
static int do_create_stash(struct pathspec ps, struct strbuf *stash_msg_buf, int include_untracked, int patch_mode, struct stash_info *info, struct strbuf *patch, int quiet) { int ret = 0; int flags = 0; int untracked_commit_option = 0; const char *head_short_sha1 = NULL; const char *branch_ref = NULL; const char *branch_name = "(no branch)"; struct commit *head_commit = NULL; struct commit_list *parents = NULL; struct strbuf msg = STRBUF_INIT; struct strbuf commit_tree_label = STRBUF_INIT; struct strbuf untracked_files = STRBUF_INIT; prepare_fallback_ident("git stash", "git@stash"); read_cache_preload(NULL); refresh_cache(REFRESH_QUIET); if (get_oid("HEAD", &info->b_commit)) { if (!quiet) fprintf_ln(stderr, _("You do not have " "the initial commit yet")); ret = -1; goto done; } else { head_commit = lookup_commit(the_repository, &info->b_commit); } if (!check_changes(ps, include_untracked, &untracked_files)) { ret = 1; goto done; } branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags); if (flags & REF_ISSYMREF) branch_name = strrchr(branch_ref, '/') + 1; head_short_sha1 = find_unique_abbrev(&head_commit->object.oid, DEFAULT_ABBREV); strbuf_addf(&msg, "%s: %s ", branch_name, head_short_sha1); pp_commit_easy(CMIT_FMT_ONELINE, head_commit, &msg); strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf); commit_list_insert(head_commit, &parents); if (write_cache_as_tree(&info->i_tree, 0, NULL) || commit_tree(commit_tree_label.buf, commit_tree_label.len, &info->i_tree, parents, &info->i_commit, NULL, NULL)) { if (!quiet) fprintf_ln(stderr, _("Cannot save the current " "index state")); ret = -1; goto done; } if (include_untracked) { if (save_untracked_files(info, &msg, untracked_files)) { if (!quiet) fprintf_ln(stderr, _("Cannot save " "the untracked files")); ret = -1; goto done; } untracked_commit_option = 1; } if (patch_mode) { ret = stash_patch(info, ps, patch, quiet); if (ret < 0) { if (!quiet) fprintf_ln(stderr, _("Cannot save the current " "worktree state")); goto done; } else if (ret > 0) { goto done; } } else { if (stash_working_tree(info, ps)) { if (!quiet) fprintf_ln(stderr, _("Cannot save the current " "worktree state")); ret = -1; goto done; } } if (!stash_msg_buf->len) strbuf_addf(stash_msg_buf, "WIP on %s", msg.buf); else strbuf_insertf(stash_msg_buf, 0, "On %s: ", branch_name); /* * `parents` will be empty after calling `commit_tree()`, so there is * no need to call `free_commit_list()` */ parents = NULL; if (untracked_commit_option) commit_list_insert(lookup_commit(the_repository, &info->u_commit), &parents); commit_list_insert(lookup_commit(the_repository, &info->i_commit), &parents); commit_list_insert(head_commit, &parents); if (commit_tree(stash_msg_buf->buf, stash_msg_buf->len, &info->w_tree, parents, &info->w_commit, NULL, NULL)) { if (!quiet) fprintf_ln(stderr, _("Cannot record " "working tree state")); ret = -1; goto done; } done: strbuf_release(&commit_tree_label); strbuf_release(&msg); strbuf_release(&untracked_files); return ret; }
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; }
static int merge_working_tree(const struct checkout_opts *opts, struct branch_info *old_branch_info, struct branch_info *new_branch_info, int *writeout_error) { int ret; struct lock_file lock_file = LOCK_INIT; hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); if (read_cache_preload(NULL) < 0) return error(_("index file corrupt")); resolve_undo_clear(); if (opts->force) { ret = reset_tree(get_commit_tree(new_branch_info->commit), opts, 1, writeout_error); if (ret) return ret; } else { struct tree_desc trees[2]; struct tree *tree; struct unpack_trees_options topts; memset(&topts, 0, sizeof(topts)); topts.head_idx = -1; topts.src_index = &the_index; topts.dst_index = &the_index; setup_unpack_trees_porcelain(&topts, "checkout"); refresh_cache(REFRESH_QUIET); if (unmerged_cache()) { error(_("you need to resolve your current index first")); return 1; } /* 2-way merge to the new branch */ topts.initial_checkout = is_cache_unborn(); topts.update = 1; topts.merge = 1; topts.gently = opts->merge && old_branch_info->commit; topts.verbose_update = opts->show_progress; topts.fn = twoway_merge; if (opts->overwrite_ignore) { topts.dir = xcalloc(1, sizeof(*topts.dir)); topts.dir->flags |= DIR_SHOW_IGNORED; setup_standard_excludes(topts.dir); } tree = parse_tree_indirect(old_branch_info->commit ? &old_branch_info->commit->object.oid : the_hash_algo->empty_tree); init_tree_desc(&trees[0], tree->buffer, tree->size); tree = parse_tree_indirect(&new_branch_info->commit->object.oid); init_tree_desc(&trees[1], tree->buffer, tree->size); ret = unpack_trees(2, trees, &topts); clear_unpack_trees_porcelain(&topts); if (ret == -1) { /* * Unpack couldn't do a trivial merge; either * give up or do a real merge, depending on * whether the merge flag was used. */ struct tree *result; struct tree *work; struct merge_options o; if (!opts->merge) return 1; /* * Without old_branch_info->commit, the below is the same as * the two-tree unpack we already tried and failed. */ if (!old_branch_info->commit) return 1; /* Do more real merge */ /* * We update the index fully, then write the * tree from the index, then merge the new * branch with the current tree, with the old * branch as the base. Then we reset the index * (but not the working tree) to the new * branch, leaving the working tree as the * merged version, but skipping unmerged * entries in the index. */ add_files_to_cache(NULL, NULL, 0); /* * NEEDSWORK: carrying over local changes * when branches have different end-of-line * normalization (or clean+smudge rules) is * a pain; plumb in an option to set * o.renormalize? */ init_merge_options(&o, the_repository); o.verbosity = 0; work = write_tree_from_memory(&o); ret = reset_tree(get_commit_tree(new_branch_info->commit), opts, 1, writeout_error); if (ret) return ret; o.ancestor = old_branch_info->name; o.branch1 = new_branch_info->name; o.branch2 = "local"; ret = merge_trees(&o, get_commit_tree(new_branch_info->commit), work, get_commit_tree(old_branch_info->commit), &result); if (ret < 0) exit(128); ret = reset_tree(get_commit_tree(new_branch_info->commit), opts, 0, writeout_error); strbuf_release(&o.obuf); if (ret) return ret; } } if (!active_cache_tree) active_cache_tree = cache_tree(); if (!cache_tree_fully_valid(active_cache_tree)) cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR); if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); if (!opts->force && !opts->quiet) show_local_changes(&new_branch_info->commit->object, &opts->diff_options); return 0; }
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; }
static char *prepare_index(int argc, const char **argv, const char *prefix, int is_status) { int fd; struct string_list partial; const char **pathspec = NULL; int refresh_flags = REFRESH_QUIET; if (is_status) refresh_flags |= REFRESH_UNMERGED; if (interactive) { if (interactive_add(argc, argv, prefix) != 0) die("interactive add failed"); if (read_cache_preload(NULL) < 0) die("index file corrupt"); commit_style = COMMIT_AS_IS; return get_index_file(); } if (*argv) pathspec = get_pathspec(prefix, argv); if (read_cache_preload(pathspec) < 0) die("index file corrupt"); /* * Non partial, non as-is commit. * * (1) get the real index; * (2) update the_index as necessary; * (3) write the_index out to the real index (still locked); * (4) return the name of the locked index file. * * The caller should run hooks on the locked real index, and * (A) if all goes well, commit the real index; * (B) on failure, rollback the real index. */ if (all || (also && pathspec && *pathspec)) { fd = hold_locked_index(&index_lock, 1); add_files_to_cache(also ? prefix : NULL, pathspec, 0); refresh_cache_or_die(refresh_flags); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die("unable to write new_index file"); commit_style = COMMIT_NORMAL; return index_lock.filename; } /* * As-is commit. * * (1) return the name of the real index file. * * The caller should run hooks on the real index, * and create commit from the_index. * We still need to refresh the index here. */ if (!pathspec || !*pathspec) { fd = hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); if (active_cache_changed) { if (write_cache(fd, active_cache, active_nr) || commit_locked_index(&index_lock)) die("unable to write new_index file"); } else { rollback_lock_file(&index_lock); } commit_style = COMMIT_AS_IS; return get_index_file(); } /* * A partial commit. * * (0) find the set of affected paths; * (1) get lock on the real index file; * (2) update the_index with the given paths; * (3) write the_index out to the real index (still locked); * (4) get lock on the false index file; * (5) reset the_index from HEAD; * (6) update the_index the same way as (2); * (7) write the_index out to the false index file; * (8) return the name of the false index file (still locked); * * The caller should run hooks on the locked false index, and * create commit from it. Then * (A) if all goes well, commit the real index; * (B) on failure, rollback the real index; * In either case, rollback the false index. */ commit_style = COMMIT_PARTIAL; if (in_merge) die("cannot do a partial commit during a merge."); memset(&partial, 0, sizeof(partial)); partial.strdup_strings = 1; if (list_paths(&partial, initial_commit ? NULL : "HEAD", prefix, pathspec)) exit(1); discard_cache(); if (read_cache() < 0) die("cannot read the index"); fd = hold_locked_index(&index_lock, 1); add_remove_files(&partial); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die("unable to write new_index file"); fd = hold_lock_file_for_update(&false_lock, git_path("next-index-%"PRIuMAX, (uintmax_t) getpid()), LOCK_DIE_ON_ERROR); create_base_index(); add_remove_files(&partial); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&false_lock)) die("unable to write temporary index file"); discard_cache(); read_cache_from(false_lock.filename); return false_lock.filename; }