diff options
Diffstat (limited to 'third_party/git/builtin')
118 files changed, 0 insertions, 72559 deletions
diff --git a/third_party/git/builtin/add.c b/third_party/git/builtin/add.c deleted file mode 100644 index a825887c503d..000000000000 --- a/third_party/git/builtin/add.c +++ /dev/null @@ -1,623 +0,0 @@ -/* - * "git add" builtin command - * - * Copyright (C) 2006 Linus Torvalds - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "builtin.h" -#include "lockfile.h" -#include "dir.h" -#include "pathspec.h" -#include "exec-cmd.h" -#include "cache-tree.h" -#include "run-command.h" -#include "parse-options.h" -#include "diff.h" -#include "diffcore.h" -#include "revision.h" -#include "bulk-checkin.h" -#include "strvec.h" -#include "submodule.h" -#include "add-interactive.h" - -static const char * const builtin_add_usage[] = { - N_("git add [<options>] [--] <pathspec>..."), - NULL -}; -static int patch_interactive, add_interactive, edit_interactive; -static int take_worktree_changes; -static int add_renormalize; -static int pathspec_file_nul; -static const char *pathspec_from_file; -static int legacy_stash_p; /* support for the scripted `git stash` */ - -struct update_callback_data { - int flags; - int add_errors; -}; - -static void chmod_pathspec(struct pathspec *pathspec, char flip) -{ - int i; - - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; - - if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL)) - continue; - - if (chmod_cache_entry(ce, flip) < 0) - fprintf(stderr, "cannot chmod %cx '%s'\n", flip, ce->name); - } -} - -static int fix_unmerged_status(struct diff_filepair *p, - struct update_callback_data *data) -{ - if (p->status != DIFF_STATUS_UNMERGED) - return p->status; - if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode) - /* - * This is not an explicit add request, and the - * path is missing from the working tree (deleted) - */ - return DIFF_STATUS_DELETED; - else - /* - * Either an explicit add request, or path exists - * in the working tree. An attempt to explicitly - * add a path that does not exist in the working tree - * will be caught as an error by the caller immediately. - */ - return DIFF_STATUS_MODIFIED; -} - -static void update_callback(struct diff_queue_struct *q, - struct diff_options *opt, void *cbdata) -{ - int i; - struct update_callback_data *data = cbdata; - - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - const char *path = p->one->path; - switch (fix_unmerged_status(p, data)) { - default: - die(_("unexpected diff status %c"), p->status); - case DIFF_STATUS_MODIFIED: - case DIFF_STATUS_TYPE_CHANGED: - if (add_file_to_index(&the_index, path, data->flags)) { - if (!(data->flags & ADD_CACHE_IGNORE_ERRORS)) - die(_("updating files failed")); - data->add_errors++; - } - break; - case DIFF_STATUS_DELETED: - if (data->flags & ADD_CACHE_IGNORE_REMOVAL) - break; - if (!(data->flags & ADD_CACHE_PRETEND)) - remove_file_from_index(&the_index, path); - if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE)) - printf(_("remove '%s'\n"), path); - break; - } - } -} - -int add_files_to_cache(const char *prefix, - const struct pathspec *pathspec, int flags) -{ - struct update_callback_data data; - struct rev_info rev; - - memset(&data, 0, sizeof(data)); - data.flags = flags; - - repo_init_revisions(the_repository, &rev, prefix); - setup_revisions(0, NULL, &rev, NULL); - if (pathspec) - copy_pathspec(&rev.prune_data, pathspec); - rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; - rev.diffopt.format_callback = update_callback; - rev.diffopt.format_callback_data = &data; - rev.diffopt.flags.override_submodule_config = 1; - rev.max_count = 0; /* do not compare unmerged paths with stage #2 */ - run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); - clear_pathspec(&rev.prune_data); - return !!data.add_errors; -} - -static int renormalize_tracked_files(const struct pathspec *pathspec, int flags) -{ - int i, retval = 0; - - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; - - if (ce_stage(ce)) - continue; /* do not touch unmerged paths */ - if (!S_ISREG(ce->ce_mode) && !S_ISLNK(ce->ce_mode)) - continue; /* do not touch non blobs */ - if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL)) - continue; - retval |= add_file_to_cache(ce->name, flags | ADD_CACHE_RENORMALIZE); - } - - return retval; -} - -static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix) -{ - char *seen; - int i; - struct dir_entry **src, **dst; - - seen = xcalloc(pathspec->nr, 1); - - src = dst = dir->entries; - i = dir->nr; - while (--i >= 0) { - struct dir_entry *entry = *src++; - if (dir_path_match(&the_index, entry, pathspec, prefix, seen)) - *dst++ = entry; - } - dir->nr = dst - dir->entries; - add_pathspec_matches_against_index(pathspec, &the_index, seen); - return seen; -} - -static void refresh(int verbose, const struct pathspec *pathspec) -{ - char *seen; - int i; - - seen = xcalloc(pathspec->nr, 1); - refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET, - pathspec, seen, _("Unstaged changes after refreshing the index:")); - for (i = 0; i < pathspec->nr; i++) { - if (!seen[i]) - die(_("pathspec '%s' did not match any files"), - pathspec->items[i].match); - } - free(seen); -} - -int run_add_interactive(const char *revision, const char *patch_mode, - const struct pathspec *pathspec) -{ - int status, i; - struct strvec argv = STRVEC_INIT; - int use_builtin_add_i = - git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1); - - if (use_builtin_add_i < 0) { - int experimental; - if (!git_config_get_bool("add.interactive.usebuiltin", - &use_builtin_add_i)) - ; /* ok */ - else if (!git_config_get_bool("feature.experimental", &experimental) && - experimental) - use_builtin_add_i = 1; - } - - if (use_builtin_add_i == 1) { - enum add_p_mode mode; - - if (!patch_mode) - return !!run_add_i(the_repository, pathspec); - - if (!strcmp(patch_mode, "--patch")) - mode = ADD_P_ADD; - else if (!strcmp(patch_mode, "--patch=stash")) - mode = ADD_P_STASH; - else if (!strcmp(patch_mode, "--patch=reset")) - mode = ADD_P_RESET; - else if (!strcmp(patch_mode, "--patch=checkout")) - mode = ADD_P_CHECKOUT; - else if (!strcmp(patch_mode, "--patch=worktree")) - mode = ADD_P_WORKTREE; - else - die("'%s' not supported", patch_mode); - - return !!run_add_p(the_repository, mode, revision, pathspec); - } - - strvec_push(&argv, "add--interactive"); - if (patch_mode) - strvec_push(&argv, patch_mode); - if (revision) - strvec_push(&argv, revision); - strvec_push(&argv, "--"); - for (i = 0; i < pathspec->nr; i++) - /* pass original pathspec, to be re-parsed */ - strvec_push(&argv, pathspec->items[i].original); - - status = run_command_v_opt(argv.v, RUN_GIT_CMD); - strvec_clear(&argv); - return status; -} - -int interactive_add(const char **argv, const char *prefix, int patch) -{ - struct pathspec pathspec; - - parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_FULL | - PATHSPEC_SYMLINK_LEADING_PATH | - PATHSPEC_PREFIX_ORIGIN, - prefix, argv); - - return run_add_interactive(NULL, - patch ? "--patch" : NULL, - &pathspec); -} - -static int edit_patch(int argc, const char **argv, const char *prefix) -{ - char *file = git_pathdup("ADD_EDIT.patch"); - const char *apply_argv[] = { "apply", "--recount", "--cached", - NULL, NULL }; - struct child_process child = CHILD_PROCESS_INIT; - struct rev_info rev; - int out; - struct stat st; - - apply_argv[3] = file; - - git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ - - if (read_cache() < 0) - die(_("Could not read the index")); - - repo_init_revisions(the_repository, &rev, prefix); - rev.diffopt.context = 7; - - argc = setup_revisions(argc, argv, &rev, NULL); - rev.diffopt.output_format = DIFF_FORMAT_PATCH; - rev.diffopt.use_color = 0; - rev.diffopt.flags.ignore_dirty_submodules = 1; - out = open(file, O_CREAT | O_WRONLY | O_TRUNC, 0666); - if (out < 0) - die(_("Could not open '%s' for writing."), file); - rev.diffopt.file = xfdopen(out, "w"); - rev.diffopt.close_file = 1; - if (run_diff_files(&rev, 0)) - die(_("Could not write patch")); - - if (launch_editor(file, NULL, NULL)) - die(_("editing patch failed")); - - if (stat(file, &st)) - die_errno(_("Could not stat '%s'"), file); - if (!st.st_size) - die(_("Empty patch. Aborted.")); - - child.git_cmd = 1; - child.argv = apply_argv; - if (run_command(&child)) - die(_("Could not apply '%s'"), file); - - unlink(file); - free(file); - return 0; -} - -static const char ignore_error[] = -N_("The following paths are ignored by one of your .gitignore files:\n"); - -static int verbose, show_only, ignored_too, refresh_only; -static int ignore_add_errors, intent_to_add, ignore_missing; -static int warn_on_embedded_repo = 1; - -#define ADDREMOVE_DEFAULT 1 -static int addremove = ADDREMOVE_DEFAULT; -static int addremove_explicit = -1; /* unspecified */ - -static char *chmod_arg; - -static int ignore_removal_cb(const struct option *opt, const char *arg, int unset) -{ - /* if we are told to ignore, we are not adding removals */ - *(int *)opt->value = !unset ? 0 : 1; - return 0; -} - -static struct option builtin_add_options[] = { - OPT__DRY_RUN(&show_only, N_("dry run")), - OPT__VERBOSE(&verbose, N_("be verbose")), - OPT_GROUP(""), - OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")), - OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")), - OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")), - OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0), - OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")), - OPT_BOOL(0, "renormalize", &add_renormalize, N_("renormalize EOL of tracked files (implies -u)")), - OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")), - OPT_BOOL('A', "all", &addremove_explicit, N_("add changes from all tracked and untracked files")), - OPT_CALLBACK_F(0, "ignore-removal", &addremove_explicit, - NULL /* takes no arguments */, - N_("ignore paths removed in the working tree (same as --no-all)"), - PARSE_OPT_NOARG, ignore_removal_cb), - OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")), - OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")), - OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")), - OPT_STRING(0, "chmod", &chmod_arg, "(+|-)x", - N_("override the executable bit of the listed files")), - OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo, - N_("warn when adding an embedded repository")), - OPT_HIDDEN_BOOL(0, "legacy-stash-p", &legacy_stash_p, - N_("backend for `git stash -p`")), - OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), - OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), - OPT_END(), -}; - -static int add_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "add.ignoreerrors") || - !strcmp(var, "add.ignore-errors")) { - ignore_add_errors = git_config_bool(var, value); - return 0; - } - - return git_default_config(var, value, cb); -} - -static const char embedded_advice[] = N_( -"You've added another git repository inside your current repository.\n" -"Clones of the outer repository will not contain the contents of\n" -"the embedded repository and will not know how to obtain it.\n" -"If you meant to add a submodule, use:\n" -"\n" -" git submodule add <url> %s\n" -"\n" -"If you added this path by mistake, you can remove it from the\n" -"index with:\n" -"\n" -" git rm --cached %s\n" -"\n" -"See \"git help submodule\" for more information." -); - -static void check_embedded_repo(const char *path) -{ - struct strbuf name = STRBUF_INIT; - - if (!warn_on_embedded_repo) - return; - if (!ends_with(path, "/")) - return; - - /* Drop trailing slash for aesthetics */ - strbuf_addstr(&name, path); - strbuf_strip_suffix(&name, "/"); - - warning(_("adding embedded git repository: %s"), name.buf); - if (advice_add_embedded_repo) { - advise(embedded_advice, name.buf, name.buf); - /* there may be multiple entries; advise only once */ - advice_add_embedded_repo = 0; - } - - strbuf_release(&name); -} - -static int add_files(struct dir_struct *dir, int flags) -{ - int i, exit_status = 0; - - if (dir->ignored_nr) { - fprintf(stderr, _(ignore_error)); - for (i = 0; i < dir->ignored_nr; i++) - fprintf(stderr, "%s\n", dir->ignored[i]->name); - if (advice_add_ignored_file) - advise(_("Use -f if you really want to add them.\n" - "Turn this message off by running\n" - "\"git config advice.addIgnoredFile false\"")); - exit_status = 1; - } - - for (i = 0; i < dir->nr; i++) { - if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) { - if (!ignore_add_errors) - die(_("adding files failed")); - exit_status = 1; - } else { - check_embedded_repo(dir->entries[i]->name); - } - } - return exit_status; -} - -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) { - if (pathspec_from_file) - die(_("--pathspec-from-file is incompatible with --interactive/--patch")); - exit(interactive_add(argv + 1, prefix, patch_interactive)); - } - if (legacy_stash_p) { - struct pathspec pathspec; - - parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_FULL | - PATHSPEC_SYMLINK_LEADING_PATH | - PATHSPEC_PREFIX_ORIGIN, - prefix, argv); - - return run_add_interactive(NULL, "--patch=stash", &pathspec); - } - - if (edit_interactive) { - if (pathspec_from_file) - die(_("--pathspec-from-file is incompatible with --edit")); - 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 (!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); - - /* - * 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 (pathspec_from_file) { - if (pathspec.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); - - parse_pathspec_file(&pathspec, PATHSPEC_ATTR, - PATHSPEC_PREFER_FULL | - PATHSPEC_SYMLINK_LEADING_PATH, - prefix, pathspec_from_file, pathspec_file_nul); - } else if (pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); - } - - if (require_pathspec && pathspec.nr == 0) { - fprintf(stderr, _("Nothing specified, nothing added.\n")); - if (advice_add_empty_pathspec) - advise( _("Maybe you wanted to say 'git add .'?\n" - "Turn this message off by running\n" - "\"git config advice.addEmptyPathspec false\"")); - return 0; - } - - if (!take_worktree_changes && addremove_explicit < 0 && pathspec.nr) - /* Turn "git add pathspec..." to "git add -A pathspec..." */ - addremove = 1; - - 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 (read_cache_preload(&pathspec) < 0) - die(_("index file corrupt")); - - die_in_unpopulated_submodule(&the_index, prefix); - die_path_inside_submodule(&the_index, &pathspec); - - dir_init(&dir); - if (add_new_files) { - int baselen; - - /* Set up the default git porcelain excludes */ - 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")); - - dir_clear(&dir); - UNLEAK(pathspec); - return exit_status; -} diff --git a/third_party/git/builtin/am.c b/third_party/git/builtin/am.c deleted file mode 100644 index 4949535a7f1f..000000000000 --- a/third_party/git/builtin/am.c +++ /dev/null @@ -1,2437 +0,0 @@ -/* - * Builtin "git am" - * - * Based on git-am.sh by Junio C Hamano. - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "builtin.h" -#include "exec-cmd.h" -#include "parse-options.h" -#include "dir.h" -#include "run-command.h" -#include "quote.h" -#include "tempfile.h" -#include "lockfile.h" -#include "cache-tree.h" -#include "refs.h" -#include "commit.h" -#include "diff.h" -#include "diffcore.h" -#include "unpack-trees.h" -#include "branch.h" -#include "sequencer.h" -#include "revision.h" -#include "merge-recursive.h" -#include "log-tree.h" -#include "notes-utils.h" -#include "rerere.h" -#include "prompt.h" -#include "mailinfo.h" -#include "apply.h" -#include "string-list.h" -#include "packfile.h" -#include "repository.h" - -/** - * Returns the length of the first line of msg. - */ -static int linelen(const char *msg) -{ - return strchrnul(msg, '\n') - msg; -} - -/** - * Returns true if `str` consists of only whitespace, false otherwise. - */ -static int str_isspace(const char *str) -{ - for (; *str; str++) - if (!isspace(*str)) - return 0; - - return 1; -} - -enum patch_format { - PATCH_FORMAT_UNKNOWN = 0, - PATCH_FORMAT_MBOX, - PATCH_FORMAT_STGIT, - PATCH_FORMAT_STGIT_SERIES, - PATCH_FORMAT_HG, - PATCH_FORMAT_MBOXRD -}; - -enum keep_type { - KEEP_FALSE = 0, - KEEP_TRUE, /* pass -k flag to git-mailinfo */ - KEEP_NON_PATCH /* pass -b flag to git-mailinfo */ -}; - -enum scissors_type { - SCISSORS_UNSET = -1, - SCISSORS_FALSE = 0, /* pass --no-scissors to git-mailinfo */ - SCISSORS_TRUE /* pass --scissors to git-mailinfo */ -}; - -enum signoff_type { - SIGNOFF_FALSE = 0, - SIGNOFF_TRUE = 1, - SIGNOFF_EXPLICIT /* --signoff was set on the command-line */ -}; - -enum show_patch_type { - SHOW_PATCH_RAW = 0, - SHOW_PATCH_DIFF = 1, -}; - -struct am_state { - /* state directory path */ - char *dir; - - /* current and last patch numbers, 1-indexed */ - int cur; - int last; - - /* commit metadata and message */ - char *author_name; - char *author_email; - char *author_date; - char *committer_name; - char *committer_email; - char *msg; - size_t msg_len; - - /* when --rebasing, records the original commit the patch came from */ - struct object_id orig_commit; - - /* number of digits in patch filename */ - int prec; - - /* various operating modes and command line options */ - int interactive; - int threeway; - int quiet; - int signoff; /* enum signoff_type */ - int utf8; - int keep; /* enum keep_type */ - int message_id; - int scissors; /* enum scissors_type */ - struct strvec git_apply_opts; - const char *resolvemsg; - int committer_date_is_author_date; - int ignore_date; - int allow_rerere_autoupdate; - const char *sign_commit; - int rebasing; -}; - -/** - * Initializes am_state with the default values. - */ -static void am_state_init(struct am_state *state) -{ - const char *committer; - struct ident_split id; - int gpgsign; - - memset(state, 0, sizeof(*state)); - - state->dir = git_pathdup("rebase-apply"); - - state->prec = 4; - - git_config_get_bool("am.threeway", &state->threeway); - - state->utf8 = 1; - - git_config_get_bool("am.messageid", &state->message_id); - - state->scissors = SCISSORS_UNSET; - - strvec_init(&state->git_apply_opts); - - if (!git_config_get_bool("commit.gpgsign", &gpgsign)) - state->sign_commit = gpgsign ? "" : NULL; - - committer = git_committer_info(IDENT_STRICT); - if (split_ident_line(&id, committer, strlen(committer)) < 0) - die(_("invalid committer: %s"), committer); - state->committer_name = - xmemdupz(id.name_begin, id.name_end - id.name_begin); - state->committer_email = - xmemdupz(id.mail_begin, id.mail_end - id.mail_begin); -} - -/** - * Releases memory allocated by an am_state. - */ -static void am_state_release(struct am_state *state) -{ - free(state->dir); - free(state->author_name); - free(state->author_email); - free(state->author_date); - free(state->committer_name); - free(state->committer_email); - free(state->msg); - strvec_clear(&state->git_apply_opts); -} - -/** - * Returns path relative to the am_state directory. - */ -static inline const char *am_path(const struct am_state *state, const char *path) -{ - return mkpath("%s/%s", state->dir, path); -} - -/** - * For convenience to call write_file() - */ -static void write_state_text(const struct am_state *state, - const char *name, const char *string) -{ - write_file(am_path(state, name), "%s", string); -} - -static void write_state_count(const struct am_state *state, - const char *name, int value) -{ - write_file(am_path(state, name), "%d", value); -} - -static void write_state_bool(const struct am_state *state, - const char *name, int value) -{ - write_state_text(state, name, value ? "t" : "f"); -} - -/** - * If state->quiet is false, calls fprintf(fp, fmt, ...), and appends a newline - * at the end. - */ -static void say(const struct am_state *state, FILE *fp, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - if (!state->quiet) { - vfprintf(fp, fmt, ap); - putc('\n', fp); - } - va_end(ap); -} - -/** - * Returns 1 if there is an am session in progress, 0 otherwise. - */ -static int am_in_progress(const struct am_state *state) -{ - struct stat st; - - if (lstat(state->dir, &st) < 0 || !S_ISDIR(st.st_mode)) - return 0; - if (lstat(am_path(state, "last"), &st) || !S_ISREG(st.st_mode)) - return 0; - if (lstat(am_path(state, "next"), &st) || !S_ISREG(st.st_mode)) - return 0; - return 1; -} - -/** - * Reads the contents of `file` in the `state` directory into `sb`. Returns the - * number of bytes read on success, -1 if the file does not exist. If `trim` is - * set, trailing whitespace will be removed. - */ -static int read_state_file(struct strbuf *sb, const struct am_state *state, - const char *file, int trim) -{ - strbuf_reset(sb); - - if (strbuf_read_file(sb, am_path(state, file), 0) >= 0) { - if (trim) - strbuf_trim(sb); - - return sb->len; - } - - if (errno == ENOENT) - return -1; - - die_errno(_("could not read '%s'"), am_path(state, file)); -} - -/** - * Reads and parses the state directory's "author-script" file, and sets - * state->author_name, state->author_email and state->author_date accordingly. - * Returns 0 on success, -1 if the file could not be parsed. - * - * The author script is of the format: - * - * GIT_AUTHOR_NAME='$author_name' - * GIT_AUTHOR_EMAIL='$author_email' - * GIT_AUTHOR_DATE='$author_date' - * - * where $author_name, $author_email and $author_date are quoted. We are strict - * with our parsing, as the file was meant to be eval'd in the old git-am.sh - * script, and thus if the file differs from what this function expects, it is - * better to bail out than to do something that the user does not expect. - */ -static int read_am_author_script(struct am_state *state) -{ - const char *filename = am_path(state, "author-script"); - - assert(!state->author_name); - assert(!state->author_email); - assert(!state->author_date); - - return read_author_script(filename, &state->author_name, - &state->author_email, &state->author_date, 1); -} - -/** - * Saves state->author_name, state->author_email and state->author_date in the - * state directory's "author-script" file. - */ -static void write_author_script(const struct am_state *state) -{ - struct strbuf sb = STRBUF_INIT; - - strbuf_addstr(&sb, "GIT_AUTHOR_NAME="); - sq_quote_buf(&sb, state->author_name); - strbuf_addch(&sb, '\n'); - - strbuf_addstr(&sb, "GIT_AUTHOR_EMAIL="); - sq_quote_buf(&sb, state->author_email); - strbuf_addch(&sb, '\n'); - - strbuf_addstr(&sb, "GIT_AUTHOR_DATE="); - sq_quote_buf(&sb, state->author_date); - strbuf_addch(&sb, '\n'); - - write_state_text(state, "author-script", sb.buf); - - strbuf_release(&sb); -} - -/** - * Reads the commit message from the state directory's "final-commit" file, - * setting state->msg to its contents and state->msg_len to the length of its - * contents in bytes. - * - * Returns 0 on success, -1 if the file does not exist. - */ -static int read_commit_msg(struct am_state *state) -{ - struct strbuf sb = STRBUF_INIT; - - assert(!state->msg); - - if (read_state_file(&sb, state, "final-commit", 0) < 0) { - strbuf_release(&sb); - return -1; - } - - state->msg = strbuf_detach(&sb, &state->msg_len); - return 0; -} - -/** - * Saves state->msg in the state directory's "final-commit" file. - */ -static void write_commit_msg(const struct am_state *state) -{ - const char *filename = am_path(state, "final-commit"); - write_file_buf(filename, state->msg, state->msg_len); -} - -/** - * Loads state from disk. - */ -static void am_load(struct am_state *state) -{ - struct strbuf sb = STRBUF_INIT; - - if (read_state_file(&sb, state, "next", 1) < 0) - BUG("state file 'next' does not exist"); - state->cur = strtol(sb.buf, NULL, 10); - - if (read_state_file(&sb, state, "last", 1) < 0) - BUG("state file 'last' does not exist"); - state->last = strtol(sb.buf, NULL, 10); - - if (read_am_author_script(state) < 0) - die(_("could not parse author script")); - - read_commit_msg(state); - - if (read_state_file(&sb, state, "original-commit", 1) < 0) - oidclr(&state->orig_commit); - else if (get_oid_hex(sb.buf, &state->orig_commit) < 0) - die(_("could not parse %s"), am_path(state, "original-commit")); - - read_state_file(&sb, state, "threeway", 1); - state->threeway = !strcmp(sb.buf, "t"); - - read_state_file(&sb, state, "quiet", 1); - state->quiet = !strcmp(sb.buf, "t"); - - read_state_file(&sb, state, "sign", 1); - state->signoff = !strcmp(sb.buf, "t"); - - read_state_file(&sb, state, "utf8", 1); - state->utf8 = !strcmp(sb.buf, "t"); - - if (file_exists(am_path(state, "rerere-autoupdate"))) { - read_state_file(&sb, state, "rerere-autoupdate", 1); - state->allow_rerere_autoupdate = strcmp(sb.buf, "t") ? - RERERE_NOAUTOUPDATE : RERERE_AUTOUPDATE; - } else { - state->allow_rerere_autoupdate = 0; - } - - read_state_file(&sb, state, "keep", 1); - if (!strcmp(sb.buf, "t")) - state->keep = KEEP_TRUE; - else if (!strcmp(sb.buf, "b")) - state->keep = KEEP_NON_PATCH; - else - state->keep = KEEP_FALSE; - - read_state_file(&sb, state, "messageid", 1); - state->message_id = !strcmp(sb.buf, "t"); - - read_state_file(&sb, state, "scissors", 1); - if (!strcmp(sb.buf, "t")) - state->scissors = SCISSORS_TRUE; - else if (!strcmp(sb.buf, "f")) - state->scissors = SCISSORS_FALSE; - else - state->scissors = SCISSORS_UNSET; - - read_state_file(&sb, state, "apply-opt", 1); - strvec_clear(&state->git_apply_opts); - if (sq_dequote_to_strvec(sb.buf, &state->git_apply_opts) < 0) - die(_("could not parse %s"), am_path(state, "apply-opt")); - - state->rebasing = !!file_exists(am_path(state, "rebasing")); - - strbuf_release(&sb); -} - -/** - * Removes the am_state directory, forcefully terminating the current am - * session. - */ -static void am_destroy(const struct am_state *state) -{ - struct strbuf sb = STRBUF_INIT; - - strbuf_addstr(&sb, state->dir); - remove_dir_recursively(&sb, 0); - strbuf_release(&sb); -} - -/** - * Runs applypatch-msg hook. Returns its exit code. - */ -static int run_applypatch_msg_hook(struct am_state *state) -{ - int ret; - - assert(state->msg); - ret = run_hook_le(NULL, "applypatch-msg", am_path(state, "final-commit"), NULL); - - if (!ret) { - FREE_AND_NULL(state->msg); - if (read_commit_msg(state) < 0) - die(_("'%s' was deleted by the applypatch-msg hook"), - am_path(state, "final-commit")); - } - - return ret; -} - -/** - * Runs post-rewrite hook. Returns it exit code. - */ -static int run_post_rewrite_hook(const struct am_state *state) -{ - struct child_process cp = CHILD_PROCESS_INIT; - const char *hook = find_hook("post-rewrite"); - int ret; - - if (!hook) - return 0; - - strvec_push(&cp.args, hook); - strvec_push(&cp.args, "rebase"); - - cp.in = xopen(am_path(state, "rewritten"), O_RDONLY); - cp.stdout_to_stderr = 1; - cp.trace2_hook_name = "post-rewrite"; - - ret = run_command(&cp); - - close(cp.in); - return ret; -} - -/** - * Reads the state directory's "rewritten" file, and copies notes from the old - * commits listed in the file to their rewritten commits. - * - * Returns 0 on success, -1 on failure. - */ -static int copy_notes_for_rebase(const struct am_state *state) -{ - struct notes_rewrite_cfg *c; - struct strbuf sb = STRBUF_INIT; - const char *invalid_line = _("Malformed input line: '%s'."); - const char *msg = "Notes added by 'git rebase'"; - FILE *fp; - int ret = 0; - - assert(state->rebasing); - - c = init_copy_notes_for_rewrite("rebase"); - if (!c) - return 0; - - fp = xfopen(am_path(state, "rewritten"), "r"); - - while (!strbuf_getline_lf(&sb, fp)) { - struct object_id from_obj, to_obj; - const char *p; - - if (sb.len != the_hash_algo->hexsz * 2 + 1) { - ret = error(invalid_line, sb.buf); - goto finish; - } - - if (parse_oid_hex(sb.buf, &from_obj, &p)) { - ret = error(invalid_line, sb.buf); - goto finish; - } - - if (*p != ' ') { - ret = error(invalid_line, sb.buf); - goto finish; - } - - if (get_oid_hex(p + 1, &to_obj)) { - ret = error(invalid_line, sb.buf); - goto finish; - } - - if (copy_note_for_rewrite(c, &from_obj, &to_obj)) - ret = error(_("Failed to copy notes from '%s' to '%s'"), - oid_to_hex(&from_obj), oid_to_hex(&to_obj)); - } - -finish: - finish_copy_notes_for_rewrite(the_repository, c, msg); - fclose(fp); - strbuf_release(&sb); - return ret; -} - -/** - * Determines if the file looks like a piece of RFC2822 mail by grabbing all - * non-indented lines and checking if they look like they begin with valid - * header field names. - * - * Returns 1 if the file looks like a piece of mail, 0 otherwise. - */ -static int is_mail(FILE *fp) -{ - const char *header_regex = "^[!-9;-~]+:"; - struct strbuf sb = STRBUF_INIT; - regex_t regex; - int ret = 1; - - if (fseek(fp, 0L, SEEK_SET)) - die_errno(_("fseek failed")); - - if (regcomp(®ex, header_regex, REG_NOSUB | REG_EXTENDED)) - die("invalid pattern: %s", header_regex); - - while (!strbuf_getline(&sb, fp)) { - if (!sb.len) - break; /* End of header */ - - /* Ignore indented folded lines */ - if (*sb.buf == '\t' || *sb.buf == ' ') - continue; - - /* It's a header if it matches header_regex */ - if (regexec(®ex, sb.buf, 0, NULL, 0)) { - ret = 0; - goto done; - } - } - -done: - regfree(®ex); - strbuf_release(&sb); - return ret; -} - -/** - * Attempts to detect the patch_format of the patches contained in `paths`, - * returning the PATCH_FORMAT_* enum value. Returns PATCH_FORMAT_UNKNOWN if - * detection fails. - */ -static int detect_patch_format(const char **paths) -{ - enum patch_format ret = PATCH_FORMAT_UNKNOWN; - struct strbuf l1 = STRBUF_INIT; - struct strbuf l2 = STRBUF_INIT; - struct strbuf l3 = STRBUF_INIT; - FILE *fp; - - /* - * We default to mbox format if input is from stdin and for directories - */ - if (!*paths || !strcmp(*paths, "-") || is_directory(*paths)) - return PATCH_FORMAT_MBOX; - - /* - * Otherwise, check the first few lines of the first patch, starting - * from the first non-blank line, to try to detect its format. - */ - - fp = xfopen(*paths, "r"); - - while (!strbuf_getline(&l1, fp)) { - if (l1.len) - break; - } - - if (starts_with(l1.buf, "From ") || starts_with(l1.buf, "From: ")) { - ret = PATCH_FORMAT_MBOX; - goto done; - } - - if (starts_with(l1.buf, "# This series applies on GIT commit")) { - ret = PATCH_FORMAT_STGIT_SERIES; - goto done; - } - - if (!strcmp(l1.buf, "# HG changeset patch")) { - ret = PATCH_FORMAT_HG; - goto done; - } - - strbuf_getline(&l2, fp); - strbuf_getline(&l3, fp); - - /* - * If the second line is empty and the third is a From, Author or Date - * entry, this is likely an StGit patch. - */ - if (l1.len && !l2.len && - (starts_with(l3.buf, "From:") || - starts_with(l3.buf, "Author:") || - starts_with(l3.buf, "Date:"))) { - ret = PATCH_FORMAT_STGIT; - goto done; - } - - if (l1.len && is_mail(fp)) { - ret = PATCH_FORMAT_MBOX; - goto done; - } - -done: - fclose(fp); - strbuf_release(&l1); - strbuf_release(&l2); - strbuf_release(&l3); - return ret; -} - -/** - * Splits out individual email patches from `paths`, where each path is either - * a mbox file or a Maildir. Returns 0 on success, -1 on failure. - */ -static int split_mail_mbox(struct am_state *state, const char **paths, - int keep_cr, int mboxrd) -{ - struct child_process cp = CHILD_PROCESS_INIT; - struct strbuf last = STRBUF_INIT; - int ret; - - cp.git_cmd = 1; - strvec_push(&cp.args, "mailsplit"); - strvec_pushf(&cp.args, "-d%d", state->prec); - strvec_pushf(&cp.args, "-o%s", state->dir); - strvec_push(&cp.args, "-b"); - if (keep_cr) - strvec_push(&cp.args, "--keep-cr"); - if (mboxrd) - strvec_push(&cp.args, "--mboxrd"); - strvec_push(&cp.args, "--"); - strvec_pushv(&cp.args, paths); - - ret = capture_command(&cp, &last, 8); - if (ret) - goto exit; - - state->cur = 1; - state->last = strtol(last.buf, NULL, 10); - -exit: - strbuf_release(&last); - return ret ? -1 : 0; -} - -/** - * Callback signature for split_mail_conv(). The foreign patch should be - * read from `in`, and the converted patch (in RFC2822 mail format) should be - * written to `out`. Return 0 on success, or -1 on failure. - */ -typedef int (*mail_conv_fn)(FILE *out, FILE *in, int keep_cr); - -/** - * Calls `fn` for each file in `paths` to convert the foreign patch to the - * RFC2822 mail format suitable for parsing with git-mailinfo. - * - * Returns 0 on success, -1 on failure. - */ -static int split_mail_conv(mail_conv_fn fn, struct am_state *state, - const char **paths, int keep_cr) -{ - static const char *stdin_only[] = {"-", NULL}; - int i; - - if (!*paths) - paths = stdin_only; - - for (i = 0; *paths; paths++, i++) { - FILE *in, *out; - const char *mail; - int ret; - - if (!strcmp(*paths, "-")) - in = stdin; - else - in = fopen(*paths, "r"); - - if (!in) - return error_errno(_("could not open '%s' for reading"), - *paths); - - mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1); - - out = fopen(mail, "w"); - if (!out) { - if (in != stdin) - fclose(in); - return error_errno(_("could not open '%s' for writing"), - mail); - } - - ret = fn(out, in, keep_cr); - - fclose(out); - if (in != stdin) - fclose(in); - - if (ret) - return error(_("could not parse patch '%s'"), *paths); - } - - state->cur = 1; - state->last = i; - return 0; -} - -/** - * A split_mail_conv() callback that converts an StGit patch to an RFC2822 - * message suitable for parsing with git-mailinfo. - */ -static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr) -{ - struct strbuf sb = STRBUF_INIT; - int subject_printed = 0; - - while (!strbuf_getline_lf(&sb, in)) { - const char *str; - - if (str_isspace(sb.buf)) - continue; - else if (skip_prefix(sb.buf, "Author:", &str)) - fprintf(out, "From:%s\n", str); - else if (starts_with(sb.buf, "From") || starts_with(sb.buf, "Date")) - fprintf(out, "%s\n", sb.buf); - else if (!subject_printed) { - fprintf(out, "Subject: %s\n", sb.buf); - subject_printed = 1; - } else { - fprintf(out, "\n%s\n", sb.buf); - break; - } - } - - strbuf_reset(&sb); - while (strbuf_fread(&sb, 8192, in) > 0) { - fwrite(sb.buf, 1, sb.len, out); - strbuf_reset(&sb); - } - - strbuf_release(&sb); - return 0; -} - -/** - * This function only supports a single StGit series file in `paths`. - * - * Given an StGit series file, converts the StGit patches in the series into - * RFC2822 messages suitable for parsing with git-mailinfo, and queues them in - * the state directory. - * - * Returns 0 on success, -1 on failure. - */ -static int split_mail_stgit_series(struct am_state *state, const char **paths, - int keep_cr) -{ - const char *series_dir; - char *series_dir_buf; - FILE *fp; - struct strvec patches = STRVEC_INIT; - struct strbuf sb = STRBUF_INIT; - int ret; - - if (!paths[0] || paths[1]) - return error(_("Only one StGIT patch series can be applied at once")); - - series_dir_buf = xstrdup(*paths); - series_dir = dirname(series_dir_buf); - - fp = fopen(*paths, "r"); - if (!fp) - return error_errno(_("could not open '%s' for reading"), *paths); - - while (!strbuf_getline_lf(&sb, fp)) { - if (*sb.buf == '#') - continue; /* skip comment lines */ - - strvec_push(&patches, mkpath("%s/%s", series_dir, sb.buf)); - } - - fclose(fp); - strbuf_release(&sb); - free(series_dir_buf); - - ret = split_mail_conv(stgit_patch_to_mail, state, patches.v, keep_cr); - - strvec_clear(&patches); - return ret; -} - -/** - * A split_patches_conv() callback that converts a mercurial patch to a RFC2822 - * message suitable for parsing with git-mailinfo. - */ -static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr) -{ - struct strbuf sb = STRBUF_INIT; - int rc = 0; - - while (!strbuf_getline_lf(&sb, in)) { - const char *str; - - if (skip_prefix(sb.buf, "# User ", &str)) - fprintf(out, "From: %s\n", str); - else if (skip_prefix(sb.buf, "# Date ", &str)) { - timestamp_t timestamp; - long tz, tz2; - char *end; - - errno = 0; - timestamp = parse_timestamp(str, &end, 10); - if (errno) { - rc = error(_("invalid timestamp")); - goto exit; - } - - if (!skip_prefix(end, " ", &str)) { - rc = error(_("invalid Date line")); - goto exit; - } - - errno = 0; - tz = strtol(str, &end, 10); - if (errno) { - rc = error(_("invalid timezone offset")); - goto exit; - } - - if (*end) { - rc = error(_("invalid Date line")); - goto exit; - } - - /* - * mercurial's timezone is in seconds west of UTC, - * however git's timezone is in hours + minutes east of - * UTC. Convert it. - */ - tz2 = labs(tz) / 3600 * 100 + labs(tz) % 3600 / 60; - if (tz > 0) - tz2 = -tz2; - - fprintf(out, "Date: %s\n", show_date(timestamp, tz2, DATE_MODE(RFC2822))); - } else if (starts_with(sb.buf, "# ")) { - continue; - } else { - fprintf(out, "\n%s\n", sb.buf); - break; - } - } - - strbuf_reset(&sb); - while (strbuf_fread(&sb, 8192, in) > 0) { - fwrite(sb.buf, 1, sb.len, out); - strbuf_reset(&sb); - } -exit: - strbuf_release(&sb); - return rc; -} - -/** - * Splits a list of files/directories into individual email patches. Each path - * in `paths` must be a file/directory that is formatted according to - * `patch_format`. - * - * Once split out, the individual email patches will be stored in the state - * directory, with each patch's filename being its index, padded to state->prec - * digits. - * - * state->cur will be set to the index of the first mail, and state->last will - * be set to the index of the last mail. - * - * Set keep_cr to 0 to convert all lines ending with \r\n to end with \n, 1 - * to disable this behavior, -1 to use the default configured setting. - * - * Returns 0 on success, -1 on failure. - */ -static int split_mail(struct am_state *state, enum patch_format patch_format, - const char **paths, int keep_cr) -{ - if (keep_cr < 0) { - keep_cr = 0; - git_config_get_bool("am.keepcr", &keep_cr); - } - - switch (patch_format) { - case PATCH_FORMAT_MBOX: - return split_mail_mbox(state, paths, keep_cr, 0); - case PATCH_FORMAT_STGIT: - return split_mail_conv(stgit_patch_to_mail, state, paths, keep_cr); - case PATCH_FORMAT_STGIT_SERIES: - return split_mail_stgit_series(state, paths, keep_cr); - case PATCH_FORMAT_HG: - return split_mail_conv(hg_patch_to_mail, state, paths, keep_cr); - case PATCH_FORMAT_MBOXRD: - return split_mail_mbox(state, paths, keep_cr, 1); - default: - BUG("invalid patch_format"); - } - return -1; -} - -/** - * Setup a new am session for applying patches - */ -static void am_setup(struct am_state *state, enum patch_format patch_format, - const char **paths, int keep_cr) -{ - struct object_id curr_head; - const char *str; - struct strbuf sb = STRBUF_INIT; - - if (!patch_format) - patch_format = detect_patch_format(paths); - - if (!patch_format) { - fprintf_ln(stderr, _("Patch format detection failed.")); - exit(128); - } - - if (mkdir(state->dir, 0777) < 0 && errno != EEXIST) - die_errno(_("failed to create directory '%s'"), state->dir); - delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); - - if (split_mail(state, patch_format, paths, keep_cr) < 0) { - am_destroy(state); - die(_("Failed to split patches.")); - } - - if (state->rebasing) - state->threeway = 1; - - write_state_bool(state, "threeway", state->threeway); - write_state_bool(state, "quiet", state->quiet); - write_state_bool(state, "sign", state->signoff); - write_state_bool(state, "utf8", state->utf8); - - if (state->allow_rerere_autoupdate) - write_state_bool(state, "rerere-autoupdate", - state->allow_rerere_autoupdate == RERERE_AUTOUPDATE); - - switch (state->keep) { - case KEEP_FALSE: - str = "f"; - break; - case KEEP_TRUE: - str = "t"; - break; - case KEEP_NON_PATCH: - str = "b"; - break; - default: - BUG("invalid value for state->keep"); - } - - write_state_text(state, "keep", str); - write_state_bool(state, "messageid", state->message_id); - - switch (state->scissors) { - case SCISSORS_UNSET: - str = ""; - break; - case SCISSORS_FALSE: - str = "f"; - break; - case SCISSORS_TRUE: - str = "t"; - break; - default: - BUG("invalid value for state->scissors"); - } - write_state_text(state, "scissors", str); - - sq_quote_argv(&sb, state->git_apply_opts.v); - write_state_text(state, "apply-opt", sb.buf); - - if (state->rebasing) - write_state_text(state, "rebasing", ""); - else - write_state_text(state, "applying", ""); - - if (!get_oid("HEAD", &curr_head)) { - write_state_text(state, "abort-safety", oid_to_hex(&curr_head)); - if (!state->rebasing) - update_ref("am", "ORIG_HEAD", &curr_head, NULL, 0, - UPDATE_REFS_DIE_ON_ERR); - } else { - write_state_text(state, "abort-safety", ""); - if (!state->rebasing) - delete_ref(NULL, "ORIG_HEAD", NULL, 0); - } - - /* - * NOTE: Since the "next" and "last" files determine if an am_state - * session is in progress, they should be written last. - */ - - write_state_count(state, "next", state->cur); - write_state_count(state, "last", state->last); - - strbuf_release(&sb); -} - -/** - * Increments the patch pointer, and cleans am_state for the application of the - * next patch. - */ -static void am_next(struct am_state *state) -{ - struct object_id head; - - FREE_AND_NULL(state->author_name); - FREE_AND_NULL(state->author_email); - FREE_AND_NULL(state->author_date); - FREE_AND_NULL(state->msg); - state->msg_len = 0; - - unlink(am_path(state, "author-script")); - unlink(am_path(state, "final-commit")); - - oidclr(&state->orig_commit); - unlink(am_path(state, "original-commit")); - delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); - - if (!get_oid("HEAD", &head)) - write_state_text(state, "abort-safety", oid_to_hex(&head)); - else - write_state_text(state, "abort-safety", ""); - - state->cur++; - write_state_count(state, "next", state->cur); -} - -/** - * Returns the filename of the current patch email. - */ -static const char *msgnum(const struct am_state *state) -{ - static struct strbuf sb = STRBUF_INIT; - - strbuf_reset(&sb); - strbuf_addf(&sb, "%0*d", state->prec, state->cur); - - return sb.buf; -} - -/** - * Dies with a user-friendly message on how to proceed after resolving the - * problem. This message can be overridden with state->resolvemsg. - */ -static void NORETURN die_user_resolve(const struct am_state *state) -{ - if (state->resolvemsg) { - printf_ln("%s", state->resolvemsg); - } else { - const char *cmdline = state->interactive ? "git am -i" : "git am"; - - printf_ln(_("When you have resolved this problem, run \"%s --continue\"."), cmdline); - printf_ln(_("If you prefer to skip this patch, run \"%s --skip\" instead."), cmdline); - printf_ln(_("To restore the original branch and stop patching, run \"%s --abort\"."), cmdline); - } - - exit(128); -} - -/** - * Appends signoff to the "msg" field of the am_state. - */ -static void am_append_signoff(struct am_state *state) -{ - struct strbuf sb = STRBUF_INIT; - - strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len); - append_signoff(&sb, 0, 0); - state->msg = strbuf_detach(&sb, &state->msg_len); -} - -/** - * Parses `mail` using git-mailinfo, extracting its patch and authorship info. - * state->msg will be set to the patch message. state->author_name, - * state->author_email and state->author_date will be set to the patch author's - * name, email and date respectively. The patch body will be written to the - * state directory's "patch" file. - * - * Returns 1 if the patch should be skipped, 0 otherwise. - */ -static int parse_mail(struct am_state *state, const char *mail) -{ - FILE *fp; - struct strbuf sb = STRBUF_INIT; - struct strbuf msg = STRBUF_INIT; - struct strbuf author_name = STRBUF_INIT; - struct strbuf author_date = STRBUF_INIT; - struct strbuf author_email = STRBUF_INIT; - int ret = 0; - struct mailinfo mi; - - setup_mailinfo(&mi); - - if (state->utf8) - mi.metainfo_charset = get_commit_output_encoding(); - else - mi.metainfo_charset = NULL; - - switch (state->keep) { - case KEEP_FALSE: - break; - case KEEP_TRUE: - mi.keep_subject = 1; - break; - case KEEP_NON_PATCH: - mi.keep_non_patch_brackets_in_subject = 1; - break; - default: - BUG("invalid value for state->keep"); - } - - if (state->message_id) - mi.add_message_id = 1; - - switch (state->scissors) { - case SCISSORS_UNSET: - break; - case SCISSORS_FALSE: - mi.use_scissors = 0; - break; - case SCISSORS_TRUE: - mi.use_scissors = 1; - break; - default: - BUG("invalid value for state->scissors"); - } - - mi.input = xfopen(mail, "r"); - mi.output = xfopen(am_path(state, "info"), "w"); - if (mailinfo(&mi, am_path(state, "msg"), am_path(state, "patch"))) - die("could not parse patch"); - - fclose(mi.input); - fclose(mi.output); - - if (mi.format_flowed) - warning(_("Patch sent with format=flowed; " - "space at the end of lines might be lost.")); - - /* Extract message and author information */ - fp = xfopen(am_path(state, "info"), "r"); - while (!strbuf_getline_lf(&sb, fp)) { - const char *x; - - if (skip_prefix(sb.buf, "Subject: ", &x)) { - if (msg.len) - strbuf_addch(&msg, '\n'); - strbuf_addstr(&msg, x); - } else if (skip_prefix(sb.buf, "Author: ", &x)) - strbuf_addstr(&author_name, x); - else if (skip_prefix(sb.buf, "Email: ", &x)) - strbuf_addstr(&author_email, x); - else if (skip_prefix(sb.buf, "Date: ", &x)) - strbuf_addstr(&author_date, x); - } - fclose(fp); - - /* Skip pine's internal folder data */ - if (!strcmp(author_name.buf, "Mail System Internal Data")) { - ret = 1; - goto finish; - } - - if (is_empty_or_missing_file(am_path(state, "patch"))) { - printf_ln(_("Patch is empty.")); - die_user_resolve(state); - } - - strbuf_addstr(&msg, "\n\n"); - strbuf_addbuf(&msg, &mi.log_message); - strbuf_stripspace(&msg, 0); - - assert(!state->author_name); - state->author_name = strbuf_detach(&author_name, NULL); - - assert(!state->author_email); - state->author_email = strbuf_detach(&author_email, NULL); - - assert(!state->author_date); - state->author_date = strbuf_detach(&author_date, NULL); - - assert(!state->msg); - state->msg = strbuf_detach(&msg, &state->msg_len); - -finish: - strbuf_release(&msg); - strbuf_release(&author_date); - strbuf_release(&author_email); - strbuf_release(&author_name); - strbuf_release(&sb); - clear_mailinfo(&mi); - return ret; -} - -/** - * Sets commit_id to the commit hash where the mail was generated from. - * Returns 0 on success, -1 on failure. - */ -static int get_mail_commit_oid(struct object_id *commit_id, const char *mail) -{ - struct strbuf sb = STRBUF_INIT; - FILE *fp = xfopen(mail, "r"); - const char *x; - int ret = 0; - - if (strbuf_getline_lf(&sb, fp) || - !skip_prefix(sb.buf, "From ", &x) || - get_oid_hex(x, commit_id) < 0) - ret = -1; - - strbuf_release(&sb); - fclose(fp); - return ret; -} - -/** - * Sets state->msg, state->author_name, state->author_email, state->author_date - * to the commit's respective info. - */ -static void get_commit_info(struct am_state *state, struct commit *commit) -{ - const char *buffer, *ident_line, *msg; - size_t ident_len; - struct ident_split id; - - buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding()); - - ident_line = find_commit_header(buffer, "author", &ident_len); - if (!ident_line) - die(_("missing author line in commit %s"), - oid_to_hex(&commit->object.oid)); - if (split_ident_line(&id, ident_line, ident_len) < 0) - die(_("invalid ident line: %.*s"), (int)ident_len, ident_line); - - assert(!state->author_name); - if (id.name_begin) - state->author_name = - xmemdupz(id.name_begin, id.name_end - id.name_begin); - else - state->author_name = xstrdup(""); - - assert(!state->author_email); - if (id.mail_begin) - state->author_email = - xmemdupz(id.mail_begin, id.mail_end - id.mail_begin); - else - state->author_email = xstrdup(""); - - assert(!state->author_date); - state->author_date = xstrdup(show_ident_date(&id, DATE_MODE(NORMAL))); - - assert(!state->msg); - msg = strstr(buffer, "\n\n"); - if (!msg) - die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid)); - state->msg = xstrdup(msg + 2); - state->msg_len = strlen(state->msg); - unuse_commit_buffer(commit, buffer); -} - -/** - * Writes `commit` as a patch to the state directory's "patch" file. - */ -static void write_commit_patch(const struct am_state *state, struct commit *commit) -{ - struct rev_info rev_info; - FILE *fp; - - fp = xfopen(am_path(state, "patch"), "w"); - repo_init_revisions(the_repository, &rev_info, NULL); - rev_info.diff = 1; - rev_info.abbrev = 0; - rev_info.disable_stdin = 1; - rev_info.show_root_diff = 1; - rev_info.diffopt.output_format = DIFF_FORMAT_PATCH; - rev_info.no_commit_id = 1; - rev_info.diffopt.flags.binary = 1; - rev_info.diffopt.flags.full_index = 1; - rev_info.diffopt.use_color = 0; - rev_info.diffopt.file = fp; - rev_info.diffopt.close_file = 1; - add_pending_object(&rev_info, &commit->object, ""); - diff_setup_done(&rev_info.diffopt); - log_tree_commit(&rev_info, commit); -} - -/** - * Writes the diff of the index against HEAD as a patch to the state - * directory's "patch" file. - */ -static void write_index_patch(const struct am_state *state) -{ - struct tree *tree; - struct object_id head; - struct rev_info rev_info; - FILE *fp; - - if (!get_oid("HEAD", &head)) { - struct commit *commit = lookup_commit_or_die(&head, "HEAD"); - tree = get_commit_tree(commit); - } else - tree = lookup_tree(the_repository, - the_repository->hash_algo->empty_tree); - - fp = xfopen(am_path(state, "patch"), "w"); - repo_init_revisions(the_repository, &rev_info, NULL); - rev_info.diff = 1; - rev_info.disable_stdin = 1; - rev_info.no_commit_id = 1; - rev_info.diffopt.output_format = DIFF_FORMAT_PATCH; - rev_info.diffopt.use_color = 0; - rev_info.diffopt.file = fp; - rev_info.diffopt.close_file = 1; - add_pending_object(&rev_info, &tree->object, ""); - diff_setup_done(&rev_info.diffopt); - run_diff_index(&rev_info, 1); -} - -/** - * Like parse_mail(), but parses the mail by looking up its commit ID - * directly. This is used in --rebasing mode to bypass git-mailinfo's munging - * of patches. - * - * state->orig_commit will be set to the original commit ID. - * - * Will always return 0 as the patch should never be skipped. - */ -static int parse_mail_rebase(struct am_state *state, const char *mail) -{ - struct commit *commit; - struct object_id commit_oid; - - if (get_mail_commit_oid(&commit_oid, mail) < 0) - die(_("could not parse %s"), mail); - - commit = lookup_commit_or_die(&commit_oid, mail); - - get_commit_info(state, commit); - - write_commit_patch(state, commit); - - oidcpy(&state->orig_commit, &commit_oid); - write_state_text(state, "original-commit", oid_to_hex(&commit_oid)); - update_ref("am", "REBASE_HEAD", &commit_oid, - NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR); - - return 0; -} - -/** - * Applies current patch with git-apply. Returns 0 on success, -1 otherwise. If - * `index_file` is not NULL, the patch will be applied to that index. - */ -static int run_apply(const struct am_state *state, const char *index_file) -{ - struct strvec apply_paths = STRVEC_INIT; - struct strvec apply_opts = STRVEC_INIT; - struct apply_state apply_state; - int res, opts_left; - int force_apply = 0; - int options = 0; - - if (init_apply_state(&apply_state, the_repository, NULL)) - BUG("init_apply_state() failed"); - - strvec_push(&apply_opts, "apply"); - strvec_pushv(&apply_opts, state->git_apply_opts.v); - - opts_left = apply_parse_options(apply_opts.nr, apply_opts.v, - &apply_state, &force_apply, &options, - NULL); - - if (opts_left != 0) - die("unknown option passed through to git apply"); - - if (index_file) { - apply_state.index_file = index_file; - apply_state.cached = 1; - } else - apply_state.check_index = 1; - - /* - * If we are allowed to fall back on 3-way merge, don't give false - * errors during the initial attempt. - */ - if (state->threeway && !index_file) - apply_state.apply_verbosity = verbosity_silent; - - if (check_apply_state(&apply_state, force_apply)) - BUG("check_apply_state() failed"); - - strvec_push(&apply_paths, am_path(state, "patch")); - - res = apply_all_patches(&apply_state, apply_paths.nr, apply_paths.v, options); - - strvec_clear(&apply_paths); - strvec_clear(&apply_opts); - clear_apply_state(&apply_state); - - if (res) - return res; - - if (index_file) { - /* Reload index as apply_all_patches() will have modified it. */ - discard_cache(); - read_cache_from(index_file); - } - - return 0; -} - -/** - * Builds an index that contains just the blobs needed for a 3way merge. - */ -static int build_fake_ancestor(const struct am_state *state, const char *index_file) -{ - struct child_process cp = CHILD_PROCESS_INIT; - - cp.git_cmd = 1; - strvec_push(&cp.args, "apply"); - strvec_pushv(&cp.args, state->git_apply_opts.v); - strvec_pushf(&cp.args, "--build-fake-ancestor=%s", index_file); - strvec_push(&cp.args, am_path(state, "patch")); - - if (run_command(&cp)) - return -1; - - return 0; -} - -/** - * Attempt a threeway merge, using index_path as the temporary index. - */ -static int fall_back_threeway(const struct am_state *state, const char *index_path) -{ - struct object_id orig_tree, their_tree, our_tree; - const struct object_id *bases[1] = { &orig_tree }; - struct merge_options o; - struct commit *result; - char *their_tree_name; - - if (get_oid("HEAD", &our_tree) < 0) - oidcpy(&our_tree, the_hash_algo->empty_tree); - - if (build_fake_ancestor(state, index_path)) - return error("could not build fake ancestor"); - - discard_cache(); - read_cache_from(index_path); - - if (write_index_as_tree(&orig_tree, &the_index, index_path, 0, NULL)) - return error(_("Repository lacks necessary blobs to fall back on 3-way merge.")); - - say(state, stdout, _("Using index info to reconstruct a base tree...")); - - if (!state->quiet) { - /* - * List paths that needed 3-way fallback, so that the user can - * review them with extra care to spot mismerges. - */ - struct rev_info rev_info; - - repo_init_revisions(the_repository, &rev_info, NULL); - rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS; - rev_info.diffopt.filter |= diff_filter_bit('A'); - rev_info.diffopt.filter |= diff_filter_bit('M'); - add_pending_oid(&rev_info, "HEAD", &our_tree, 0); - diff_setup_done(&rev_info.diffopt); - run_diff_index(&rev_info, 1); - } - - if (run_apply(state, index_path)) - return error(_("Did you hand edit your patch?\n" - "It does not apply to blobs recorded in its index.")); - - if (write_index_as_tree(&their_tree, &the_index, index_path, 0, NULL)) - return error("could not write tree"); - - say(state, stdout, _("Falling back to patching base and 3-way merge...")); - - discard_cache(); - read_cache(); - - /* - * This is not so wrong. Depending on which base we picked, orig_tree - * may be wildly different from ours, but their_tree has the same set of - * wildly different changes in parts the patch did not touch, so - * recursive ends up canceling them, saying that we reverted all those - * changes. - */ - - init_merge_options(&o, the_repository); - - o.branch1 = "HEAD"; - their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg); - o.branch2 = their_tree_name; - o.detect_directory_renames = MERGE_DIRECTORY_RENAMES_NONE; - - if (state->quiet) - o.verbosity = 0; - - if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) { - repo_rerere(the_repository, state->allow_rerere_autoupdate); - free(their_tree_name); - return error(_("Failed to merge in the changes.")); - } - - free(their_tree_name); - return 0; -} - -/** - * Commits the current index with state->msg as the commit message and - * state->author_name, state->author_email and state->author_date as the author - * information. - */ -static void do_commit(const struct am_state *state) -{ - struct object_id tree, parent, commit; - const struct object_id *old_oid; - struct commit_list *parents = NULL; - const char *reflog_msg, *author, *committer = NULL; - struct strbuf sb = STRBUF_INIT; - - if (run_hook_le(NULL, "pre-applypatch", NULL)) - exit(1); - - if (write_cache_as_tree(&tree, 0, NULL)) - die(_("git write-tree failed to write a tree")); - - if (!get_oid_commit("HEAD", &parent)) { - old_oid = &parent; - commit_list_insert(lookup_commit(the_repository, &parent), - &parents); - } else { - old_oid = NULL; - say(state, stderr, _("applying to an empty history")); - } - - author = fmt_ident(state->author_name, state->author_email, - WANT_AUTHOR_IDENT, - state->ignore_date ? NULL : state->author_date, - IDENT_STRICT); - - if (state->committer_date_is_author_date) - committer = fmt_ident(state->committer_name, - state->committer_email, WANT_COMMITTER_IDENT, - state->ignore_date ? NULL - : state->author_date, - IDENT_STRICT); - - if (commit_tree_extended(state->msg, state->msg_len, &tree, parents, - &commit, author, committer, state->sign_commit, - NULL)) - die(_("failed to write commit object")); - - reflog_msg = getenv("GIT_REFLOG_ACTION"); - if (!reflog_msg) - reflog_msg = "am"; - - strbuf_addf(&sb, "%s: %.*s", reflog_msg, linelen(state->msg), - state->msg); - - update_ref(sb.buf, "HEAD", &commit, old_oid, 0, - UPDATE_REFS_DIE_ON_ERR); - - if (state->rebasing) { - FILE *fp = xfopen(am_path(state, "rewritten"), "a"); - - assert(!is_null_oid(&state->orig_commit)); - fprintf(fp, "%s ", oid_to_hex(&state->orig_commit)); - fprintf(fp, "%s\n", oid_to_hex(&commit)); - fclose(fp); - } - - run_hook_le(NULL, "post-applypatch", NULL); - - strbuf_release(&sb); -} - -/** - * Validates the am_state for resuming -- the "msg" and authorship fields must - * be filled up. - */ -static void validate_resume_state(const struct am_state *state) -{ - if (!state->msg) - die(_("cannot resume: %s does not exist."), - am_path(state, "final-commit")); - - if (!state->author_name || !state->author_email || !state->author_date) - die(_("cannot resume: %s does not exist."), - am_path(state, "author-script")); -} - -/** - * Interactively prompt the user on whether the current patch should be - * applied. - * - * Returns 0 if the user chooses to apply the patch, 1 if the user chooses to - * skip it. - */ -static int do_interactive(struct am_state *state) -{ - assert(state->msg); - - for (;;) { - char reply[64]; - - puts(_("Commit Body is:")); - puts("--------------------------"); - printf("%s", state->msg); - puts("--------------------------"); - - /* - * TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a] - * in your translation. The program will only accept English - * input at this point. - */ - printf(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: ")); - if (!fgets(reply, sizeof(reply), stdin)) - die("unable to read from stdin; aborting"); - - if (*reply == 'y' || *reply == 'Y') { - return 0; - } else if (*reply == 'a' || *reply == 'A') { - state->interactive = 0; - return 0; - } else if (*reply == 'n' || *reply == 'N') { - return 1; - } else if (*reply == 'e' || *reply == 'E') { - struct strbuf msg = STRBUF_INIT; - - if (!launch_editor(am_path(state, "final-commit"), &msg, NULL)) { - free(state->msg); - state->msg = strbuf_detach(&msg, &state->msg_len); - } - strbuf_release(&msg); - } else if (*reply == 'v' || *reply == 'V') { - const char *pager = git_pager(1); - struct child_process cp = CHILD_PROCESS_INIT; - - if (!pager) - pager = "cat"; - prepare_pager_args(&cp, pager); - strvec_push(&cp.args, am_path(state, "patch")); - run_command(&cp); - } - } -} - -/** - * Applies all queued mail. - * - * If `resume` is true, we are "resuming". The "msg" and authorship fields, as - * well as the state directory's "patch" file is used as-is for applying the - * patch and committing it. - */ -static void am_run(struct am_state *state, int resume) -{ - struct strbuf sb = STRBUF_INIT; - - unlink(am_path(state, "dirtyindex")); - - if (refresh_and_write_cache(REFRESH_QUIET, 0, 0) < 0) - die(_("unable to write index file")); - - if (repo_index_has_changes(the_repository, NULL, &sb)) { - write_state_bool(state, "dirtyindex", 1); - die(_("Dirty index: cannot apply patches (dirty: %s)"), sb.buf); - } - - strbuf_release(&sb); - - while (state->cur <= state->last) { - const char *mail = am_path(state, msgnum(state)); - int apply_status; - - reset_ident_date(); - - if (!file_exists(mail)) - goto next; - - if (resume) { - validate_resume_state(state); - } else { - int skip; - - if (state->rebasing) - skip = parse_mail_rebase(state, mail); - else - skip = parse_mail(state, mail); - - if (skip) - goto next; /* mail should be skipped */ - - if (state->signoff) - am_append_signoff(state); - - write_author_script(state); - write_commit_msg(state); - } - - if (state->interactive && do_interactive(state)) - goto next; - - if (run_applypatch_msg_hook(state)) - exit(1); - - say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg); - - apply_status = run_apply(state, NULL); - - if (apply_status && state->threeway) { - struct strbuf sb = STRBUF_INIT; - - strbuf_addstr(&sb, am_path(state, "patch-merge-index")); - apply_status = fall_back_threeway(state, sb.buf); - strbuf_release(&sb); - - /* - * Applying the patch to an earlier tree and merging - * the result may have produced the same tree as ours. - */ - if (!apply_status && - !repo_index_has_changes(the_repository, NULL, NULL)) { - say(state, stdout, _("No changes -- Patch already applied.")); - goto next; - } - } - - if (apply_status) { - printf_ln(_("Patch failed at %s %.*s"), msgnum(state), - linelen(state->msg), state->msg); - - if (advice_amworkdir) - advise(_("Use 'git am --show-current-patch=diff' to see the failed patch")); - - die_user_resolve(state); - } - - do_commit(state); - -next: - am_next(state); - - if (resume) - am_load(state); - resume = 0; - } - - if (!is_empty_or_missing_file(am_path(state, "rewritten"))) { - assert(state->rebasing); - copy_notes_for_rebase(state); - run_post_rewrite_hook(state); - } - - /* - * In rebasing mode, it's up to the caller to take care of - * housekeeping. - */ - if (!state->rebasing) { - am_destroy(state); - close_object_store(the_repository->objects); - run_auto_maintenance(state->quiet); - } -} - -/** - * Resume the current am session after patch application failure. The user did - * all the hard work, and we do not have to do any patch application. Just - * trust and commit what the user has in the index and working tree. - */ -static void am_resolve(struct am_state *state) -{ - validate_resume_state(state); - - say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg); - - if (!repo_index_has_changes(the_repository, NULL, NULL)) { - printf_ln(_("No changes - did you forget to use 'git add'?\n" - "If there is nothing left to stage, chances are that something else\n" - "already introduced the same changes; you might want to skip this patch.")); - die_user_resolve(state); - } - - if (unmerged_cache()) { - printf_ln(_("You still have unmerged paths in your index.\n" - "You should 'git add' each file with resolved conflicts to mark them as such.\n" - "You might run `git rm` on a file to accept \"deleted by them\" for it.")); - die_user_resolve(state); - } - - if (state->interactive) { - write_index_patch(state); - if (do_interactive(state)) - goto next; - } - - repo_rerere(the_repository, 0); - - do_commit(state); - -next: - am_next(state); - am_load(state); - am_run(state, 0); -} - -/** - * Performs a checkout fast-forward from `head` to `remote`. If `reset` is - * true, any unmerged entries will be discarded. Returns 0 on success, -1 on - * failure. - */ -static int fast_forward_to(struct tree *head, struct tree *remote, int reset) -{ - struct lock_file lock_file = LOCK_INIT; - struct unpack_trees_options opts; - struct tree_desc t[2]; - - if (parse_tree(head) || parse_tree(remote)) - return -1; - - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); - - refresh_cache(REFRESH_QUIET); - - memset(&opts, 0, sizeof(opts)); - opts.head_idx = 1; - opts.src_index = &the_index; - opts.dst_index = &the_index; - opts.update = 1; - opts.merge = 1; - opts.reset = reset; - opts.fn = twoway_merge; - init_tree_desc(&t[0], head->buffer, head->size); - init_tree_desc(&t[1], remote->buffer, remote->size); - - if (unpack_trees(2, t, &opts)) { - rollback_lock_file(&lock_file); - return -1; - } - - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) - die(_("unable to write new index file")); - - return 0; -} - -/** - * Merges a tree into the index. The index's stat info will take precedence - * over the merged tree's. Returns 0 on success, -1 on failure. - */ -static int merge_tree(struct tree *tree) -{ - struct lock_file lock_file = LOCK_INIT; - struct unpack_trees_options opts; - struct tree_desc t[1]; - - if (parse_tree(tree)) - return -1; - - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); - - memset(&opts, 0, sizeof(opts)); - opts.head_idx = 1; - opts.src_index = &the_index; - opts.dst_index = &the_index; - opts.merge = 1; - opts.fn = oneway_merge; - init_tree_desc(&t[0], tree->buffer, tree->size); - - if (unpack_trees(1, t, &opts)) { - rollback_lock_file(&lock_file); - return -1; - } - - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) - die(_("unable to write new index file")); - - return 0; -} - -/** - * Clean the index without touching entries that are not modified between - * `head` and `remote`. - */ -static int clean_index(const struct object_id *head, const struct object_id *remote) -{ - struct tree *head_tree, *remote_tree, *index_tree; - struct object_id index; - - head_tree = parse_tree_indirect(head); - if (!head_tree) - return error(_("Could not parse object '%s'."), oid_to_hex(head)); - - remote_tree = parse_tree_indirect(remote); - if (!remote_tree) - return error(_("Could not parse object '%s'."), oid_to_hex(remote)); - - read_cache_unmerged(); - - if (fast_forward_to(head_tree, head_tree, 1)) - return -1; - - if (write_cache_as_tree(&index, 0, NULL)) - return -1; - - index_tree = parse_tree_indirect(&index); - if (!index_tree) - return error(_("Could not parse object '%s'."), oid_to_hex(&index)); - - if (fast_forward_to(index_tree, remote_tree, 0)) - return -1; - - if (merge_tree(remote_tree)) - return -1; - - remove_branch_state(the_repository, 0); - - return 0; -} - -/** - * Resets rerere's merge resolution metadata. - */ -static void am_rerere_clear(void) -{ - struct string_list merge_rr = STRING_LIST_INIT_DUP; - rerere_clear(the_repository, &merge_rr); - string_list_clear(&merge_rr, 1); -} - -/** - * Resume the current am session by skipping the current patch. - */ -static void am_skip(struct am_state *state) -{ - struct object_id head; - - am_rerere_clear(); - - if (get_oid("HEAD", &head)) - oidcpy(&head, the_hash_algo->empty_tree); - - if (clean_index(&head, &head)) - die(_("failed to clean index")); - - if (state->rebasing) { - FILE *fp = xfopen(am_path(state, "rewritten"), "a"); - - assert(!is_null_oid(&state->orig_commit)); - fprintf(fp, "%s ", oid_to_hex(&state->orig_commit)); - fprintf(fp, "%s\n", oid_to_hex(&head)); - fclose(fp); - } - - am_next(state); - am_load(state); - am_run(state, 0); -} - -/** - * Returns true if it is safe to reset HEAD to the ORIG_HEAD, false otherwise. - * - * It is not safe to reset HEAD when: - * 1. git-am previously failed because the index was dirty. - * 2. HEAD has moved since git-am previously failed. - */ -static int safe_to_abort(const struct am_state *state) -{ - struct strbuf sb = STRBUF_INIT; - struct object_id abort_safety, head; - - if (file_exists(am_path(state, "dirtyindex"))) - return 0; - - if (read_state_file(&sb, state, "abort-safety", 1) > 0) { - if (get_oid_hex(sb.buf, &abort_safety)) - die(_("could not parse %s"), am_path(state, "abort-safety")); - } else - oidclr(&abort_safety); - strbuf_release(&sb); - - if (get_oid("HEAD", &head)) - oidclr(&head); - - if (oideq(&head, &abort_safety)) - return 1; - - warning(_("You seem to have moved HEAD since the last 'am' failure.\n" - "Not rewinding to ORIG_HEAD")); - - return 0; -} - -/** - * Aborts the current am session if it is safe to do so. - */ -static void am_abort(struct am_state *state) -{ - struct object_id curr_head, orig_head; - int has_curr_head, has_orig_head; - char *curr_branch; - - if (!safe_to_abort(state)) { - am_destroy(state); - return; - } - - am_rerere_clear(); - - curr_branch = resolve_refdup("HEAD", 0, &curr_head, NULL); - has_curr_head = curr_branch && !is_null_oid(&curr_head); - if (!has_curr_head) - oidcpy(&curr_head, the_hash_algo->empty_tree); - - has_orig_head = !get_oid("ORIG_HEAD", &orig_head); - if (!has_orig_head) - oidcpy(&orig_head, the_hash_algo->empty_tree); - - clean_index(&curr_head, &orig_head); - - if (has_orig_head) - update_ref("am --abort", "HEAD", &orig_head, - has_curr_head ? &curr_head : NULL, 0, - UPDATE_REFS_DIE_ON_ERR); - else if (curr_branch) - delete_ref(NULL, curr_branch, NULL, REF_NO_DEREF); - - free(curr_branch); - am_destroy(state); -} - -static int show_patch(struct am_state *state, enum show_patch_type sub_mode) -{ - struct strbuf sb = STRBUF_INIT; - const char *patch_path; - int len; - - if (!is_null_oid(&state->orig_commit)) { - const char *av[4] = { "show", NULL, "--", NULL }; - char *new_oid_str; - int ret; - - av[1] = new_oid_str = xstrdup(oid_to_hex(&state->orig_commit)); - ret = run_command_v_opt(av, RUN_GIT_CMD); - free(new_oid_str); - return ret; - } - - switch (sub_mode) { - case SHOW_PATCH_RAW: - patch_path = am_path(state, msgnum(state)); - break; - case SHOW_PATCH_DIFF: - patch_path = am_path(state, "patch"); - break; - default: - BUG("invalid mode for --show-current-patch"); - } - - len = strbuf_read_file(&sb, patch_path, 0); - if (len < 0) - die_errno(_("failed to read '%s'"), patch_path); - - setup_pager(); - write_in_full(1, sb.buf, sb.len); - strbuf_release(&sb); - return 0; -} - -/** - * parse_options() callback that validates and sets opt->value to the - * PATCH_FORMAT_* enum value corresponding to `arg`. - */ -static int parse_opt_patchformat(const struct option *opt, const char *arg, int unset) -{ - int *opt_value = opt->value; - - if (unset) - *opt_value = PATCH_FORMAT_UNKNOWN; - else if (!strcmp(arg, "mbox")) - *opt_value = PATCH_FORMAT_MBOX; - else if (!strcmp(arg, "stgit")) - *opt_value = PATCH_FORMAT_STGIT; - else if (!strcmp(arg, "stgit-series")) - *opt_value = PATCH_FORMAT_STGIT_SERIES; - else if (!strcmp(arg, "hg")) - *opt_value = PATCH_FORMAT_HG; - else if (!strcmp(arg, "mboxrd")) - *opt_value = PATCH_FORMAT_MBOXRD; - /* - * Please update $__git_patchformat in git-completion.bash - * when you add new options - */ - else - return error(_("Invalid value for --patch-format: %s"), arg); - return 0; -} - -enum resume_type { - RESUME_FALSE = 0, - RESUME_APPLY, - RESUME_RESOLVED, - RESUME_SKIP, - RESUME_ABORT, - RESUME_QUIT, - RESUME_SHOW_PATCH -}; - -struct resume_mode { - enum resume_type mode; - enum show_patch_type sub_mode; -}; - -static int parse_opt_show_current_patch(const struct option *opt, const char *arg, int unset) -{ - int *opt_value = opt->value; - struct resume_mode *resume = container_of(opt_value, struct resume_mode, mode); - - /* - * Please update $__git_showcurrentpatch in git-completion.bash - * when you add new options - */ - const char *valid_modes[] = { - [SHOW_PATCH_DIFF] = "diff", - [SHOW_PATCH_RAW] = "raw" - }; - int new_value = SHOW_PATCH_RAW; - - BUG_ON_OPT_NEG(unset); - - if (arg) { - for (new_value = 0; new_value < ARRAY_SIZE(valid_modes); new_value++) { - if (!strcmp(arg, valid_modes[new_value])) - break; - } - if (new_value >= ARRAY_SIZE(valid_modes)) - return error(_("Invalid value for --show-current-patch: %s"), arg); - } - - if (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode) - return error(_("--show-current-patch=%s is incompatible with " - "--show-current-patch=%s"), - arg, valid_modes[resume->sub_mode]); - - resume->mode = RESUME_SHOW_PATCH; - resume->sub_mode = new_value; - return 0; -} - -static int git_am_config(const char *k, const char *v, void *cb) -{ - int status; - - status = git_gpg_config(k, v, NULL); - if (status) - return status; - - return git_default_config(k, v, NULL); -} - -int cmd_am(int argc, const char **argv, const char *prefix) -{ - struct am_state state; - int binary = -1; - int keep_cr = -1; - int patch_format = PATCH_FORMAT_UNKNOWN; - struct resume_mode resume = { .mode = RESUME_FALSE }; - int in_progress; - int ret = 0; - - const char * const usage[] = { - N_("git am [<options>] [(<mbox> | <Maildir>)...]"), - N_("git am [<options>] (--continue | --skip | --abort)"), - NULL - }; - - struct option options[] = { - OPT_BOOL('i', "interactive", &state.interactive, - N_("run interactively")), - OPT_HIDDEN_BOOL('b', "binary", &binary, - N_("historical option -- no-op")), - OPT_BOOL('3', "3way", &state.threeway, - N_("allow fall back on 3way merging if needed")), - OPT__QUIET(&state.quiet, N_("be quiet")), - OPT_SET_INT('s', "signoff", &state.signoff, - N_("add a Signed-off-by line to the commit message"), - SIGNOFF_EXPLICIT), - OPT_BOOL('u', "utf8", &state.utf8, - N_("recode into utf8 (default)")), - OPT_SET_INT('k', "keep", &state.keep, - N_("pass -k flag to git-mailinfo"), KEEP_TRUE), - OPT_SET_INT(0, "keep-non-patch", &state.keep, - N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH), - OPT_BOOL('m', "message-id", &state.message_id, - N_("pass -m flag to git-mailinfo")), - OPT_SET_INT_F(0, "keep-cr", &keep_cr, - N_("pass --keep-cr flag to git-mailsplit for mbox format"), - 1, PARSE_OPT_NONEG), - OPT_SET_INT_F(0, "no-keep-cr", &keep_cr, - N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"), - 0, PARSE_OPT_NONEG), - OPT_BOOL('c', "scissors", &state.scissors, - N_("strip everything before a scissors line")), - OPT_PASSTHRU_ARGV(0, "whitespace", &state.git_apply_opts, N_("action"), - N_("pass it through git-apply"), - 0), - OPT_PASSTHRU_ARGV(0, "ignore-space-change", &state.git_apply_opts, NULL, - N_("pass it through git-apply"), - PARSE_OPT_NOARG), - OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &state.git_apply_opts, NULL, - N_("pass it through git-apply"), - PARSE_OPT_NOARG), - OPT_PASSTHRU_ARGV(0, "directory", &state.git_apply_opts, N_("root"), - N_("pass it through git-apply"), - 0), - OPT_PASSTHRU_ARGV(0, "exclude", &state.git_apply_opts, N_("path"), - N_("pass it through git-apply"), - 0), - OPT_PASSTHRU_ARGV(0, "include", &state.git_apply_opts, N_("path"), - N_("pass it through git-apply"), - 0), - OPT_PASSTHRU_ARGV('C', NULL, &state.git_apply_opts, N_("n"), - N_("pass it through git-apply"), - 0), - OPT_PASSTHRU_ARGV('p', NULL, &state.git_apply_opts, N_("num"), - N_("pass it through git-apply"), - 0), - OPT_CALLBACK(0, "patch-format", &patch_format, N_("format"), - N_("format the patch(es) are in"), - parse_opt_patchformat), - OPT_PASSTHRU_ARGV(0, "reject", &state.git_apply_opts, NULL, - N_("pass it through git-apply"), - PARSE_OPT_NOARG), - OPT_STRING(0, "resolvemsg", &state.resolvemsg, NULL, - N_("override error message when patch failure occurs")), - OPT_CMDMODE(0, "continue", &resume.mode, - N_("continue applying patches after resolving a conflict"), - RESUME_RESOLVED), - OPT_CMDMODE('r', "resolved", &resume.mode, - N_("synonyms for --continue"), - RESUME_RESOLVED), - OPT_CMDMODE(0, "skip", &resume.mode, - N_("skip the current patch"), - RESUME_SKIP), - OPT_CMDMODE(0, "abort", &resume.mode, - N_("restore the original branch and abort the patching operation."), - RESUME_ABORT), - OPT_CMDMODE(0, "quit", &resume.mode, - N_("abort the patching operation but keep HEAD where it is."), - RESUME_QUIT), - { OPTION_CALLBACK, 0, "show-current-patch", &resume.mode, - "(diff|raw)", - N_("show the patch being applied"), - PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, - parse_opt_show_current_patch, RESUME_SHOW_PATCH }, - OPT_BOOL(0, "committer-date-is-author-date", - &state.committer_date_is_author_date, - N_("lie about committer date")), - OPT_BOOL(0, "ignore-date", &state.ignore_date, - N_("use current timestamp for author date")), - OPT_RERERE_AUTOUPDATE(&state.allow_rerere_autoupdate), - { OPTION_STRING, 'S', "gpg-sign", &state.sign_commit, N_("key-id"), - N_("GPG-sign commits"), - PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - OPT_HIDDEN_BOOL(0, "rebasing", &state.rebasing, - N_("(internal use for git-rebase)")), - OPT_END() - }; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage_with_options(usage, options); - - git_config(git_am_config, NULL); - - am_state_init(&state); - - in_progress = am_in_progress(&state); - if (in_progress) - am_load(&state); - - argc = parse_options(argc, argv, prefix, options, usage, 0); - - if (binary >= 0) - fprintf_ln(stderr, _("The -b/--binary option has been a no-op for long time, and\n" - "it will be removed. Please do not use it anymore.")); - - /* Ensure a valid committer ident can be constructed */ - git_committer_info(IDENT_STRICT); - - if (repo_read_index_preload(the_repository, NULL, 0) < 0) - die(_("failed to read the index")); - - if (in_progress) { - /* - * Catch user error to feed us patches when there is a session - * in progress: - * - * 1. mbox path(s) are provided on the command-line. - * 2. stdin is not a tty: the user is trying to feed us a patch - * from standard input. This is somewhat unreliable -- stdin - * could be /dev/null for example and the caller did not - * intend to feed us a patch but wanted to continue - * unattended. - */ - if (argc || (resume.mode == RESUME_FALSE && !isatty(0))) - die(_("previous rebase directory %s still exists but mbox given."), - state.dir); - - if (resume.mode == RESUME_FALSE) - resume.mode = RESUME_APPLY; - - if (state.signoff == SIGNOFF_EXPLICIT) - am_append_signoff(&state); - } else { - struct strvec paths = STRVEC_INIT; - int i; - - /* - * Handle stray state directory in the independent-run case. In - * the --rebasing case, it is up to the caller to take care of - * stray directories. - */ - if (file_exists(state.dir) && !state.rebasing) { - if (resume.mode == RESUME_ABORT || resume.mode == RESUME_QUIT) { - am_destroy(&state); - am_state_release(&state); - return 0; - } - - die(_("Stray %s directory found.\n" - "Use \"git am --abort\" to remove it."), - state.dir); - } - - if (resume.mode) - die(_("Resolve operation not in progress, we are not resuming.")); - - for (i = 0; i < argc; i++) { - if (is_absolute_path(argv[i]) || !prefix) - strvec_push(&paths, argv[i]); - else - strvec_push(&paths, mkpath("%s/%s", prefix, argv[i])); - } - - if (state.interactive && !paths.nr) - die(_("interactive mode requires patches on the command line")); - - am_setup(&state, patch_format, paths.v, keep_cr); - - strvec_clear(&paths); - } - - switch (resume.mode) { - case RESUME_FALSE: - am_run(&state, 0); - break; - case RESUME_APPLY: - am_run(&state, 1); - break; - case RESUME_RESOLVED: - am_resolve(&state); - break; - case RESUME_SKIP: - am_skip(&state); - break; - case RESUME_ABORT: - am_abort(&state); - break; - case RESUME_QUIT: - am_rerere_clear(); - am_destroy(&state); - break; - case RESUME_SHOW_PATCH: - ret = show_patch(&state, resume.sub_mode); - break; - default: - BUG("invalid resume value"); - } - - am_state_release(&state); - - return ret; -} diff --git a/third_party/git/builtin/annotate.c b/third_party/git/builtin/annotate.c deleted file mode 100644 index 58ff977a2314..000000000000 --- a/third_party/git/builtin/annotate.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * "git annotate" builtin alias - * - * Copyright (C) 2006 Ryan Anderson - */ -#include "git-compat-util.h" -#include "builtin.h" -#include "strvec.h" - -int cmd_annotate(int argc, const char **argv, const char *prefix) -{ - struct strvec args = STRVEC_INIT; - int i; - - strvec_pushl(&args, "annotate", "-c", NULL); - - for (i = 1; i < argc; i++) { - strvec_push(&args, argv[i]); - } - - return cmd_blame(args.nr, args.v, prefix); -} diff --git a/third_party/git/builtin/apply.c b/third_party/git/builtin/apply.c deleted file mode 100644 index 3f099b960565..000000000000 --- a/third_party/git/builtin/apply.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "cache.h" -#include "builtin.h" -#include "parse-options.h" -#include "lockfile.h" -#include "apply.h" - -static const char * const apply_usage[] = { - N_("git apply [<options>] [<patch>...]"), - NULL -}; - -int cmd_apply(int argc, const char **argv, const char *prefix) -{ - int force_apply = 0; - int options = 0; - int ret; - struct apply_state state; - - if (init_apply_state(&state, the_repository, prefix)) - exit(128); - - argc = apply_parse_options(argc, argv, - &state, &force_apply, &options, - apply_usage); - - if (check_apply_state(&state, force_apply)) - exit(128); - - ret = apply_all_patches(&state, argc, argv, options); - - clear_apply_state(&state); - - return ret; -} diff --git a/third_party/git/builtin/archive.c b/third_party/git/builtin/archive.c deleted file mode 100644 index 45d11669aae4..000000000000 --- a/third_party/git/builtin/archive.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2006 Franck Bui-Huu - * Copyright (c) 2006 Rene Scharfe - */ -#include "cache.h" -#include "builtin.h" -#include "archive.h" -#include "transport.h" -#include "parse-options.h" -#include "pkt-line.h" -#include "sideband.h" - -static void create_output_file(const char *output_file) -{ - int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666); - if (output_fd < 0) - die_errno(_("could not create archive file '%s'"), output_file); - if (output_fd != 1) { - if (dup2(output_fd, 1) < 0) - die_errno(_("could not redirect output")); - else - close(output_fd); - } -} - -static int run_remote_archiver(int argc, const char **argv, - const char *remote, const char *exec, - const char *name_hint) -{ - int fd[2], i, rv; - struct transport *transport; - struct remote *_remote; - struct packet_reader reader; - - _remote = remote_get(remote); - if (!_remote->url[0]) - die(_("git archive: Remote with no URL")); - transport = transport_get(_remote, _remote->url[0]); - transport_connect(transport, "git-upload-archive", exec, fd); - - /* - * Inject a fake --format field at the beginning of the - * arguments, with the format inferred from our output - * filename. This way explicit --format options can override - * it. - */ - if (name_hint) { - const char *format = archive_format_from_filename(name_hint); - if (format) - packet_write_fmt(fd[1], "argument --format=%s\n", format); - } - for (i = 1; i < argc; i++) - packet_write_fmt(fd[1], "argument %s\n", argv[i]); - packet_flush(fd[1]); - - packet_reader_init(&reader, fd[0], NULL, 0, - PACKET_READ_CHOMP_NEWLINE | - PACKET_READ_DIE_ON_ERR_PACKET); - - if (packet_reader_read(&reader) != PACKET_READ_NORMAL) - die(_("git archive: expected ACK/NAK, got a flush packet")); - if (strcmp(reader.line, "ACK")) { - if (starts_with(reader.line, "NACK ")) - die(_("git archive: NACK %s"), reader.line + 5); - die(_("git archive: protocol error")); - } - - if (packet_reader_read(&reader) != PACKET_READ_FLUSH) - die(_("git archive: expected a flush")); - - /* Now, start reading from fd[0] and spit it out to stdout */ - rv = recv_sideband("archive", fd[0], 1); - rv |= transport_disconnect(transport); - - return !!rv; -} - -#define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | \ - PARSE_OPT_KEEP_ARGV0 | \ - PARSE_OPT_KEEP_UNKNOWN | \ - PARSE_OPT_NO_INTERNAL_HELP ) - -int cmd_archive(int argc, const char **argv, const char *prefix) -{ - const char *exec = "git-upload-archive"; - const char *output = NULL; - const char *remote = NULL; - struct option local_opts[] = { - OPT_FILENAME('o', "output", &output, - N_("write the archive to this file")), - OPT_STRING(0, "remote", &remote, N_("repo"), - N_("retrieve the archive from remote repository <repo>")), - OPT_STRING(0, "exec", &exec, N_("command"), - N_("path to the remote git-upload-archive command")), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, local_opts, NULL, - PARSE_OPT_KEEP_ALL); - - init_archivers(); - - if (output) - create_output_file(output); - - if (remote) - return run_remote_archiver(argc, argv, remote, exec, output); - - setvbuf(stderr, NULL, _IOLBF, BUFSIZ); - - return write_archive(argc, argv, prefix, the_repository, output, 0); -} diff --git a/third_party/git/builtin/bisect--helper.c b/third_party/git/builtin/bisect--helper.c deleted file mode 100644 index 7512b880f0d6..000000000000 --- a/third_party/git/builtin/bisect--helper.c +++ /dev/null @@ -1,981 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "parse-options.h" -#include "bisect.h" -#include "refs.h" -#include "dir.h" -#include "strvec.h" -#include "run-command.h" -#include "prompt.h" -#include "quote.h" -#include "revision.h" - -static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS") -static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV") -static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK") -static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START") -static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG") -static GIT_PATH_FUNC(git_path_head_name, "head-name") -static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES") -static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT") - -static const char * const git_bisect_helper_usage[] = { - N_("git bisect--helper --next-all"), - N_("git bisect--helper --write-terms <bad_term> <good_term>"), - N_("git bisect--helper --bisect-clean-state"), - N_("git bisect--helper --bisect-reset [<commit>]"), - N_("git bisect--helper --bisect-write [--no-log] <state> <revision> <good_term> <bad_term>"), - N_("git bisect--helper --bisect-check-and-set-terms <command> <good_term> <bad_term>"), - N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"), - N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"), - N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]" - " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"), - N_("git bisect--helper --bisect-next"), - N_("git bisect--helper --bisect-auto-next"), - N_("git bisect--helper --bisect-autostart"), - NULL -}; - -struct add_bisect_ref_data { - struct rev_info *revs; - unsigned int object_flags; -}; - -struct bisect_terms { - char *term_good; - char *term_bad; -}; - -static void free_terms(struct bisect_terms *terms) -{ - FREE_AND_NULL(terms->term_good); - FREE_AND_NULL(terms->term_bad); -} - -static void set_terms(struct bisect_terms *terms, const char *bad, - const char *good) -{ - free((void *)terms->term_good); - terms->term_good = xstrdup(good); - free((void *)terms->term_bad); - terms->term_bad = xstrdup(bad); -} - -static const char vocab_bad[] = "bad|new"; -static const char vocab_good[] = "good|old"; - -static int bisect_autostart(struct bisect_terms *terms); - -/* - * Check whether the string `term` belongs to the set of strings - * included in the variable arguments. - */ -LAST_ARG_MUST_BE_NULL -static int one_of(const char *term, ...) -{ - int res = 0; - va_list matches; - const char *match; - - va_start(matches, term); - while (!res && (match = va_arg(matches, const char *))) - res = !strcmp(term, match); - va_end(matches); - - return res; -} - -static int write_in_file(const char *path, const char *mode, const char *format, va_list args) -{ - FILE *fp = NULL; - int res = 0; - - if (strcmp(mode, "w") && strcmp(mode, "a")) - BUG("write-in-file does not support '%s' mode", mode); - fp = fopen(path, mode); - if (!fp) - return error_errno(_("cannot open file '%s' in mode '%s'"), path, mode); - res = vfprintf(fp, format, args); - - if (res < 0) { - int saved_errno = errno; - fclose(fp); - errno = saved_errno; - return error_errno(_("could not write to file '%s'"), path); - } - - return fclose(fp); -} - -static int write_to_file(const char *path, const char *format, ...) -{ - int res; - va_list args; - - va_start(args, format); - res = write_in_file(path, "w", format, args); - va_end(args); - - return res; -} - -static int append_to_file(const char *path, const char *format, ...) -{ - int res; - va_list args; - - va_start(args, format); - res = write_in_file(path, "a", format, args); - va_end(args); - - return res; -} - -static int check_term_format(const char *term, const char *orig_term) -{ - int res; - char *new_term = xstrfmt("refs/bisect/%s", term); - - res = check_refname_format(new_term, 0); - free(new_term); - - if (res) - return error(_("'%s' is not a valid term"), term); - - if (one_of(term, "help", "start", "skip", "next", "reset", - "visualize", "view", "replay", "log", "run", "terms", NULL)) - return error(_("can't use the builtin command '%s' as a term"), term); - - /* - * In theory, nothing prevents swapping completely good and bad, - * but this situation could be confusing and hasn't been tested - * enough. Forbid it for now. - */ - - if ((strcmp(orig_term, "bad") && one_of(term, "bad", "new", NULL)) || - (strcmp(orig_term, "good") && one_of(term, "good", "old", NULL))) - return error(_("can't change the meaning of the term '%s'"), term); - - return 0; -} - -static int write_terms(const char *bad, const char *good) -{ - int res; - - if (!strcmp(bad, good)) - return error(_("please use two different terms")); - - if (check_term_format(bad, "bad") || check_term_format(good, "good")) - return -1; - - res = write_to_file(git_path_bisect_terms(), "%s\n%s\n", bad, good); - - return res; -} - -static int is_expected_rev(const char *expected_hex) -{ - struct strbuf actual_hex = STRBUF_INIT; - int res = 0; - if (strbuf_read_file(&actual_hex, git_path_bisect_expected_rev(), 0) >= 40) { - strbuf_trim(&actual_hex); - res = !strcmp(actual_hex.buf, expected_hex); - } - strbuf_release(&actual_hex); - return res; -} - -static void check_expected_revs(const char **revs, int rev_nr) -{ - int i; - - for (i = 0; i < rev_nr; i++) { - if (!is_expected_rev(revs[i])) { - unlink_or_warn(git_path_bisect_ancestors_ok()); - unlink_or_warn(git_path_bisect_expected_rev()); - } - } -} - -static int bisect_reset(const char *commit) -{ - struct strbuf branch = STRBUF_INIT; - - if (!commit) { - if (strbuf_read_file(&branch, git_path_bisect_start(), 0) < 1) { - printf(_("We are not bisecting.\n")); - return 0; - } - strbuf_rtrim(&branch); - } else { - struct object_id oid; - - if (get_oid_commit(commit, &oid)) - return error(_("'%s' is not a valid commit"), commit); - strbuf_addstr(&branch, commit); - } - - if (!ref_exists("BISECT_HEAD")) { - struct strvec argv = STRVEC_INIT; - - strvec_pushl(&argv, "checkout", branch.buf, "--", NULL); - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) { - error(_("could not check out original" - " HEAD '%s'. Try 'git bisect" - " reset <commit>'."), branch.buf); - strbuf_release(&branch); - strvec_clear(&argv); - return -1; - } - strvec_clear(&argv); - } - - strbuf_release(&branch); - return bisect_clean_state(); -} - -static void log_commit(FILE *fp, char *fmt, const char *state, - struct commit *commit) -{ - struct pretty_print_context pp = {0}; - struct strbuf commit_msg = STRBUF_INIT; - char *label = xstrfmt(fmt, state); - - format_commit_message(commit, "%s", &commit_msg, &pp); - - fprintf(fp, "# %s: [%s] %s\n", label, oid_to_hex(&commit->object.oid), - commit_msg.buf); - - strbuf_release(&commit_msg); - free(label); -} - -static int bisect_write(const char *state, const char *rev, - const struct bisect_terms *terms, int nolog) -{ - struct strbuf tag = STRBUF_INIT; - struct object_id oid; - struct commit *commit; - FILE *fp = NULL; - int res = 0; - - if (!strcmp(state, terms->term_bad)) { - strbuf_addf(&tag, "refs/bisect/%s", state); - } else if (one_of(state, terms->term_good, "skip", NULL)) { - strbuf_addf(&tag, "refs/bisect/%s-%s", state, rev); - } else { - res = error(_("Bad bisect_write argument: %s"), state); - goto finish; - } - - if (get_oid(rev, &oid)) { - res = error(_("couldn't get the oid of the rev '%s'"), rev); - goto finish; - } - - if (update_ref(NULL, tag.buf, &oid, NULL, 0, - UPDATE_REFS_MSG_ON_ERR)) { - res = -1; - goto finish; - } - - fp = fopen(git_path_bisect_log(), "a"); - if (!fp) { - res = error_errno(_("couldn't open the file '%s'"), git_path_bisect_log()); - goto finish; - } - - commit = lookup_commit_reference(the_repository, &oid); - log_commit(fp, "%s", state, commit); - - if (!nolog) - fprintf(fp, "git bisect %s %s\n", state, rev); - -finish: - if (fp) - fclose(fp); - strbuf_release(&tag); - return res; -} - -static int check_and_set_terms(struct bisect_terms *terms, const char *cmd) -{ - int has_term_file = !is_empty_or_missing_file(git_path_bisect_terms()); - - if (one_of(cmd, "skip", "start", "terms", NULL)) - return 0; - - if (has_term_file && strcmp(cmd, terms->term_bad) && - strcmp(cmd, terms->term_good)) - return error(_("Invalid command: you're currently in a " - "%s/%s bisect"), terms->term_bad, - terms->term_good); - - if (!has_term_file) { - if (one_of(cmd, "bad", "good", NULL)) { - set_terms(terms, "bad", "good"); - return write_terms(terms->term_bad, terms->term_good); - } - if (one_of(cmd, "new", "old", NULL)) { - set_terms(terms, "new", "old"); - return write_terms(terms->term_bad, terms->term_good); - } - } - - return 0; -} - -static int mark_good(const char *refname, const struct object_id *oid, - int flag, void *cb_data) -{ - int *m_good = (int *)cb_data; - *m_good = 0; - return 1; -} - -static const char need_bad_and_good_revision_warning[] = - N_("You need to give me at least one %s and %s revision.\n" - "You can use \"git bisect %s\" and \"git bisect %s\" for that."); - -static const char need_bisect_start_warning[] = - N_("You need to start by \"git bisect start\".\n" - "You then need to give me at least one %s and %s revision.\n" - "You can use \"git bisect %s\" and \"git bisect %s\" for that."); - -static int decide_next(const struct bisect_terms *terms, - const char *current_term, int missing_good, - int missing_bad) -{ - if (!missing_good && !missing_bad) - return 0; - if (!current_term) - return -1; - - if (missing_good && !missing_bad && - !strcmp(current_term, terms->term_good)) { - char *yesno; - /* - * have bad (or new) but not good (or old). We could bisect - * although this is less optimum. - */ - warning(_("bisecting only with a %s commit"), terms->term_bad); - if (!isatty(0)) - return 0; - /* - * TRANSLATORS: Make sure to include [Y] and [n] in your - * translation. The program will only accept English input - * at this point. - */ - yesno = git_prompt(_("Are you sure [Y/n]? "), PROMPT_ECHO); - if (starts_with(yesno, "N") || starts_with(yesno, "n")) - return -1; - return 0; - } - - if (!is_empty_or_missing_file(git_path_bisect_start())) - return error(_(need_bad_and_good_revision_warning), - vocab_bad, vocab_good, vocab_bad, vocab_good); - else - return error(_(need_bisect_start_warning), - vocab_good, vocab_bad, vocab_good, vocab_bad); -} - -static int bisect_next_check(const struct bisect_terms *terms, - const char *current_term) -{ - int missing_good = 1, missing_bad = 1; - char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad); - char *good_glob = xstrfmt("%s-*", terms->term_good); - - if (ref_exists(bad_ref)) - missing_bad = 0; - - for_each_glob_ref_in(mark_good, good_glob, "refs/bisect/", - (void *) &missing_good); - - free(good_glob); - free(bad_ref); - - return decide_next(terms, current_term, missing_good, missing_bad); -} - -static int get_terms(struct bisect_terms *terms) -{ - struct strbuf str = STRBUF_INIT; - FILE *fp = NULL; - int res = 0; - - fp = fopen(git_path_bisect_terms(), "r"); - if (!fp) { - res = -1; - goto finish; - } - - free_terms(terms); - strbuf_getline_lf(&str, fp); - terms->term_bad = strbuf_detach(&str, NULL); - strbuf_getline_lf(&str, fp); - terms->term_good = strbuf_detach(&str, NULL); - -finish: - if (fp) - fclose(fp); - strbuf_release(&str); - return res; -} - -static int bisect_terms(struct bisect_terms *terms, const char *option) -{ - if (get_terms(terms)) - return error(_("no terms defined")); - - if (option == NULL) { - printf(_("Your current terms are %s for the old state\n" - "and %s for the new state.\n"), - terms->term_good, terms->term_bad); - return 0; - } - if (one_of(option, "--term-good", "--term-old", NULL)) - printf("%s\n", terms->term_good); - else if (one_of(option, "--term-bad", "--term-new", NULL)) - printf("%s\n", terms->term_bad); - else - return error(_("invalid argument %s for 'git bisect terms'.\n" - "Supported options are: " - "--term-good|--term-old and " - "--term-bad|--term-new."), option); - - return 0; -} - -static int bisect_append_log_quoted(const char **argv) -{ - int res = 0; - FILE *fp = fopen(git_path_bisect_log(), "a"); - struct strbuf orig_args = STRBUF_INIT; - - if (!fp) - return -1; - - if (fprintf(fp, "git bisect start") < 1) { - res = -1; - goto finish; - } - - sq_quote_argv(&orig_args, argv); - if (fprintf(fp, "%s\n", orig_args.buf) < 1) - res = -1; - -finish: - fclose(fp); - strbuf_release(&orig_args); - return res; -} - -static int add_bisect_ref(const char *refname, const struct object_id *oid, - int flags, void *cb) -{ - struct add_bisect_ref_data *data = cb; - - add_pending_oid(data->revs, refname, oid, data->object_flags); - - return 0; -} - -static int prepare_revs(struct bisect_terms *terms, struct rev_info *revs) -{ - int res = 0; - struct add_bisect_ref_data cb = { revs }; - char *good = xstrfmt("%s-*", terms->term_good); - - /* - * We cannot use terms->term_bad directly in - * for_each_glob_ref_in() and we have to append a '*' to it, - * otherwise for_each_glob_ref_in() will append '/' and '*'. - */ - char *bad = xstrfmt("%s*", terms->term_bad); - - /* - * It is important to reset the flags used by revision walks - * as the previous call to bisect_next_all() in turn - * sets up a revision walk. - */ - reset_revision_walk(); - init_revisions(revs, NULL); - setup_revisions(0, NULL, revs, NULL); - for_each_glob_ref_in(add_bisect_ref, bad, "refs/bisect/", &cb); - cb.object_flags = UNINTERESTING; - for_each_glob_ref_in(add_bisect_ref, good, "refs/bisect/", &cb); - if (prepare_revision_walk(revs)) - res = error(_("revision walk setup failed\n")); - - free(good); - free(bad); - return res; -} - -static int bisect_skipped_commits(struct bisect_terms *terms) -{ - int res; - FILE *fp = NULL; - struct rev_info revs; - struct commit *commit; - struct pretty_print_context pp = {0}; - struct strbuf commit_name = STRBUF_INIT; - - res = prepare_revs(terms, &revs); - if (res) - return res; - - fp = fopen(git_path_bisect_log(), "a"); - if (!fp) - return error_errno(_("could not open '%s' for appending"), - git_path_bisect_log()); - - if (fprintf(fp, "# only skipped commits left to test\n") < 0) - return error_errno(_("failed to write to '%s'"), git_path_bisect_log()); - - while ((commit = get_revision(&revs)) != NULL) { - strbuf_reset(&commit_name); - format_commit_message(commit, "%s", - &commit_name, &pp); - fprintf(fp, "# possible first %s commit: [%s] %s\n", - terms->term_bad, oid_to_hex(&commit->object.oid), - commit_name.buf); - } - - /* - * Reset the flags used by revision walks in case - * there is another revision walk after this one. - */ - reset_revision_walk(); - - strbuf_release(&commit_name); - fclose(fp); - return 0; -} - -static int bisect_successful(struct bisect_terms *terms) -{ - struct object_id oid; - struct commit *commit; - struct pretty_print_context pp = {0}; - struct strbuf commit_name = STRBUF_INIT; - char *bad_ref = xstrfmt("refs/bisect/%s",terms->term_bad); - int res; - - read_ref(bad_ref, &oid); - commit = lookup_commit_reference_by_name(bad_ref); - format_commit_message(commit, "%s", &commit_name, &pp); - - res = append_to_file(git_path_bisect_log(), "# first %s commit: [%s] %s\n", - terms->term_bad, oid_to_hex(&commit->object.oid), - commit_name.buf); - - strbuf_release(&commit_name); - free(bad_ref); - return res; -} - -static enum bisect_error bisect_next(struct bisect_terms *terms, const char *prefix) -{ - enum bisect_error res; - - if (bisect_autostart(terms)) - return BISECT_FAILED; - - if (bisect_next_check(terms, terms->term_good)) - return BISECT_FAILED; - - /* Perform all bisection computation */ - res = bisect_next_all(the_repository, prefix); - - if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) { - res = bisect_successful(terms); - return res ? res : BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND; - } else if (res == BISECT_ONLY_SKIPPED_LEFT) { - res = bisect_skipped_commits(terms); - return res ? res : BISECT_ONLY_SKIPPED_LEFT; - } - return res; -} - -static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char *prefix) -{ - if (bisect_next_check(terms, NULL)) - return BISECT_OK; - - return bisect_next(terms, prefix); -} - -static int bisect_start(struct bisect_terms *terms, const char **argv, int argc) -{ - int no_checkout = 0; - int first_parent_only = 0; - int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0; - int flags, pathspec_pos, res = 0; - struct string_list revs = STRING_LIST_INIT_DUP; - struct string_list states = STRING_LIST_INIT_DUP; - struct strbuf start_head = STRBUF_INIT; - struct strbuf bisect_names = STRBUF_INIT; - struct object_id head_oid; - struct object_id oid; - const char *head; - - if (is_bare_repository()) - no_checkout = 1; - - /* - * Check for one bad and then some good revisions - */ - for (i = 0; i < argc; i++) { - if (!strcmp(argv[i], "--")) { - has_double_dash = 1; - break; - } - } - - for (i = 0; i < argc; i++) { - const char *arg = argv[i]; - if (!strcmp(argv[i], "--")) { - break; - } else if (!strcmp(arg, "--no-checkout")) { - no_checkout = 1; - } else if (!strcmp(arg, "--first-parent")) { - first_parent_only = 1; - } else if (!strcmp(arg, "--term-good") || - !strcmp(arg, "--term-old")) { - i++; - if (argc <= i) - return error(_("'' is not a valid term")); - must_write_terms = 1; - free((void *) terms->term_good); - terms->term_good = xstrdup(argv[i]); - } else if (skip_prefix(arg, "--term-good=", &arg) || - skip_prefix(arg, "--term-old=", &arg)) { - must_write_terms = 1; - free((void *) terms->term_good); - terms->term_good = xstrdup(arg); - } else if (!strcmp(arg, "--term-bad") || - !strcmp(arg, "--term-new")) { - i++; - if (argc <= i) - return error(_("'' is not a valid term")); - must_write_terms = 1; - free((void *) terms->term_bad); - terms->term_bad = xstrdup(argv[i]); - } else if (skip_prefix(arg, "--term-bad=", &arg) || - skip_prefix(arg, "--term-new=", &arg)) { - must_write_terms = 1; - free((void *) terms->term_bad); - terms->term_bad = xstrdup(arg); - } else if (starts_with(arg, "--")) { - return error(_("unrecognized option: '%s'"), arg); - } else if (!get_oidf(&oid, "%s^{commit}", arg)) { - string_list_append(&revs, oid_to_hex(&oid)); - } else if (has_double_dash) { - die(_("'%s' does not appear to be a valid " - "revision"), arg); - } else { - break; - } - } - pathspec_pos = i; - - /* - * The user ran "git bisect start <sha1> <sha1>", hence did not - * explicitly specify the terms, but we are already starting to - * set references named with the default terms, and won't be able - * to change afterwards. - */ - if (revs.nr) - must_write_terms = 1; - for (i = 0; i < revs.nr; i++) { - if (bad_seen) { - string_list_append(&states, terms->term_good); - } else { - bad_seen = 1; - string_list_append(&states, terms->term_bad); - } - } - - /* - * Verify HEAD - */ - head = resolve_ref_unsafe("HEAD", 0, &head_oid, &flags); - if (!head) - if (get_oid("HEAD", &head_oid)) - return error(_("bad HEAD - I need a HEAD")); - - /* - * Check if we are bisecting - */ - if (!is_empty_or_missing_file(git_path_bisect_start())) { - /* Reset to the rev from where we started */ - strbuf_read_file(&start_head, git_path_bisect_start(), 0); - strbuf_trim(&start_head); - if (!no_checkout) { - struct strvec argv = STRVEC_INIT; - - strvec_pushl(&argv, "checkout", start_head.buf, - "--", NULL); - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) { - res = error(_("checking out '%s' failed." - " Try 'git bisect start " - "<valid-branch>'."), - start_head.buf); - goto finish; - } - } - } else { - /* Get the rev from where we start. */ - if (!get_oid(head, &head_oid) && - !starts_with(head, "refs/heads/")) { - strbuf_reset(&start_head); - strbuf_addstr(&start_head, oid_to_hex(&head_oid)); - } else if (!get_oid(head, &head_oid) && - skip_prefix(head, "refs/heads/", &head)) { - /* - * This error message should only be triggered by - * cogito usage, and cogito users should understand - * it relates to cg-seek. - */ - if (!is_empty_or_missing_file(git_path_head_name())) - return error(_("won't bisect on cg-seek'ed tree")); - strbuf_addstr(&start_head, head); - } else { - return error(_("bad HEAD - strange symbolic ref")); - } - } - - /* - * Get rid of any old bisect state. - */ - if (bisect_clean_state()) - return -1; - - /* - * In case of mistaken revs or checkout error, or signals received, - * "bisect_auto_next" below may exit or misbehave. - * We have to trap this to be able to clean up using - * "bisect_clean_state". - */ - - /* - * Write new start state - */ - write_file(git_path_bisect_start(), "%s\n", start_head.buf); - - if (first_parent_only) - write_file(git_path_bisect_first_parent(), "\n"); - - if (no_checkout) { - if (get_oid(start_head.buf, &oid) < 0) { - res = error(_("invalid ref: '%s'"), start_head.buf); - goto finish; - } - if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0, - UPDATE_REFS_MSG_ON_ERR)) { - res = -1; - goto finish; - } - } - - if (pathspec_pos < argc - 1) - sq_quote_argv(&bisect_names, argv + pathspec_pos); - write_file(git_path_bisect_names(), "%s\n", bisect_names.buf); - - for (i = 0; i < states.nr; i++) - if (bisect_write(states.items[i].string, - revs.items[i].string, terms, 1)) { - res = -1; - goto finish; - } - - if (must_write_terms && write_terms(terms->term_bad, - terms->term_good)) { - res = -1; - goto finish; - } - - res = bisect_append_log_quoted(argv); - if (res) - res = -1; - -finish: - string_list_clear(&revs, 0); - string_list_clear(&states, 0); - strbuf_release(&start_head); - strbuf_release(&bisect_names); - return res; -} - -static inline int file_is_not_empty(const char *path) -{ - return !is_empty_or_missing_file(path); -} - -static int bisect_autostart(struct bisect_terms *terms) -{ - int res; - const char *yesno; - - if (file_is_not_empty(git_path_bisect_start())) - return 0; - - fprintf_ln(stderr, _("You need to start by \"git bisect " - "start\"\n")); - - if (!isatty(STDIN_FILENO)) - return -1; - - /* - * TRANSLATORS: Make sure to include [Y] and [n] in your - * translation. The program will only accept English input - * at this point. - */ - yesno = git_prompt(_("Do you want me to do it for you " - "[Y/n]? "), PROMPT_ECHO); - res = tolower(*yesno) == 'n' ? - -1 : bisect_start(terms, empty_strvec, 0); - - return res; -} - -int cmd_bisect__helper(int argc, const char **argv, const char *prefix) -{ - enum { - NEXT_ALL = 1, - WRITE_TERMS, - BISECT_CLEAN_STATE, - CHECK_EXPECTED_REVS, - BISECT_RESET, - BISECT_WRITE, - CHECK_AND_SET_TERMS, - BISECT_NEXT_CHECK, - BISECT_TERMS, - BISECT_START, - BISECT_AUTOSTART, - BISECT_NEXT, - BISECT_AUTO_NEXT - } cmdmode = 0; - int res = 0, nolog = 0; - struct option options[] = { - OPT_CMDMODE(0, "next-all", &cmdmode, - N_("perform 'git bisect next'"), NEXT_ALL), - OPT_CMDMODE(0, "write-terms", &cmdmode, - N_("write the terms to .git/BISECT_TERMS"), WRITE_TERMS), - OPT_CMDMODE(0, "bisect-clean-state", &cmdmode, - N_("cleanup the bisection state"), BISECT_CLEAN_STATE), - OPT_CMDMODE(0, "check-expected-revs", &cmdmode, - N_("check for expected revs"), CHECK_EXPECTED_REVS), - OPT_CMDMODE(0, "bisect-reset", &cmdmode, - N_("reset the bisection state"), BISECT_RESET), - OPT_CMDMODE(0, "bisect-write", &cmdmode, - N_("write out the bisection state in BISECT_LOG"), BISECT_WRITE), - OPT_CMDMODE(0, "check-and-set-terms", &cmdmode, - N_("check and set terms in a bisection state"), CHECK_AND_SET_TERMS), - OPT_CMDMODE(0, "bisect-next-check", &cmdmode, - N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK), - OPT_CMDMODE(0, "bisect-terms", &cmdmode, - N_("print out the bisect terms"), BISECT_TERMS), - OPT_CMDMODE(0, "bisect-start", &cmdmode, - N_("start the bisect session"), BISECT_START), - OPT_CMDMODE(0, "bisect-next", &cmdmode, - N_("find the next bisection commit"), BISECT_NEXT), - OPT_CMDMODE(0, "bisect-auto-next", &cmdmode, - N_("verify the next bisection state then checkout the next bisection commit"), BISECT_AUTO_NEXT), - OPT_CMDMODE(0, "bisect-autostart", &cmdmode, - N_("start the bisection if it has not yet been started"), BISECT_AUTOSTART), - OPT_BOOL(0, "no-log", &nolog, - N_("no log for BISECT_WRITE")), - OPT_END() - }; - struct bisect_terms terms = { .term_good = NULL, .term_bad = NULL }; - - argc = parse_options(argc, argv, prefix, options, - git_bisect_helper_usage, - PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_UNKNOWN); - - if (!cmdmode) - usage_with_options(git_bisect_helper_usage, options); - - switch (cmdmode) { - case NEXT_ALL: - res = bisect_next_all(the_repository, prefix); - break; - case WRITE_TERMS: - if (argc != 2) - return error(_("--write-terms requires two arguments")); - return write_terms(argv[0], argv[1]); - case BISECT_CLEAN_STATE: - if (argc != 0) - return error(_("--bisect-clean-state requires no arguments")); - return bisect_clean_state(); - case CHECK_EXPECTED_REVS: - check_expected_revs(argv, argc); - return 0; - case BISECT_RESET: - if (argc > 1) - return error(_("--bisect-reset requires either no argument or a commit")); - return !!bisect_reset(argc ? argv[0] : NULL); - case BISECT_WRITE: - if (argc != 4 && argc != 5) - return error(_("--bisect-write requires either 4 or 5 arguments")); - set_terms(&terms, argv[3], argv[2]); - res = bisect_write(argv[0], argv[1], &terms, nolog); - break; - case CHECK_AND_SET_TERMS: - if (argc != 3) - return error(_("--check-and-set-terms requires 3 arguments")); - set_terms(&terms, argv[2], argv[1]); - res = check_and_set_terms(&terms, argv[0]); - break; - case BISECT_NEXT_CHECK: - if (argc != 2 && argc != 3) - return error(_("--bisect-next-check requires 2 or 3 arguments")); - set_terms(&terms, argv[1], argv[0]); - res = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL); - break; - case BISECT_TERMS: - if (argc > 1) - return error(_("--bisect-terms requires 0 or 1 argument")); - res = bisect_terms(&terms, argc == 1 ? argv[0] : NULL); - break; - case BISECT_START: - set_terms(&terms, "bad", "good"); - res = bisect_start(&terms, argv, argc); - break; - case BISECT_NEXT: - if (argc) - return error(_("--bisect-next requires 0 arguments")); - get_terms(&terms); - res = bisect_next(&terms, prefix); - break; - case BISECT_AUTO_NEXT: - if (argc) - return error(_("--bisect-auto-next requires 0 arguments")); - get_terms(&terms); - res = bisect_auto_next(&terms, prefix); - break; - case BISECT_AUTOSTART: - if (argc) - return error(_("--bisect-autostart does not accept arguments")); - set_terms(&terms, "bad", "good"); - res = bisect_autostart(&terms); - break; - default: - BUG("unknown subcommand %d", cmdmode); - } - free_terms(&terms); - - /* - * Handle early success - * From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all - */ - if ((res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) || (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND)) - res = BISECT_OK; - - return -res; -} diff --git a/third_party/git/builtin/blame.c b/third_party/git/builtin/blame.c deleted file mode 100644 index 7112be2afe16..000000000000 --- a/third_party/git/builtin/blame.c +++ /dev/null @@ -1,1204 +0,0 @@ -/* - * Blame - * - * Copyright (c) 2006, 2014 by its authors - * See COPYING for licensing conditions - */ - -#include "cache.h" -#include "config.h" -#include "color.h" -#include "builtin.h" -#include "repository.h" -#include "commit.h" -#include "diff.h" -#include "revision.h" -#include "quote.h" -#include "string-list.h" -#include "mailmap.h" -#include "parse-options.h" -#include "prio-queue.h" -#include "utf8.h" -#include "userdiff.h" -#include "line-range.h" -#include "line-log.h" -#include "dir.h" -#include "progress.h" -#include "object-store.h" -#include "blame.h" -#include "refs.h" -#include "tag.h" - -static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>"); - -static const char *blame_opt_usage[] = { - blame_usage, - "", - N_("<rev-opts> are documented in git-rev-list(1)"), - NULL -}; - -static int longest_file; -static int longest_author; -static int max_orig_digits; -static int max_digits; -static int max_score_digits; -static int show_root; -static int reverse; -static int blank_boundary; -static int incremental; -static int xdl_opts; -static int abbrev = -1; -static int no_whole_file_rename; -static int show_progress; -static char repeated_meta_color[COLOR_MAXLEN]; -static int coloring_mode; -static struct string_list ignore_revs_file_list = STRING_LIST_INIT_NODUP; -static int mark_unblamable_lines; -static int mark_ignored_lines; - -static struct date_mode blame_date_mode = { DATE_ISO8601 }; -static size_t blame_date_width; - -static struct string_list mailmap = STRING_LIST_INIT_NODUP; - -#ifndef DEBUG_BLAME -#define DEBUG_BLAME 0 -#endif - -static unsigned blame_move_score; -static unsigned blame_copy_score; - -/* Remember to update object flag allocation in object.h */ -#define METAINFO_SHOWN (1u<<12) -#define MORE_THAN_ONE_PATH (1u<<13) - -struct progress_info { - struct progress *progress; - int blamed_lines; -}; - -static const char *nth_line_cb(void *data, long lno) -{ - return blame_nth_line((struct blame_scoreboard *)data, lno); -} - -/* - * Information on commits, used for output. - */ -struct commit_info { - struct strbuf author; - struct strbuf author_mail; - timestamp_t author_time; - struct strbuf author_tz; - - /* filled only when asked for details */ - struct strbuf committer; - struct strbuf committer_mail; - timestamp_t committer_time; - struct strbuf committer_tz; - - struct strbuf summary; -}; - -/* - * Parse author/committer line in the commit object buffer - */ -static void get_ac_line(const char *inbuf, const char *what, - struct strbuf *name, struct strbuf *mail, - timestamp_t *time, struct strbuf *tz) -{ - struct ident_split ident; - size_t len, maillen, namelen; - char *tmp, *endp; - const char *namebuf, *mailbuf; - - tmp = strstr(inbuf, what); - if (!tmp) - goto error_out; - tmp += strlen(what); - endp = strchr(tmp, '\n'); - if (!endp) - len = strlen(tmp); - else - len = endp - tmp; - - if (split_ident_line(&ident, tmp, len)) { - error_out: - /* Ugh */ - tmp = "(unknown)"; - strbuf_addstr(name, tmp); - strbuf_addstr(mail, tmp); - strbuf_addstr(tz, tmp); - *time = 0; - return; - } - - namelen = ident.name_end - ident.name_begin; - namebuf = ident.name_begin; - - maillen = ident.mail_end - ident.mail_begin; - mailbuf = ident.mail_begin; - - if (ident.date_begin && ident.date_end) - *time = strtoul(ident.date_begin, NULL, 10); - else - *time = 0; - - if (ident.tz_begin && ident.tz_end) - strbuf_add(tz, ident.tz_begin, ident.tz_end - ident.tz_begin); - else - strbuf_addstr(tz, "(unknown)"); - - /* - * Now, convert both name and e-mail using mailmap - */ - map_user(&mailmap, &mailbuf, &maillen, - &namebuf, &namelen); - - strbuf_addf(mail, "<%.*s>", (int)maillen, mailbuf); - strbuf_add(name, namebuf, namelen); -} - -static void commit_info_init(struct commit_info *ci) -{ - - strbuf_init(&ci->author, 0); - strbuf_init(&ci->author_mail, 0); - strbuf_init(&ci->author_tz, 0); - strbuf_init(&ci->committer, 0); - strbuf_init(&ci->committer_mail, 0); - strbuf_init(&ci->committer_tz, 0); - strbuf_init(&ci->summary, 0); -} - -static void commit_info_destroy(struct commit_info *ci) -{ - - strbuf_release(&ci->author); - strbuf_release(&ci->author_mail); - strbuf_release(&ci->author_tz); - strbuf_release(&ci->committer); - strbuf_release(&ci->committer_mail); - strbuf_release(&ci->committer_tz); - strbuf_release(&ci->summary); -} - -static void get_commit_info(struct commit *commit, - struct commit_info *ret, - int detailed) -{ - int len; - const char *subject, *encoding; - const char *message; - - commit_info_init(ret); - - encoding = get_log_output_encoding(); - message = logmsg_reencode(commit, NULL, encoding); - get_ac_line(message, "\nauthor ", - &ret->author, &ret->author_mail, - &ret->author_time, &ret->author_tz); - - if (!detailed) { - unuse_commit_buffer(commit, message); - return; - } - - get_ac_line(message, "\ncommitter ", - &ret->committer, &ret->committer_mail, - &ret->committer_time, &ret->committer_tz); - - len = find_commit_subject(message, &subject); - if (len) - strbuf_add(&ret->summary, subject, len); - else - strbuf_addf(&ret->summary, "(%s)", oid_to_hex(&commit->object.oid)); - - unuse_commit_buffer(commit, message); -} - -/* - * Write out any suspect information which depends on the path. This must be - * handled separately from emit_one_suspect_detail(), because a given commit - * may have changes in multiple paths. So this needs to appear each time - * we mention a new group. - * - * To allow LF and other nonportable characters in pathnames, - * they are c-style quoted as needed. - */ -static void write_filename_info(struct blame_origin *suspect) -{ - if (suspect->previous) { - struct blame_origin *prev = suspect->previous; - printf("previous %s ", oid_to_hex(&prev->commit->object.oid)); - write_name_quoted(prev->path, stdout, '\n'); - } - printf("filename "); - write_name_quoted(suspect->path, stdout, '\n'); -} - -/* - * Porcelain/Incremental format wants to show a lot of details per - * commit. Instead of repeating this every line, emit it only once, - * the first time each commit appears in the output (unless the - * user has specifically asked for us to repeat). - */ -static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat) -{ - struct commit_info ci; - - if (!repeat && (suspect->commit->object.flags & METAINFO_SHOWN)) - return 0; - - suspect->commit->object.flags |= METAINFO_SHOWN; - get_commit_info(suspect->commit, &ci, 1); - printf("author %s\n", ci.author.buf); - printf("author-mail %s\n", ci.author_mail.buf); - printf("author-time %"PRItime"\n", ci.author_time); - printf("author-tz %s\n", ci.author_tz.buf); - printf("committer %s\n", ci.committer.buf); - printf("committer-mail %s\n", ci.committer_mail.buf); - printf("committer-time %"PRItime"\n", ci.committer_time); - printf("committer-tz %s\n", ci.committer_tz.buf); - printf("summary %s\n", ci.summary.buf); - if (suspect->commit->object.flags & UNINTERESTING) - printf("boundary\n"); - - commit_info_destroy(&ci); - - return 1; -} - -/* - * The blame_entry is found to be guilty for the range. - * Show it in incremental output. - */ -static void found_guilty_entry(struct blame_entry *ent, void *data) -{ - struct progress_info *pi = (struct progress_info *)data; - - if (incremental) { - struct blame_origin *suspect = ent->suspect; - - printf("%s %d %d %d\n", - oid_to_hex(&suspect->commit->object.oid), - ent->s_lno + 1, ent->lno + 1, ent->num_lines); - emit_one_suspect_detail(suspect, 0); - write_filename_info(suspect); - maybe_flush_or_die(stdout, "stdout"); - } - pi->blamed_lines += ent->num_lines; - display_progress(pi->progress, pi->blamed_lines); -} - -static const char *format_time(timestamp_t time, const char *tz_str, - int show_raw_time) -{ - static struct strbuf time_buf = STRBUF_INIT; - - strbuf_reset(&time_buf); - if (show_raw_time) { - strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str); - } - else { - const char *time_str; - size_t time_width; - int tz; - tz = atoi(tz_str); - time_str = show_date(time, tz, &blame_date_mode); - strbuf_addstr(&time_buf, time_str); - /* - * Add space paddings to time_buf to display a fixed width - * string, and use time_width for display width calibration. - */ - for (time_width = utf8_strwidth(time_str); - time_width < blame_date_width; - time_width++) - strbuf_addch(&time_buf, ' '); - } - return time_buf.buf; -} - -#define OUTPUT_ANNOTATE_COMPAT (1U<<0) -#define OUTPUT_LONG_OBJECT_NAME (1U<<1) -#define OUTPUT_RAW_TIMESTAMP (1U<<2) -#define OUTPUT_PORCELAIN (1U<<3) -#define OUTPUT_SHOW_NAME (1U<<4) -#define OUTPUT_SHOW_NUMBER (1U<<5) -#define OUTPUT_SHOW_SCORE (1U<<6) -#define OUTPUT_NO_AUTHOR (1U<<7) -#define OUTPUT_SHOW_EMAIL (1U<<8) -#define OUTPUT_LINE_PORCELAIN (1U<<9) -#define OUTPUT_COLOR_LINE (1U<<10) -#define OUTPUT_SHOW_AGE_WITH_COLOR (1U<<11) - -static void emit_porcelain_details(struct blame_origin *suspect, int repeat) -{ - if (emit_one_suspect_detail(suspect, repeat) || - (suspect->commit->object.flags & MORE_THAN_ONE_PATH)) - write_filename_info(suspect); -} - -static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent, - int opt) -{ - int repeat = opt & OUTPUT_LINE_PORCELAIN; - int cnt; - const char *cp; - struct blame_origin *suspect = ent->suspect; - char hex[GIT_MAX_HEXSZ + 1]; - - oid_to_hex_r(hex, &suspect->commit->object.oid); - printf("%s %d %d %d\n", - hex, - ent->s_lno + 1, - ent->lno + 1, - ent->num_lines); - emit_porcelain_details(suspect, repeat); - - cp = blame_nth_line(sb, ent->lno); - for (cnt = 0; cnt < ent->num_lines; cnt++) { - char ch; - if (cnt) { - printf("%s %d %d\n", hex, - ent->s_lno + 1 + cnt, - ent->lno + 1 + cnt); - if (repeat) - emit_porcelain_details(suspect, 1); - } - putchar('\t'); - do { - ch = *cp++; - putchar(ch); - } while (ch != '\n' && - cp < sb->final_buf + sb->final_buf_size); - } - - if (sb->final_buf_size && cp[-1] != '\n') - putchar('\n'); -} - -static struct color_field { - timestamp_t hop; - char col[COLOR_MAXLEN]; -} *colorfield; -static int colorfield_nr, colorfield_alloc; - -static void parse_color_fields(const char *s) -{ - struct string_list l = STRING_LIST_INIT_DUP; - struct string_list_item *item; - enum { EXPECT_DATE, EXPECT_COLOR } next = EXPECT_COLOR; - - colorfield_nr = 0; - - /* Ideally this would be stripped and split at the same time? */ - string_list_split(&l, s, ',', -1); - ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc); - - for_each_string_list_item(item, &l) { - switch (next) { - case EXPECT_DATE: - colorfield[colorfield_nr].hop = approxidate(item->string); - next = EXPECT_COLOR; - colorfield_nr++; - ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc); - break; - case EXPECT_COLOR: - if (color_parse(item->string, colorfield[colorfield_nr].col)) - die(_("expecting a color: %s"), item->string); - next = EXPECT_DATE; - break; - } - } - - if (next == EXPECT_COLOR) - die(_("must end with a color")); - - colorfield[colorfield_nr].hop = TIME_MAX; - string_list_clear(&l, 0); -} - -static void setup_default_color_by_age(void) -{ - parse_color_fields("blue,12 month ago,white,1 month ago,red"); -} - -static void determine_line_heat(struct blame_entry *ent, const char **dest_color) -{ - int i = 0; - struct commit_info ci; - get_commit_info(ent->suspect->commit, &ci, 1); - - while (i < colorfield_nr && ci.author_time > colorfield[i].hop) - i++; - - *dest_color = colorfield[i].col; -} - -static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt) -{ - int cnt; - const char *cp; - struct blame_origin *suspect = ent->suspect; - struct commit_info ci; - char hex[GIT_MAX_HEXSZ + 1]; - int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP); - const char *default_color = NULL, *color = NULL, *reset = NULL; - - get_commit_info(suspect->commit, &ci, 1); - oid_to_hex_r(hex, &suspect->commit->object.oid); - - cp = blame_nth_line(sb, ent->lno); - - if (opt & OUTPUT_SHOW_AGE_WITH_COLOR) { - determine_line_heat(ent, &default_color); - color = default_color; - reset = GIT_COLOR_RESET; - } - - for (cnt = 0; cnt < ent->num_lines; cnt++) { - char ch; - int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? the_hash_algo->hexsz : abbrev; - - if (opt & OUTPUT_COLOR_LINE) { - if (cnt > 0) { - color = repeated_meta_color; - reset = GIT_COLOR_RESET; - } else { - color = default_color ? default_color : NULL; - reset = default_color ? GIT_COLOR_RESET : NULL; - } - } - if (color) - fputs(color, stdout); - - if (suspect->commit->object.flags & UNINTERESTING) { - if (blank_boundary) - memset(hex, ' ', length); - else if (!(opt & OUTPUT_ANNOTATE_COMPAT)) { - length--; - putchar('^'); - } - } - - if (mark_unblamable_lines && ent->unblamable) { - length--; - putchar('*'); - } - if (mark_ignored_lines && ent->ignored) { - length--; - putchar('?'); - } - printf("%.*s", length, hex); - if (opt & OUTPUT_ANNOTATE_COMPAT) { - const char *name; - if (opt & OUTPUT_SHOW_EMAIL) - name = ci.author_mail.buf; - else - name = ci.author.buf; - printf("\t(%10s\t%10s\t%d)", name, - format_time(ci.author_time, ci.author_tz.buf, - show_raw_time), - ent->lno + 1 + cnt); - } else { - if (opt & OUTPUT_SHOW_SCORE) - printf(" %*d %02d", - max_score_digits, ent->score, - ent->suspect->refcnt); - if (opt & OUTPUT_SHOW_NAME) - printf(" %-*.*s", longest_file, longest_file, - suspect->path); - if (opt & OUTPUT_SHOW_NUMBER) - printf(" %*d", max_orig_digits, - ent->s_lno + 1 + cnt); - - if (!(opt & OUTPUT_NO_AUTHOR)) { - const char *name; - int pad; - if (opt & OUTPUT_SHOW_EMAIL) - name = ci.author_mail.buf; - else - name = ci.author.buf; - pad = longest_author - utf8_strwidth(name); - printf(" (%s%*s %10s", - name, pad, "", - format_time(ci.author_time, - ci.author_tz.buf, - show_raw_time)); - } - printf(" %*d) ", - max_digits, ent->lno + 1 + cnt); - } - if (reset) - fputs(reset, stdout); - do { - ch = *cp++; - putchar(ch); - } while (ch != '\n' && - cp < sb->final_buf + sb->final_buf_size); - } - - if (sb->final_buf_size && cp[-1] != '\n') - putchar('\n'); - - commit_info_destroy(&ci); -} - -static void output(struct blame_scoreboard *sb, int option) -{ - struct blame_entry *ent; - - if (option & OUTPUT_PORCELAIN) { - for (ent = sb->ent; ent; ent = ent->next) { - int count = 0; - struct blame_origin *suspect; - struct commit *commit = ent->suspect->commit; - if (commit->object.flags & MORE_THAN_ONE_PATH) - continue; - for (suspect = get_blame_suspects(commit); suspect; suspect = suspect->next) { - if (suspect->guilty && count++) { - commit->object.flags |= MORE_THAN_ONE_PATH; - break; - } - } - } - } - - for (ent = sb->ent; ent; ent = ent->next) { - if (option & OUTPUT_PORCELAIN) - emit_porcelain(sb, ent, option); - else { - emit_other(sb, ent, option); - } - } -} - -/* - * Add phony grafts for use with -S; this is primarily to - * support git's cvsserver that wants to give a linear history - * to its clients. - */ -static int read_ancestry(const char *graft_file) -{ - FILE *fp = fopen_or_warn(graft_file, "r"); - struct strbuf buf = STRBUF_INIT; - if (!fp) - return -1; - while (!strbuf_getwholeline(&buf, fp, '\n')) { - /* The format is just "Commit Parent1 Parent2 ...\n" */ - struct commit_graft *graft = read_graft_line(&buf); - if (graft) - register_commit_graft(the_repository, graft, 0); - } - fclose(fp); - strbuf_release(&buf); - return 0; -} - -static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect) -{ - const char *uniq = find_unique_abbrev(&suspect->commit->object.oid, - auto_abbrev); - int len = strlen(uniq); - if (auto_abbrev < len) - return len; - return auto_abbrev; -} - -/* - * How many columns do we need to show line numbers, authors, - * and filenames? - */ -static void find_alignment(struct blame_scoreboard *sb, int *option) -{ - int longest_src_lines = 0; - int longest_dst_lines = 0; - unsigned largest_score = 0; - struct blame_entry *e; - int compute_auto_abbrev = (abbrev < 0); - int auto_abbrev = DEFAULT_ABBREV; - - for (e = sb->ent; e; e = e->next) { - struct blame_origin *suspect = e->suspect; - int num; - - if (compute_auto_abbrev) - auto_abbrev = update_auto_abbrev(auto_abbrev, suspect); - if (strcmp(suspect->path, sb->path)) - *option |= OUTPUT_SHOW_NAME; - num = strlen(suspect->path); - if (longest_file < num) - longest_file = num; - if (!(suspect->commit->object.flags & METAINFO_SHOWN)) { - struct commit_info ci; - suspect->commit->object.flags |= METAINFO_SHOWN; - get_commit_info(suspect->commit, &ci, 1); - if (*option & OUTPUT_SHOW_EMAIL) - num = utf8_strwidth(ci.author_mail.buf); - else - num = utf8_strwidth(ci.author.buf); - if (longest_author < num) - longest_author = num; - commit_info_destroy(&ci); - } - num = e->s_lno + e->num_lines; - if (longest_src_lines < num) - longest_src_lines = num; - num = e->lno + e->num_lines; - if (longest_dst_lines < num) - longest_dst_lines = num; - if (largest_score < blame_entry_score(sb, e)) - largest_score = blame_entry_score(sb, e); - } - max_orig_digits = decimal_width(longest_src_lines); - max_digits = decimal_width(longest_dst_lines); - max_score_digits = decimal_width(largest_score); - - if (compute_auto_abbrev) - /* one more abbrev length is needed for the boundary commit */ - abbrev = auto_abbrev + 1; -} - -static void sanity_check_on_fail(struct blame_scoreboard *sb, int baa) -{ - int opt = OUTPUT_SHOW_SCORE | OUTPUT_SHOW_NUMBER | OUTPUT_SHOW_NAME; - find_alignment(sb, &opt); - output(sb, opt); - die("Baa %d!", baa); -} - -static unsigned parse_score(const char *arg) -{ - char *end; - unsigned long score = strtoul(arg, &end, 10); - if (*end) - return 0; - return score; -} - -static const char *add_prefix(const char *prefix, const char *path) -{ - return prefix_path(prefix, prefix ? strlen(prefix) : 0, path); -} - -static int git_blame_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "blame.showroot")) { - show_root = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "blame.blankboundary")) { - blank_boundary = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "blame.showemail")) { - int *output_option = cb; - if (git_config_bool(var, value)) - *output_option |= OUTPUT_SHOW_EMAIL; - else - *output_option &= ~OUTPUT_SHOW_EMAIL; - return 0; - } - if (!strcmp(var, "blame.date")) { - if (!value) - return config_error_nonbool(var); - parse_date_format(value, &blame_date_mode); - return 0; - } - if (!strcmp(var, "blame.ignorerevsfile")) { - const char *str; - int ret; - - ret = git_config_pathname(&str, var, value); - if (ret) - return ret; - string_list_insert(&ignore_revs_file_list, str); - return 0; - } - if (!strcmp(var, "blame.markunblamablelines")) { - mark_unblamable_lines = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "blame.markignoredlines")) { - mark_ignored_lines = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "color.blame.repeatedlines")) { - if (color_parse_mem(value, strlen(value), repeated_meta_color)) - warning(_("invalid color '%s' in color.blame.repeatedLines"), - value); - return 0; - } - if (!strcmp(var, "color.blame.highlightrecent")) { - parse_color_fields(value); - return 0; - } - - if (!strcmp(var, "blame.coloring")) { - if (!strcmp(value, "repeatedLines")) { - coloring_mode |= OUTPUT_COLOR_LINE; - } else if (!strcmp(value, "highlightRecent")) { - coloring_mode |= OUTPUT_SHOW_AGE_WITH_COLOR; - } else if (!strcmp(value, "none")) { - coloring_mode &= ~(OUTPUT_COLOR_LINE | - OUTPUT_SHOW_AGE_WITH_COLOR); - } else { - warning(_("invalid value for blame.coloring")); - return 0; - } - } - - if (git_diff_heuristic_config(var, value, cb) < 0) - return -1; - if (userdiff_config(var, value) < 0) - return -1; - - return git_default_config(var, value, cb); -} - -static int blame_copy_callback(const struct option *option, const char *arg, int unset) -{ - int *opt = option->value; - - BUG_ON_OPT_NEG(unset); - - /* - * -C enables copy from removed files; - * -C -C enables copy from existing files, but only - * when blaming a new file; - * -C -C -C enables copy from existing files for - * everybody - */ - if (*opt & PICKAXE_BLAME_COPY_HARDER) - *opt |= PICKAXE_BLAME_COPY_HARDEST; - if (*opt & PICKAXE_BLAME_COPY) - *opt |= PICKAXE_BLAME_COPY_HARDER; - *opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE; - - if (arg) - blame_copy_score = parse_score(arg); - return 0; -} - -static int blame_move_callback(const struct option *option, const char *arg, int unset) -{ - int *opt = option->value; - - BUG_ON_OPT_NEG(unset); - - *opt |= PICKAXE_BLAME_MOVE; - - if (arg) - blame_move_score = parse_score(arg); - return 0; -} - -static int is_a_rev(const char *name) -{ - struct object_id oid; - - if (get_oid(name, &oid)) - return 0; - return OBJ_NONE < oid_object_info(the_repository, &oid, NULL); -} - -static int peel_to_commit_oid(struct object_id *oid_ret, void *cbdata) -{ - struct repository *r = ((struct blame_scoreboard *)cbdata)->repo; - struct object_id oid; - - oidcpy(&oid, oid_ret); - while (1) { - struct object *obj; - int kind = oid_object_info(r, &oid, NULL); - if (kind == OBJ_COMMIT) { - oidcpy(oid_ret, &oid); - return 0; - } - if (kind != OBJ_TAG) - return -1; - obj = deref_tag(r, parse_object(r, &oid), NULL, 0); - oidcpy(&oid, &obj->oid); - } -} - -static void build_ignorelist(struct blame_scoreboard *sb, - struct string_list *ignore_revs_file_list, - struct string_list *ignore_rev_list) -{ - struct string_list_item *i; - struct object_id oid; - - oidset_init(&sb->ignore_list, 0); - for_each_string_list_item(i, ignore_revs_file_list) { - if (!strcmp(i->string, "")) - oidset_clear(&sb->ignore_list); - else - oidset_parse_file_carefully(&sb->ignore_list, i->string, - peel_to_commit_oid, sb); - } - for_each_string_list_item(i, ignore_rev_list) { - if (get_oid_committish(i->string, &oid) || - peel_to_commit_oid(&oid, sb)) - die(_("cannot find revision %s to ignore"), i->string); - oidset_insert(&sb->ignore_list, &oid); - } -} - -int cmd_blame(int argc, const char **argv, const char *prefix) -{ - struct rev_info revs; - const char *path; - struct blame_scoreboard sb; - struct blame_origin *o; - struct blame_entry *ent = NULL; - long dashdash_pos, lno; - struct progress_info pi = { NULL, 0 }; - - struct string_list range_list = STRING_LIST_INIT_NODUP; - struct string_list ignore_rev_list = STRING_LIST_INIT_NODUP; - int output_option = 0, opt = 0; - int show_stats = 0; - const char *revs_file = NULL; - const char *contents_from = NULL; - const struct option options[] = { - OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")), - OPT_BOOL('b', NULL, &blank_boundary, N_("Do not show object names of boundary commits (Default: off)")), - OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")), - OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")), - OPT_BOOL(0, "progress", &show_progress, N_("Force progress reporting")), - OPT_BIT(0, "score-debug", &output_option, N_("Show output score for blame entries"), OUTPUT_SHOW_SCORE), - OPT_BIT('f', "show-name", &output_option, N_("Show original filename (Default: auto)"), OUTPUT_SHOW_NAME), - OPT_BIT('n', "show-number", &output_option, N_("Show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER), - OPT_BIT('p', "porcelain", &output_option, N_("Show in a format designed for machine consumption"), OUTPUT_PORCELAIN), - OPT_BIT(0, "line-porcelain", &output_option, N_("Show porcelain format with per-line commit information"), OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN), - OPT_BIT('c', NULL, &output_option, N_("Use the same output mode as git-annotate (Default: off)"), OUTPUT_ANNOTATE_COMPAT), - OPT_BIT('t', NULL, &output_option, N_("Show raw timestamp (Default: off)"), OUTPUT_RAW_TIMESTAMP), - OPT_BIT('l', NULL, &output_option, N_("Show long commit SHA1 (Default: off)"), OUTPUT_LONG_OBJECT_NAME), - OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR), - OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL), - OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE), - OPT_STRING_LIST(0, "ignore-rev", &ignore_rev_list, N_("rev"), N_("Ignore <rev> when blaming")), - OPT_STRING_LIST(0, "ignore-revs-file", &ignore_revs_file_list, N_("file"), N_("Ignore revisions from <file>")), - OPT_BIT(0, "color-lines", &output_option, N_("color redundant metadata from previous line differently"), OUTPUT_COLOR_LINE), - OPT_BIT(0, "color-by-age", &output_option, N_("color lines by age"), OUTPUT_SHOW_AGE_WITH_COLOR), - OPT_BIT(0, "minimal", &xdl_opts, N_("Spend extra cycles to find better match"), XDF_NEED_MINIMAL), - OPT_STRING('S', NULL, &revs_file, N_("file"), N_("Use revisions from <file> instead of calling git-rev-list")), - OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")), - OPT_CALLBACK_F('C', NULL, &opt, N_("score"), N_("Find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback), - OPT_CALLBACK_F('M', NULL, &opt, N_("score"), N_("Find line movements within and across files"), PARSE_OPT_OPTARG, blame_move_callback), - OPT_STRING_LIST('L', NULL, &range_list, N_("n,m"), N_("Process only line range n,m, counting from 1")), - OPT__ABBREV(&abbrev), - OPT_END() - }; - - struct parse_opt_ctx_t ctx; - int cmd_is_annotate = !strcmp(argv[0], "annotate"); - struct range_set ranges; - unsigned int range_i; - long anchor; - const int hexsz = the_hash_algo->hexsz; - - setup_default_color_by_age(); - git_config(git_blame_config, &output_option); - repo_init_revisions(the_repository, &revs, NULL); - revs.date_mode = blame_date_mode; - revs.diffopt.flags.allow_textconv = 1; - revs.diffopt.flags.follow_renames = 1; - - save_commit_buffer = 0; - dashdash_pos = 0; - show_progress = -1; - - parse_options_start(&ctx, argc, argv, prefix, options, - PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0); - for (;;) { - switch (parse_options_step(&ctx, options, blame_opt_usage)) { - case PARSE_OPT_HELP: - case PARSE_OPT_ERROR: - exit(129); - case PARSE_OPT_COMPLETE: - exit(0); - case PARSE_OPT_DONE: - if (ctx.argv[0]) - dashdash_pos = ctx.cpidx; - goto parse_done; - } - - if (!strcmp(ctx.argv[0], "--reverse")) { - ctx.argv[0] = "--children"; - reverse = 1; - } - parse_revision_opt(&revs, &ctx, options, blame_opt_usage); - } -parse_done: - no_whole_file_rename = !revs.diffopt.flags.follow_renames; - xdl_opts |= revs.diffopt.xdl_opts & XDF_INDENT_HEURISTIC; - revs.diffopt.flags.follow_renames = 0; - argc = parse_options_end(&ctx); - - if (incremental || (output_option & OUTPUT_PORCELAIN)) { - if (show_progress > 0) - die(_("--progress can't be used with --incremental or porcelain formats")); - show_progress = 0; - } else if (show_progress < 0) - show_progress = isatty(2); - - if (0 < abbrev && abbrev < hexsz) - /* one more abbrev length is needed for the boundary commit */ - abbrev++; - else if (!abbrev) - abbrev = hexsz; - - if (revs_file && read_ancestry(revs_file)) - die_errno("reading graft file '%s' failed", revs_file); - - if (cmd_is_annotate) { - output_option |= OUTPUT_ANNOTATE_COMPAT; - blame_date_mode.type = DATE_ISO8601; - } else { - blame_date_mode = revs.date_mode; - } - - /* The maximum width used to show the dates */ - switch (blame_date_mode.type) { - case DATE_RFC2822: - blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700"); - break; - case DATE_ISO8601_STRICT: - blame_date_width = sizeof("2006-10-19T16:00:04-07:00"); - break; - case DATE_ISO8601: - blame_date_width = sizeof("2006-10-19 16:00:04 -0700"); - break; - case DATE_RAW: - blame_date_width = sizeof("1161298804 -0700"); - break; - case DATE_UNIX: - blame_date_width = sizeof("1161298804"); - break; - case DATE_SHORT: - blame_date_width = sizeof("2006-10-19"); - break; - case DATE_RELATIVE: - /* - * TRANSLATORS: This string is used to tell us the - * maximum display width for a relative timestamp in - * "git blame" output. For C locale, "4 years, 11 - * months ago", which takes 22 places, is the longest - * among various forms of relative timestamps, but - * your language may need more or fewer display - * columns. - */ - blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */ - break; - case DATE_HUMAN: - /* If the year is shown, no time is shown */ - blame_date_width = sizeof("Thu Oct 19 16:00"); - break; - case DATE_NORMAL: - blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700"); - break; - case DATE_STRFTIME: - blame_date_width = strlen(show_date(0, 0, &blame_date_mode)) + 1; /* add the null */ - break; - case DATE_DOTTIME: - blame_date_width = sizeof("2006-10-19T15ยท00-0700"); - break; - } - blame_date_width -= 1; /* strip the null */ - - if (revs.diffopt.flags.find_copies_harder) - opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE | - PICKAXE_BLAME_COPY_HARDER); - - /* - * We have collected options unknown to us in argv[1..unk] - * which are to be passed to revision machinery if we are - * going to do the "bottom" processing. - * - * The remaining are: - * - * (1) if dashdash_pos != 0, it is either - * "blame [revisions] -- <path>" or - * "blame -- <path> <rev>" - * - * (2) otherwise, it is one of the two: - * "blame [revisions] <path>" - * "blame <path> <rev>" - * - * Note that we must strip out <path> from the arguments: we do not - * want the path pruning but we may want "bottom" processing. - */ - if (dashdash_pos) { - switch (argc - dashdash_pos - 1) { - case 2: /* (1b) */ - if (argc != 4) - usage_with_options(blame_opt_usage, options); - /* reorder for the new way: <rev> -- <path> */ - argv[1] = argv[3]; - argv[3] = argv[2]; - argv[2] = "--"; - /* FALLTHROUGH */ - case 1: /* (1a) */ - path = add_prefix(prefix, argv[--argc]); - argv[argc] = NULL; - break; - default: - usage_with_options(blame_opt_usage, options); - } - } else { - if (argc < 2) - usage_with_options(blame_opt_usage, options); - if (argc == 3 && is_a_rev(argv[argc - 1])) { /* (2b) */ - path = add_prefix(prefix, argv[1]); - argv[1] = argv[2]; - } else { /* (2a) */ - if (argc == 2 && is_a_rev(argv[1]) && !get_git_work_tree()) - die("missing <path> to blame"); - path = add_prefix(prefix, argv[argc - 1]); - } - argv[argc - 1] = "--"; - } - - revs.disable_stdin = 1; - setup_revisions(argc, argv, &revs, NULL); - if (!revs.pending.nr && is_bare_repository()) { - struct commit *head_commit; - struct object_id head_oid; - - if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, - &head_oid, NULL) || - !(head_commit = lookup_commit_reference_gently(revs.repo, - &head_oid, 1))) - die("no such ref: HEAD"); - - add_pending_object(&revs, &head_commit->object, "HEAD"); - } - - init_scoreboard(&sb); - sb.revs = &revs; - sb.contents_from = contents_from; - sb.reverse = reverse; - sb.repo = the_repository; - build_ignorelist(&sb, &ignore_revs_file_list, &ignore_rev_list); - string_list_clear(&ignore_revs_file_list, 0); - string_list_clear(&ignore_rev_list, 0); - setup_scoreboard(&sb, path, &o); - - /* - * Changed-path Bloom filters are disabled when looking - * for copies. - */ - if (!(opt & PICKAXE_BLAME_COPY)) - setup_blame_bloom_data(&sb, path); - - lno = sb.num_lines; - - if (lno && !range_list.nr) - string_list_append(&range_list, "1"); - - anchor = 1; - range_set_init(&ranges, range_list.nr); - for (range_i = 0; range_i < range_list.nr; ++range_i) { - long bottom, top; - if (parse_range_arg(range_list.items[range_i].string, - nth_line_cb, &sb, lno, anchor, - &bottom, &top, sb.path, - the_repository->index)) - usage(blame_usage); - if ((!lno && (top || bottom)) || lno < bottom) - die(Q_("file %s has only %lu line", - "file %s has only %lu lines", - lno), path, lno); - if (bottom < 1) - bottom = 1; - if (top < 1 || lno < top) - top = lno; - bottom--; - range_set_append_unsafe(&ranges, bottom, top); - anchor = top + 1; - } - sort_and_merge_range_set(&ranges); - - for (range_i = ranges.nr; range_i > 0; --range_i) { - const struct range *r = &ranges.ranges[range_i - 1]; - ent = blame_entry_prepend(ent, r->start, r->end, o); - } - - o->suspects = ent; - prio_queue_put(&sb.commits, o->commit); - - blame_origin_decref(o); - - range_set_release(&ranges); - string_list_clear(&range_list, 0); - - sb.ent = NULL; - sb.path = path; - - if (blame_move_score) - sb.move_score = blame_move_score; - if (blame_copy_score) - sb.copy_score = blame_copy_score; - - sb.debug = DEBUG_BLAME; - sb.on_sanity_fail = &sanity_check_on_fail; - - sb.show_root = show_root; - sb.xdl_opts = xdl_opts; - sb.no_whole_file_rename = no_whole_file_rename; - - read_mailmap(&mailmap, NULL); - - sb.found_guilty_entry = &found_guilty_entry; - sb.found_guilty_entry_data = π - if (show_progress) - pi.progress = start_delayed_progress(_("Blaming lines"), sb.num_lines); - - assign_blame(&sb, opt); - - stop_progress(&pi.progress); - - if (!incremental) - setup_pager(); - else - return 0; - - blame_sort_final(&sb); - - blame_coalesce(&sb); - - if (!(output_option & (OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR))) - output_option |= coloring_mode; - - if (!(output_option & OUTPUT_PORCELAIN)) { - find_alignment(&sb, &output_option); - if (!*repeated_meta_color && - (output_option & OUTPUT_COLOR_LINE)) - xsnprintf(repeated_meta_color, - sizeof(repeated_meta_color), - "%s", GIT_COLOR_CYAN); - } - if (output_option & OUTPUT_ANNOTATE_COMPAT) - output_option &= ~(OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR); - - output(&sb, output_option); - free((void *)sb.final_buf); - for (ent = sb.ent; ent; ) { - struct blame_entry *e = ent->next; - free(ent); - ent = e; - } - - if (show_stats) { - printf("num read blob: %d\n", sb.num_read_blob); - printf("num get patch: %d\n", sb.num_get_patch); - printf("num commits: %d\n", sb.num_commits); - } - - cleanup_scoreboard(&sb); - return 0; -} diff --git a/third_party/git/builtin/branch.c b/third_party/git/builtin/branch.c deleted file mode 100644 index efb30b882069..000000000000 --- a/third_party/git/builtin/branch.c +++ /dev/null @@ -1,853 +0,0 @@ -/* - * Builtin "git branch" - * - * Copyright (c) 2006 Kristian Hรธgsberg <krh@redhat.com> - * Based on git-branch.sh by Junio C Hamano. - */ - -#include "cache.h" -#include "config.h" -#include "color.h" -#include "refs.h" -#include "commit.h" -#include "builtin.h" -#include "remote.h" -#include "parse-options.h" -#include "branch.h" -#include "diff.h" -#include "revision.h" -#include "string-list.h" -#include "column.h" -#include "utf8.h" -#include "wt-status.h" -#include "ref-filter.h" -#include "worktree.h" -#include "help.h" -#include "commit-reach.h" - -static const char * const builtin_branch_usage[] = { - N_("git branch [<options>] [-r | -a] [--merged] [--no-merged]"), - N_("git branch [<options>] [-l] [-f] <branch-name> [<start-point>]"), - N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."), - N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"), - N_("git branch [<options>] (-c | -C) [<old-branch>] <new-branch>"), - N_("git branch [<options>] [-r | -a] [--points-at]"), - N_("git branch [<options>] [-r | -a] [--format]"), - NULL -}; - -static const char *head; -static struct object_id head_oid; - -static int branch_use_color = -1; -static char branch_colors[][COLOR_MAXLEN] = { - GIT_COLOR_RESET, - GIT_COLOR_NORMAL, /* PLAIN */ - GIT_COLOR_RED, /* REMOTE */ - GIT_COLOR_NORMAL, /* LOCAL */ - GIT_COLOR_GREEN, /* CURRENT */ - GIT_COLOR_BLUE, /* UPSTREAM */ - GIT_COLOR_CYAN, /* WORKTREE */ -}; -enum color_branch { - BRANCH_COLOR_RESET = 0, - BRANCH_COLOR_PLAIN = 1, - BRANCH_COLOR_REMOTE = 2, - BRANCH_COLOR_LOCAL = 3, - BRANCH_COLOR_CURRENT = 4, - BRANCH_COLOR_UPSTREAM = 5, - BRANCH_COLOR_WORKTREE = 6 -}; - -static const char *color_branch_slots[] = { - [BRANCH_COLOR_RESET] = "reset", - [BRANCH_COLOR_PLAIN] = "plain", - [BRANCH_COLOR_REMOTE] = "remote", - [BRANCH_COLOR_LOCAL] = "local", - [BRANCH_COLOR_CURRENT] = "current", - [BRANCH_COLOR_UPSTREAM] = "upstream", - [BRANCH_COLOR_WORKTREE] = "worktree", -}; - -static struct string_list output = STRING_LIST_INIT_DUP; -static unsigned int colopts; - -define_list_config_array(color_branch_slots); - -static int git_branch_config(const char *var, const char *value, void *cb) -{ - const char *slot_name; - struct ref_sorting **sorting_tail = (struct ref_sorting **)cb; - - if (!strcmp(var, "branch.sort")) { - if (!value) - return config_error_nonbool(var); - parse_ref_sorting(sorting_tail, value); - return 0; - } - - if (starts_with(var, "column.")) - return git_column_config(var, value, "branch", &colopts); - if (!strcmp(var, "color.branch")) { - branch_use_color = git_config_colorbool(var, value); - return 0; - } - if (skip_prefix(var, "color.branch.", &slot_name)) { - int slot = LOOKUP_CONFIG(color_branch_slots, slot_name); - if (slot < 0) - return 0; - if (!value) - return config_error_nonbool(var); - return color_parse(value, branch_colors[slot]); - } - return git_color_default_config(var, value, cb); -} - -static const char *branch_get_color(enum color_branch ix) -{ - if (want_color(branch_use_color)) - return branch_colors[ix]; - return ""; -} - -static int branch_merged(int kind, const char *name, - struct commit *rev, struct commit *head_rev) -{ - /* - * This checks whether the merge bases of branch and HEAD (or - * the other branch this branch builds upon) contains the - * branch, which means that the branch has already been merged - * safely to HEAD (or the other branch). - */ - struct commit *reference_rev = NULL; - const char *reference_name = NULL; - void *reference_name_to_free = NULL; - int merged; - - if (kind == FILTER_REFS_BRANCHES) { - struct branch *branch = branch_get(name); - const char *upstream = branch_get_upstream(branch, NULL); - struct object_id oid; - - if (upstream && - (reference_name = reference_name_to_free = - resolve_refdup(upstream, RESOLVE_REF_READING, - &oid, NULL)) != NULL) - reference_rev = lookup_commit_reference(the_repository, - &oid); - } - if (!reference_rev) - reference_rev = head_rev; - - merged = in_merge_bases(rev, reference_rev); - - /* - * After the safety valve is fully redefined to "check with - * upstream, if any, otherwise with HEAD", we should just - * return the result of the in_merge_bases() above without - * any of the following code, but during the transition period, - * a gentle reminder is in order. - */ - if ((head_rev != reference_rev) && - in_merge_bases(rev, head_rev) != merged) { - if (merged) - warning(_("deleting branch '%s' that has been merged to\n" - " '%s', but not yet merged to HEAD."), - name, reference_name); - else - warning(_("not deleting branch '%s' that is not yet merged to\n" - " '%s', even though it is merged to HEAD."), - name, reference_name); - } - free(reference_name_to_free); - return merged; -} - -static int check_branch_commit(const char *branchname, const char *refname, - const struct object_id *oid, struct commit *head_rev, - int kinds, int force) -{ - struct commit *rev = lookup_commit_reference(the_repository, oid); - if (!rev) { - error(_("Couldn't look up commit object for '%s'"), refname); - return -1; - } - if (!force && !branch_merged(kinds, branchname, rev, head_rev)) { - error(_("The branch '%s' is not fully merged.\n" - "If you are sure you want to delete it, " - "run 'git branch -D %s'."), branchname, branchname); - return -1; - } - return 0; -} - -static void delete_branch_config(const char *branchname) -{ - struct strbuf buf = STRBUF_INIT; - strbuf_addf(&buf, "branch.%s", branchname); - if (git_config_rename_section(buf.buf, NULL) < 0) - warning(_("Update of config-file failed")); - strbuf_release(&buf); -} - -static int delete_branches(int argc, const char **argv, int force, int kinds, - int quiet) -{ - struct commit *head_rev = NULL; - struct object_id oid; - char *name = NULL; - const char *fmt; - int i; - int ret = 0; - int remote_branch = 0; - struct strbuf bname = STRBUF_INIT; - unsigned allowed_interpret; - - switch (kinds) { - case FILTER_REFS_REMOTES: - fmt = "refs/remotes/%s"; - /* For subsequent UI messages */ - remote_branch = 1; - allowed_interpret = INTERPRET_BRANCH_REMOTE; - - force = 1; - break; - case FILTER_REFS_BRANCHES: - fmt = "refs/heads/%s"; - allowed_interpret = INTERPRET_BRANCH_LOCAL; - break; - default: - die(_("cannot use -a with -d")); - } - - if (!force) { - head_rev = lookup_commit_reference(the_repository, &head_oid); - if (!head_rev) - die(_("Couldn't look up commit object for HEAD")); - } - for (i = 0; i < argc; i++, strbuf_reset(&bname)) { - char *target = NULL; - int flags = 0; - - strbuf_branchname(&bname, argv[i], allowed_interpret); - free(name); - name = mkpathdup(fmt, bname.buf); - - if (kinds == FILTER_REFS_BRANCHES) { - const struct worktree *wt = - find_shared_symref("HEAD", name); - if (wt) { - error(_("Cannot delete branch '%s' " - "checked out at '%s'"), - bname.buf, wt->path); - ret = 1; - continue; - } - } - - target = resolve_refdup(name, - RESOLVE_REF_READING - | RESOLVE_REF_NO_RECURSE - | RESOLVE_REF_ALLOW_BAD_NAME, - &oid, &flags); - if (!target) { - error(remote_branch - ? _("remote-tracking branch '%s' not found.") - : _("branch '%s' not found."), bname.buf); - ret = 1; - continue; - } - - if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) && - check_branch_commit(bname.buf, name, &oid, head_rev, kinds, - force)) { - ret = 1; - goto next; - } - - if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : &oid, - REF_NO_DEREF)) { - error(remote_branch - ? _("Error deleting remote-tracking branch '%s'") - : _("Error deleting branch '%s'"), - bname.buf); - ret = 1; - goto next; - } - if (!quiet) { - printf(remote_branch - ? _("Deleted remote-tracking branch %s (was %s).\n") - : _("Deleted branch %s (was %s).\n"), - bname.buf, - (flags & REF_ISBROKEN) ? "broken" - : (flags & REF_ISSYMREF) ? target - : find_unique_abbrev(&oid, DEFAULT_ABBREV)); - } - delete_branch_config(bname.buf); - - next: - free(target); - } - - free(name); - strbuf_release(&bname); - - return ret; -} - -static int calc_maxwidth(struct ref_array *refs, int remote_bonus) -{ - int i, max = 0; - for (i = 0; i < refs->nr; i++) { - struct ref_array_item *it = refs->items[i]; - const char *desc = it->refname; - int w; - - skip_prefix(it->refname, "refs/heads/", &desc); - skip_prefix(it->refname, "refs/remotes/", &desc); - if (it->kind == FILTER_REFS_DETACHED_HEAD) { - char *head_desc = get_head_description(); - w = utf8_strwidth(head_desc); - free(head_desc); - } else - w = utf8_strwidth(desc); - - if (it->kind == FILTER_REFS_REMOTES) - w += remote_bonus; - if (w > max) - max = w; - } - return max; -} - -static const char *quote_literal_for_format(const char *s) -{ - static struct strbuf buf = STRBUF_INIT; - - strbuf_reset(&buf); - while (*s) { - const char *ep = strchrnul(s, '%'); - if (s < ep) - strbuf_add(&buf, s, ep - s); - if (*ep == '%') { - strbuf_addstr(&buf, "%%"); - s = ep + 1; - } else { - s = ep; - } - } - return buf.buf; -} - -static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix) -{ - struct strbuf fmt = STRBUF_INIT; - struct strbuf local = STRBUF_INIT; - struct strbuf remote = STRBUF_INIT; - - strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)", - branch_get_color(BRANCH_COLOR_CURRENT), - branch_get_color(BRANCH_COLOR_WORKTREE), - branch_get_color(BRANCH_COLOR_LOCAL)); - strbuf_addf(&remote, " %s", - branch_get_color(BRANCH_COLOR_REMOTE)); - - if (filter->verbose) { - struct strbuf obname = STRBUF_INIT; - - if (filter->abbrev < 0) - strbuf_addf(&obname, "%%(objectname:short)"); - else if (!filter->abbrev) - strbuf_addf(&obname, "%%(objectname)"); - else - strbuf_addf(&obname, "%%(objectname:short=%d)", filter->abbrev); - - strbuf_addf(&local, "%%(align:%d,left)%%(refname:lstrip=2)%%(end)", maxwidth); - strbuf_addstr(&local, branch_get_color(BRANCH_COLOR_RESET)); - strbuf_addf(&local, " %s ", obname.buf); - - if (filter->verbose > 1) - { - strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)", - branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET)); - strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)" - "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)", - branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET)); - } - else - strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)"); - - strbuf_addf(&remote, "%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s" - "%%(if)%%(symref)%%(then) -> %%(symref:short)" - "%%(else) %s %%(contents:subject)%%(end)", - maxwidth, quote_literal_for_format(remote_prefix), - branch_get_color(BRANCH_COLOR_RESET), obname.buf); - strbuf_release(&obname); - } else { - strbuf_addf(&local, "%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)", - branch_get_color(BRANCH_COLOR_RESET)); - strbuf_addf(&remote, "%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)", - quote_literal_for_format(remote_prefix), - branch_get_color(BRANCH_COLOR_RESET)); - } - - strbuf_addf(&fmt, "%%(if:notequals=refs/remotes)%%(refname:rstrip=-2)%%(then)%s%%(else)%s%%(end)", local.buf, remote.buf); - - strbuf_release(&local); - strbuf_release(&remote); - return strbuf_detach(&fmt, NULL); -} - -static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, struct ref_format *format) -{ - int i; - struct ref_array array; - int maxwidth = 0; - const char *remote_prefix = ""; - char *to_free = NULL; - - /* - * If we are listing more than just remote branches, - * then remote branches will have a "remotes/" prefix. - * We need to account for this in the width. - */ - if (filter->kind != FILTER_REFS_REMOTES) - remote_prefix = "remotes/"; - - memset(&array, 0, sizeof(array)); - - filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN); - - if (filter->verbose) - maxwidth = calc_maxwidth(&array, strlen(remote_prefix)); - - if (!format->format) - format->format = to_free = build_format(filter, maxwidth, remote_prefix); - format->use_color = branch_use_color; - - if (verify_ref_format(format)) - die(_("unable to parse format string")); - - ref_array_sort(sorting, &array); - - for (i = 0; i < array.nr; i++) { - struct strbuf out = STRBUF_INIT; - struct strbuf err = STRBUF_INIT; - if (format_ref_array_item(array.items[i], format, &out, &err)) - die("%s", err.buf); - if (column_active(colopts)) { - assert(!filter->verbose && "--column and --verbose are incompatible"); - /* format to a string_list to let print_columns() do its job */ - string_list_append(&output, out.buf); - } else { - fwrite(out.buf, 1, out.len, stdout); - putchar('\n'); - } - strbuf_release(&err); - strbuf_release(&out); - } - - ref_array_clear(&array); - free(to_free); -} - -static void print_current_branch_name(void) -{ - int flags; - const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, &flags); - const char *shortname; - if (!refname) - die(_("could not resolve HEAD")); - else if (!(flags & REF_ISSYMREF)) - return; - else if (skip_prefix(refname, "refs/heads/", &shortname)) - puts(shortname); - else - die(_("HEAD (%s) points outside of refs/heads/"), refname); -} - -static void reject_rebase_or_bisect_branch(const char *target) -{ - struct worktree **worktrees = get_worktrees(); - int i; - - for (i = 0; worktrees[i]; i++) { - struct worktree *wt = worktrees[i]; - - if (!wt->is_detached) - continue; - - if (is_worktree_being_rebased(wt, target)) - die(_("Branch %s is being rebased at %s"), - target, wt->path); - - if (is_worktree_being_bisected(wt, target)) - die(_("Branch %s is being bisected at %s"), - target, wt->path); - } - - free_worktrees(worktrees); -} - -static void copy_or_rename_branch(const char *oldname, const char *newname, int copy, int force) -{ - struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT; - struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT; - const char *interpreted_oldname = NULL; - const char *interpreted_newname = NULL; - int recovery = 0; - - if (!oldname) { - if (copy) - die(_("cannot copy the current branch while not on any.")); - else - die(_("cannot rename the current branch while not on any.")); - } - - if (strbuf_check_branch_ref(&oldref, oldname)) { - /* - * Bad name --- this could be an attempt to rename a - * ref that we used to allow to be created by accident. - */ - if (ref_exists(oldref.buf)) - recovery = 1; - else - die(_("Invalid branch name: '%s'"), oldname); - } - - /* - * A command like "git branch -M currentbranch currentbranch" cannot - * cause the worktree to become inconsistent with HEAD, so allow it. - */ - if (!strcmp(oldname, newname)) - validate_branchname(newname, &newref); - else - validate_new_branchname(newname, &newref, force); - - reject_rebase_or_bisect_branch(oldref.buf); - - if (!skip_prefix(oldref.buf, "refs/heads/", &interpreted_oldname) || - !skip_prefix(newref.buf, "refs/heads/", &interpreted_newname)) { - BUG("expected prefix missing for refs"); - } - - if (copy) - strbuf_addf(&logmsg, "Branch: copied %s to %s", - oldref.buf, newref.buf); - else - strbuf_addf(&logmsg, "Branch: renamed %s to %s", - oldref.buf, newref.buf); - - if (!copy && rename_ref(oldref.buf, newref.buf, logmsg.buf)) - die(_("Branch rename failed")); - if (copy && copy_existing_ref(oldref.buf, newref.buf, logmsg.buf)) - die(_("Branch copy failed")); - - if (recovery) { - if (copy) - warning(_("Created a copy of a misnamed branch '%s'"), - interpreted_oldname); - else - warning(_("Renamed a misnamed branch '%s' away"), - interpreted_oldname); - } - - if (!copy && - replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf)) - die(_("Branch renamed to %s, but HEAD is not updated!"), newname); - - strbuf_release(&logmsg); - - strbuf_addf(&oldsection, "branch.%s", interpreted_oldname); - strbuf_release(&oldref); - strbuf_addf(&newsection, "branch.%s", interpreted_newname); - strbuf_release(&newref); - if (!copy && git_config_rename_section(oldsection.buf, newsection.buf) < 0) - die(_("Branch is renamed, but update of config-file failed")); - if (copy && strcmp(oldname, newname) && git_config_copy_section(oldsection.buf, newsection.buf) < 0) - die(_("Branch is copied, but update of config-file failed")); - strbuf_release(&oldsection); - strbuf_release(&newsection); -} - -static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION") - -static int edit_branch_description(const char *branch_name) -{ - struct strbuf buf = STRBUF_INIT; - struct strbuf name = STRBUF_INIT; - - read_branch_desc(&buf, branch_name); - if (!buf.len || buf.buf[buf.len-1] != '\n') - strbuf_addch(&buf, '\n'); - strbuf_commented_addf(&buf, - _("Please edit the description for the branch\n" - " %s\n" - "Lines starting with '%c' will be stripped.\n"), - branch_name, comment_line_char); - write_file_buf(edit_description(), buf.buf, buf.len); - strbuf_reset(&buf); - if (launch_editor(edit_description(), &buf, NULL)) { - strbuf_release(&buf); - return -1; - } - strbuf_stripspace(&buf, 1); - - strbuf_addf(&name, "branch.%s.description", branch_name); - git_config_set(name.buf, buf.len ? buf.buf : NULL); - strbuf_release(&name); - strbuf_release(&buf); - - return 0; -} - -int cmd_branch(int argc, const char **argv, const char *prefix) -{ - int delete = 0, rename = 0, copy = 0, force = 0, list = 0; - int show_current = 0; - int reflog = 0, edit_description = 0; - int quiet = 0, unset_upstream = 0; - const char *new_upstream = NULL; - enum branch_track track; - struct ref_filter filter; - int icase = 0; - static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; - struct ref_format format = REF_FORMAT_INIT; - - struct option options[] = { - OPT_GROUP(N_("Generic options")), - OPT__VERBOSE(&filter.verbose, - N_("show hash and subject, give twice for upstream branch")), - OPT__QUIET(&quiet, N_("suppress informational messages")), - OPT_SET_INT('t', "track", &track, N_("set up tracking mode (see git-pull(1))"), - BRANCH_TRACK_EXPLICIT), - OPT_SET_INT_F(0, "set-upstream", &track, N_("do not use"), - BRANCH_TRACK_OVERRIDE, PARSE_OPT_HIDDEN), - OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")), - OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("unset the upstream info")), - OPT__COLOR(&branch_use_color, N_("use colored output")), - OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"), - FILTER_REFS_REMOTES), - OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")), - OPT_NO_CONTAINS(&filter.no_commit, N_("print only branches that don't contain the commit")), - OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")), - OPT_WITHOUT(&filter.no_commit, N_("print only branches that don't contain the commit")), - OPT__ABBREV(&filter.abbrev), - - OPT_GROUP(N_("Specific git-branch actions:")), - OPT_SET_INT('a', "all", &filter.kind, N_("list both remote-tracking and local branches"), - FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES), - OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1), - OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2), - OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1), - OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2), - OPT_BIT('c', "copy", ©, N_("copy a branch and its reflog"), 1), - OPT_BIT('C', NULL, ©, N_("copy a branch, even if target exists"), 2), - OPT_BOOL('l', "list", &list, N_("list branch names")), - OPT_BOOL(0, "show-current", &show_current, N_("show current branch name")), - OPT_BOOL(0, "create-reflog", &reflog, N_("create the branch's reflog")), - OPT_BOOL(0, "edit-description", &edit_description, - N_("edit the description for the branch")), - OPT__FORCE(&force, N_("force creation, move/rename, deletion"), PARSE_OPT_NOCOMPLETE), - OPT_MERGED(&filter, N_("print only branches that are merged")), - OPT_NO_MERGED(&filter, N_("print only branches that are not merged")), - OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")), - OPT_REF_SORT(sorting_tail), - OPT_CALLBACK(0, "points-at", &filter.points_at, N_("object"), - N_("print only branches of the object"), parse_opt_object_name), - OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")), - OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")), - OPT_END(), - }; - - setup_ref_filter_porcelain_msg(); - - memset(&filter, 0, sizeof(filter)); - filter.kind = FILTER_REFS_BRANCHES; - filter.abbrev = -1; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage_with_options(builtin_branch_usage, options); - - git_config(git_branch_config, sorting_tail); - - track = git_branch_track; - - head = resolve_refdup("HEAD", 0, &head_oid, NULL); - if (!head) - die(_("Failed to resolve HEAD as a valid ref.")); - if (!strcmp(head, "HEAD")) - filter.detached = 1; - else if (!skip_prefix(head, "refs/heads/", &head)) - die(_("HEAD not found below refs/heads!")); - - argc = parse_options(argc, argv, prefix, options, builtin_branch_usage, - 0); - - if (!delete && !rename && !copy && !edit_description && !new_upstream && - !show_current && !unset_upstream && argc == 0) - list = 1; - - if (filter.with_commit || filter.no_commit || - filter.reachable_from || filter.unreachable_from || filter.points_at.nr) - list = 1; - - if (!!delete + !!rename + !!copy + !!new_upstream + !!show_current + - list + edit_description + unset_upstream > 1) - usage_with_options(builtin_branch_usage, options); - - if (filter.abbrev == -1) - filter.abbrev = DEFAULT_ABBREV; - filter.ignore_case = icase; - - finalize_colopts(&colopts, -1); - if (filter.verbose) { - if (explicitly_enable_column(colopts)) - die(_("--column and --verbose are incompatible")); - colopts = 0; - } - - if (force) { - delete *= 2; - rename *= 2; - copy *= 2; - } - - if (list) - setup_auto_pager("branch", 1); - - if (delete) { - if (!argc) - die(_("branch name required")); - return delete_branches(argc, argv, delete > 1, filter.kind, quiet); - } else if (show_current) { - print_current_branch_name(); - return 0; - } else if (list) { - /* git branch --local also shows HEAD when it is detached */ - if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached) - filter.kind |= FILTER_REFS_DETACHED_HEAD; - filter.name_patterns = argv; - /* - * If no sorting parameter is given then we default to sorting - * by 'refname'. This would give us an alphabetically sorted - * array with the 'HEAD' ref at the beginning followed by - * local branches 'refs/heads/...' and finally remote-tracking - * branches 'refs/remotes/...'. - */ - if (!sorting) - sorting = ref_default_sorting(); - ref_sorting_icase_all(sorting, icase); - print_ref_list(&filter, sorting, &format); - print_columns(&output, colopts, NULL); - string_list_clear(&output, 0); - return 0; - } else if (edit_description) { - const char *branch_name; - struct strbuf branch_ref = STRBUF_INIT; - - if (!argc) { - if (filter.detached) - die(_("Cannot give description to detached HEAD")); - branch_name = head; - } else if (argc == 1) - branch_name = argv[0]; - else - die(_("cannot edit description of more than one branch")); - - strbuf_addf(&branch_ref, "refs/heads/%s", branch_name); - if (!ref_exists(branch_ref.buf)) { - strbuf_release(&branch_ref); - - if (!argc) - return error(_("No commit on branch '%s' yet."), - branch_name); - else - return error(_("No branch named '%s'."), - branch_name); - } - strbuf_release(&branch_ref); - - if (edit_branch_description(branch_name)) - return 1; - } else if (copy) { - if (!argc) - die(_("branch name required")); - else if (argc == 1) - copy_or_rename_branch(head, argv[0], 1, copy > 1); - else if (argc == 2) - copy_or_rename_branch(argv[0], argv[1], 1, copy > 1); - else - die(_("too many branches for a copy operation")); - } else if (rename) { - if (!argc) - die(_("branch name required")); - else if (argc == 1) - copy_or_rename_branch(head, argv[0], 0, rename > 1); - else if (argc == 2) - copy_or_rename_branch(argv[0], argv[1], 0, rename > 1); - else - die(_("too many arguments for a rename operation")); - } else if (new_upstream) { - struct branch *branch = branch_get(argv[0]); - - if (argc > 1) - die(_("too many arguments to set new upstream")); - - if (!branch) { - if (!argc || !strcmp(argv[0], "HEAD")) - die(_("could not set upstream of HEAD to %s when " - "it does not point to any branch."), - new_upstream); - die(_("no such branch '%s'"), argv[0]); - } - - if (!ref_exists(branch->refname)) - die(_("branch '%s' does not exist"), branch->name); - - /* - * create_branch takes care of setting up the tracking - * info and making sure new_upstream is correct - */ - create_branch(the_repository, branch->name, new_upstream, - 0, 0, 0, quiet, BRANCH_TRACK_OVERRIDE); - } else if (unset_upstream) { - struct branch *branch = branch_get(argv[0]); - struct strbuf buf = STRBUF_INIT; - - if (argc > 1) - die(_("too many arguments to unset upstream")); - - if (!branch) { - if (!argc || !strcmp(argv[0], "HEAD")) - die(_("could not unset upstream of HEAD when " - "it does not point to any branch.")); - die(_("no such branch '%s'"), argv[0]); - } - - if (!branch_has_merge_config(branch)) - die(_("Branch '%s' has no upstream information"), branch->name); - - strbuf_addf(&buf, "branch.%s.remote", branch->name); - git_config_set_multivar(buf.buf, NULL, NULL, 1); - strbuf_reset(&buf); - strbuf_addf(&buf, "branch.%s.merge", branch->name); - git_config_set_multivar(buf.buf, NULL, NULL, 1); - strbuf_release(&buf); - } else if (argc > 0 && argc <= 2) { - if (filter.kind != FILTER_REFS_BRANCHES) - die(_("The -a, and -r, options to 'git branch' do not take a branch name.\n" - "Did you mean to use: -a|-r --list <pattern>?")); - - if (track == BRANCH_TRACK_OVERRIDE) - die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead.")); - - create_branch(the_repository, - argv[0], (argc == 2) ? argv[1] : head, - force, 0, reflog, quiet, track); - - } else - usage_with_options(builtin_branch_usage, options); - - return 0; -} diff --git a/third_party/git/builtin/bugreport.c b/third_party/git/builtin/bugreport.c deleted file mode 100644 index 3ad4b9b62e84..000000000000 --- a/third_party/git/builtin/bugreport.c +++ /dev/null @@ -1,194 +0,0 @@ -#include "builtin.h" -#include "parse-options.h" -#include "strbuf.h" -#include "help.h" -#include "compat/compiler.h" -#include "run-command.h" - - -static void get_system_info(struct strbuf *sys_info) -{ - struct utsname uname_info; - char *shell = NULL; - - /* get git version from native cmd */ - strbuf_addstr(sys_info, _("git version:\n")); - get_version_info(sys_info, 1); - - /* system call for other version info */ - strbuf_addstr(sys_info, "uname: "); - if (uname(&uname_info)) - strbuf_addf(sys_info, _("uname() failed with error '%s' (%d)\n"), - strerror(errno), - errno); - else - strbuf_addf(sys_info, "%s %s %s %s\n", - uname_info.sysname, - uname_info.release, - uname_info.version, - uname_info.machine); - - strbuf_addstr(sys_info, _("compiler info: ")); - get_compiler_info(sys_info); - - strbuf_addstr(sys_info, _("libc info: ")); - get_libc_info(sys_info); - - shell = getenv("SHELL"); - strbuf_addf(sys_info, "$SHELL (typically, interactive shell): %s\n", - shell ? shell : "<unset>"); -} - -static void get_populated_hooks(struct strbuf *hook_info, int nongit) -{ - /* - * NEEDSWORK: Doesn't look like there is a list of all possible hooks; - * so below is a transcription of `git help hooks`. Later, this should - * be replaced with some programmatically generated list (generated from - * doc or else taken from some library which tells us about all the - * hooks) - */ - static const char *hook[] = { - "applypatch-msg", - "pre-applypatch", - "post-applypatch", - "pre-commit", - "pre-merge-commit", - "prepare-commit-msg", - "commit-msg", - "post-commit", - "pre-rebase", - "post-checkout", - "post-merge", - "pre-push", - "pre-receive", - "update", - "post-receive", - "post-update", - "push-to-checkout", - "pre-auto-gc", - "post-rewrite", - "sendemail-validate", - "fsmonitor-watchman", - "p4-pre-submit", - "post-index-change", - }; - int i; - - if (nongit) { - strbuf_addstr(hook_info, - _("not run from a git repository - no hooks to show\n")); - return; - } - - for (i = 0; i < ARRAY_SIZE(hook); i++) - if (find_hook(hook[i])) - strbuf_addf(hook_info, "%s\n", hook[i]); -} - -static const char * const bugreport_usage[] = { - N_("git bugreport [-o|--output-directory <file>] [-s|--suffix <format>]"), - NULL -}; - -static int get_bug_template(struct strbuf *template) -{ - const char template_text[] = N_( -"Thank you for filling out a Git bug report!\n" -"Please answer the following questions to help us understand your issue.\n" -"\n" -"What did you do before the bug happened? (Steps to reproduce your issue)\n" -"\n" -"What did you expect to happen? (Expected behavior)\n" -"\n" -"What happened instead? (Actual behavior)\n" -"\n" -"What's different between what you expected and what actually happened?\n" -"\n" -"Anything else you want to add:\n" -"\n" -"Please review the rest of the bug report below.\n" -"You can delete any lines you don't wish to share.\n"); - - strbuf_addstr(template, _(template_text)); - return 0; -} - -static void get_header(struct strbuf *buf, const char *title) -{ - strbuf_addf(buf, "\n\n[%s]\n", title); -} - -int cmd_bugreport(int argc, const char **argv, const char *prefix) -{ - struct strbuf buffer = STRBUF_INIT; - struct strbuf report_path = STRBUF_INIT; - int report = -1; - time_t now = time(NULL); - char *option_output = NULL; - char *option_suffix = "%Y-%m-%d-%H%M"; - const char *user_relative_path = NULL; - - const struct option bugreport_options[] = { - OPT_STRING('o', "output-directory", &option_output, N_("path"), - N_("specify a destination for the bugreport file")), - OPT_STRING('s', "suffix", &option_suffix, N_("format"), - N_("specify a strftime format suffix for the filename")), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, bugreport_options, - bugreport_usage, 0); - - /* Prepare the path to put the result */ - strbuf_addstr(&report_path, - prefix_filename(prefix, - option_output ? option_output : "")); - strbuf_complete(&report_path, '/'); - - strbuf_addstr(&report_path, "git-bugreport-"); - strbuf_addftime(&report_path, option_suffix, localtime(&now), 0, 0); - strbuf_addstr(&report_path, ".txt"); - - switch (safe_create_leading_directories(report_path.buf)) { - case SCLD_OK: - case SCLD_EXISTS: - break; - default: - die(_("could not create leading directories for '%s'"), - report_path.buf); - } - - /* Prepare the report contents */ - get_bug_template(&buffer); - - get_header(&buffer, _("System Info")); - get_system_info(&buffer); - - get_header(&buffer, _("Enabled Hooks")); - get_populated_hooks(&buffer, !startup_info->have_repository); - - /* fopen doesn't offer us an O_EXCL alternative, except with glibc. */ - report = open(report_path.buf, O_CREAT | O_EXCL | O_WRONLY, 0666); - - if (report < 0) - die(_("couldn't create a new file at '%s'"), report_path.buf); - - if (write_in_full(report, buffer.buf, buffer.len) < 0) - die_errno(_("unable to write to %s"), report_path.buf); - - close(report); - - /* - * We want to print the path relative to the user, but we still need the - * path relative to us to give to the editor. - */ - if (!(prefix && skip_prefix(report_path.buf, prefix, &user_relative_path))) - user_relative_path = report_path.buf; - fprintf(stderr, _("Created new report at '%s'.\n"), - user_relative_path); - - UNLEAK(buffer); - UNLEAK(report_path); - return !!launch_editor(report_path.buf, NULL, NULL); -} diff --git a/third_party/git/builtin/bundle.c b/third_party/git/builtin/bundle.c deleted file mode 100644 index ea6948110b0f..000000000000 --- a/third_party/git/builtin/bundle.c +++ /dev/null @@ -1,197 +0,0 @@ -#include "builtin.h" -#include "strvec.h" -#include "parse-options.h" -#include "cache.h" -#include "bundle.h" - -/* - * Basic handler for bundle files to connect repositories via sneakernet. - * Invocation must include action. - * This function can create a bundle or provide information on an existing - * bundle supporting "fetch", "pull", and "ls-remote". - */ - -static const char * const builtin_bundle_usage[] = { - N_("git bundle create [<options>] <file> <git-rev-list args>"), - N_("git bundle verify [<options>] <file>"), - N_("git bundle list-heads <file> [<refname>...]"), - N_("git bundle unbundle <file> [<refname>...]"), - NULL -}; - -static const char * const builtin_bundle_create_usage[] = { - N_("git bundle create [<options>] <file> <git-rev-list args>"), - NULL -}; - -static const char * const builtin_bundle_verify_usage[] = { - N_("git bundle verify [<options>] <file>"), - NULL -}; - -static const char * const builtin_bundle_list_heads_usage[] = { - N_("git bundle list-heads <file> [<refname>...]"), - NULL -}; - -static const char * const builtin_bundle_unbundle_usage[] = { - N_("git bundle unbundle <file> [<refname>...]"), - NULL -}; - -static int verbose; - -static int parse_options_cmd_bundle(int argc, - const char **argv, - const char* prefix, - const char * const usagestr[], - const struct option options[], - const char **bundle_file) { - int newargc; - newargc = parse_options(argc, argv, NULL, options, usagestr, - PARSE_OPT_STOP_AT_NON_OPTION); - if (argc < 1) - usage_with_options(usagestr, options); - *bundle_file = prefix_filename(prefix, argv[0]); - return newargc; -} - -static int cmd_bundle_create(int argc, const char **argv, const char *prefix) { - int all_progress_implied = 0; - int progress = isatty(STDERR_FILENO); - struct strvec pack_opts; - int version = -1; - - struct option options[] = { - OPT_SET_INT('q', "quiet", &progress, - N_("do not show progress meter"), 0), - OPT_SET_INT(0, "progress", &progress, - N_("show progress meter"), 1), - OPT_SET_INT(0, "all-progress", &progress, - N_("show progress meter during object writing phase"), 2), - OPT_BOOL(0, "all-progress-implied", - &all_progress_implied, - N_("similar to --all-progress when progress meter is shown")), - OPT_INTEGER(0, "version", &version, - N_("specify bundle format version")), - OPT_END() - }; - const char* bundle_file; - - argc = parse_options_cmd_bundle(argc, argv, prefix, - builtin_bundle_create_usage, options, &bundle_file); - /* bundle internals use argv[1] as further parameters */ - - strvec_init(&pack_opts); - if (progress == 0) - strvec_push(&pack_opts, "--quiet"); - else if (progress == 1) - strvec_push(&pack_opts, "--progress"); - else if (progress == 2) - strvec_push(&pack_opts, "--all-progress"); - if (progress && all_progress_implied) - strvec_push(&pack_opts, "--all-progress-implied"); - - if (!startup_info->have_repository) - die(_("Need a repository to create a bundle.")); - return !!create_bundle(the_repository, bundle_file, argc, argv, &pack_opts, version); -} - -static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) { - struct bundle_header header; - int bundle_fd = -1; - int quiet = 0; - - struct option options[] = { - OPT_BOOL('q', "quiet", &quiet, - N_("do not show bundle details")), - OPT_END() - }; - const char* bundle_file; - - argc = parse_options_cmd_bundle(argc, argv, prefix, - builtin_bundle_verify_usage, options, &bundle_file); - /* bundle internals use argv[1] as further parameters */ - - memset(&header, 0, sizeof(header)); - if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) - return 1; - close(bundle_fd); - if (verify_bundle(the_repository, &header, !quiet)) - return 1; - fprintf(stderr, _("%s is okay\n"), bundle_file); - return 0; -} - -static int cmd_bundle_list_heads(int argc, const char **argv, const char *prefix) { - struct bundle_header header; - int bundle_fd = -1; - - struct option options[] = { - OPT_END() - }; - const char* bundle_file; - - argc = parse_options_cmd_bundle(argc, argv, prefix, - builtin_bundle_list_heads_usage, options, &bundle_file); - /* bundle internals use argv[1] as further parameters */ - - memset(&header, 0, sizeof(header)); - if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) - return 1; - close(bundle_fd); - return !!list_bundle_refs(&header, argc, argv); -} - -static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix) { - struct bundle_header header; - int bundle_fd = -1; - - struct option options[] = { - OPT_END() - }; - const char* bundle_file; - - argc = parse_options_cmd_bundle(argc, argv, prefix, - builtin_bundle_unbundle_usage, options, &bundle_file); - /* bundle internals use argv[1] as further parameters */ - - memset(&header, 0, sizeof(header)); - if ((bundle_fd = read_bundle_header(bundle_file, &header)) < 0) - return 1; - if (!startup_info->have_repository) - die(_("Need a repository to unbundle.")); - return !!unbundle(the_repository, &header, bundle_fd, 0) || - list_bundle_refs(&header, argc, argv); -} - -int cmd_bundle(int argc, const char **argv, const char *prefix) -{ - struct option options[] = { - OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")), - OPT_END() - }; - int result; - - argc = parse_options(argc, argv, prefix, options, builtin_bundle_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - - packet_trace_identity("bundle"); - - if (argc < 2) - usage_with_options(builtin_bundle_usage, options); - - else if (!strcmp(argv[0], "create")) - result = cmd_bundle_create(argc, argv, prefix); - else if (!strcmp(argv[0], "verify")) - result = cmd_bundle_verify(argc, argv, prefix); - else if (!strcmp(argv[0], "list-heads")) - result = cmd_bundle_list_heads(argc, argv, prefix); - else if (!strcmp(argv[0], "unbundle")) - result = cmd_bundle_unbundle(argc, argv, prefix); - else { - error(_("Unknown subcommand: %s"), argv[0]); - usage_with_options(builtin_bundle_usage, options); - } - return result ? 1 : 0; -} diff --git a/third_party/git/builtin/cat-file.c b/third_party/git/builtin/cat-file.c deleted file mode 100644 index 5ebf13359e83..000000000000 --- a/third_party/git/builtin/cat-file.c +++ /dev/null @@ -1,721 +0,0 @@ -/* - * GIT - The information manager from hell - * - * Copyright (C) Linus Torvalds, 2005 - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "builtin.h" -#include "diff.h" -#include "parse-options.h" -#include "userdiff.h" -#include "streaming.h" -#include "tree-walk.h" -#include "oid-array.h" -#include "packfile.h" -#include "object-store.h" -#include "promisor-remote.h" - -struct batch_options { - int enabled; - int follow_symlinks; - int print_contents; - int buffer_output; - int all_objects; - int unordered; - int cmdmode; /* may be 'w' or 'c' for --filters or --textconv */ - const char *format; -}; - -static const char *force_path; - -static int filter_object(const char *path, unsigned mode, - const struct object_id *oid, - char **buf, unsigned long *size) -{ - enum object_type type; - - *buf = read_object_file(oid, &type, size); - if (!*buf) - return error(_("cannot read object %s '%s'"), - oid_to_hex(oid), path); - if ((type == OBJ_BLOB) && S_ISREG(mode)) { - struct strbuf strbuf = STRBUF_INIT; - struct checkout_metadata meta; - - init_checkout_metadata(&meta, NULL, NULL, oid); - if (convert_to_working_tree(&the_index, path, *buf, *size, &strbuf, &meta)) { - free(*buf); - *size = strbuf.len; - *buf = strbuf_detach(&strbuf, NULL); - } - } - - return 0; -} - -static int stream_blob(const struct object_id *oid) -{ - if (stream_blob_to_fd(1, oid, NULL, 0)) - die("unable to stream %s to stdout", oid_to_hex(oid)); - return 0; -} - -static int cat_one_file(int opt, const char *exp_type, const char *obj_name, - int unknown_type) -{ - struct object_id oid; - enum object_type type; - char *buf; - unsigned long size; - struct object_context obj_context; - struct object_info oi = OBJECT_INFO_INIT; - struct strbuf sb = STRBUF_INIT; - unsigned flags = OBJECT_INFO_LOOKUP_REPLACE; - const char *path = force_path; - - if (unknown_type) - flags |= OBJECT_INFO_ALLOW_UNKNOWN_TYPE; - - if (get_oid_with_context(the_repository, obj_name, - GET_OID_RECORD_PATH, - &oid, &obj_context)) - die("Not a valid object name %s", obj_name); - - if (!path) - path = obj_context.path; - if (obj_context.mode == S_IFINVALID) - obj_context.mode = 0100644; - - buf = NULL; - switch (opt) { - case 't': - oi.type_name = &sb; - if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0) - die("git cat-file: could not get object info"); - if (sb.len) { - printf("%s\n", sb.buf); - strbuf_release(&sb); - return 0; - } - break; - - case 's': - oi.sizep = &size; - if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0) - die("git cat-file: could not get object info"); - printf("%"PRIuMAX"\n", (uintmax_t)size); - return 0; - - case 'e': - return !has_object_file(&oid); - - case 'w': - if (!path) - die("git cat-file --filters %s: <object> must be " - "<sha1:path>", obj_name); - - if (filter_object(path, obj_context.mode, - &oid, &buf, &size)) - return -1; - break; - - case 'c': - if (!path) - die("git cat-file --textconv %s: <object> must be <sha1:path>", - obj_name); - - if (textconv_object(the_repository, path, obj_context.mode, - &oid, 1, &buf, &size)) - break; - /* else fallthrough */ - - case 'p': - type = oid_object_info(the_repository, &oid, NULL); - if (type < 0) - die("Not a valid object name %s", obj_name); - - /* custom pretty-print here */ - if (type == OBJ_TREE) { - const char *ls_args[3] = { NULL }; - ls_args[0] = "ls-tree"; - ls_args[1] = obj_name; - return cmd_ls_tree(2, ls_args, NULL); - } - - if (type == OBJ_BLOB) - return stream_blob(&oid); - buf = read_object_file(&oid, &type, &size); - if (!buf) - die("Cannot read object %s", obj_name); - - /* otherwise just spit out the data */ - break; - - case 0: - if (type_from_string(exp_type) == OBJ_BLOB) { - struct object_id blob_oid; - if (oid_object_info(the_repository, &oid, NULL) == OBJ_TAG) { - char *buffer = read_object_file(&oid, &type, - &size); - const char *target; - if (!skip_prefix(buffer, "object ", &target) || - get_oid_hex(target, &blob_oid)) - die("%s not a valid tag", oid_to_hex(&oid)); - free(buffer); - } else - oidcpy(&blob_oid, &oid); - - if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB) - return stream_blob(&blob_oid); - /* - * we attempted to dereference a tag to a blob - * and failed; there may be new dereference - * mechanisms this code is not aware of. - * fall-back to the usual case. - */ - } - buf = read_object_with_reference(the_repository, - &oid, exp_type, &size, NULL); - break; - - default: - die("git cat-file: unknown option: %s", exp_type); - } - - if (!buf) - die("git cat-file %s: bad file", obj_name); - - write_or_die(1, buf, size); - free(buf); - free(obj_context.path); - return 0; -} - -struct expand_data { - struct object_id oid; - enum object_type type; - unsigned long size; - off_t disk_size; - const char *rest; - struct object_id delta_base_oid; - - /* - * If mark_query is true, we do not expand anything, but rather - * just mark the object_info with items we wish to query. - */ - int mark_query; - - /* - * Whether to split the input on whitespace before feeding it to - * get_sha1; this is decided during the mark_query phase based on - * whether we have a %(rest) token in our format. - */ - int split_on_whitespace; - - /* - * After a mark_query run, this object_info is set up to be - * passed to oid_object_info_extended. It will point to the data - * elements above, so you can retrieve the response from there. - */ - struct object_info info; - - /* - * This flag will be true if the requested batch format and options - * don't require us to call oid_object_info, which can then be - * optimized out. - */ - unsigned skip_object_info : 1; -}; - -static int is_atom(const char *atom, const char *s, int slen) -{ - int alen = strlen(atom); - return alen == slen && !memcmp(atom, s, alen); -} - -static void expand_atom(struct strbuf *sb, const char *atom, int len, - void *vdata) -{ - struct expand_data *data = vdata; - - if (is_atom("objectname", atom, len)) { - if (!data->mark_query) - strbuf_addstr(sb, oid_to_hex(&data->oid)); - } else if (is_atom("objecttype", atom, len)) { - if (data->mark_query) - data->info.typep = &data->type; - else - strbuf_addstr(sb, type_name(data->type)); - } else if (is_atom("objectsize", atom, len)) { - if (data->mark_query) - data->info.sizep = &data->size; - else - strbuf_addf(sb, "%"PRIuMAX , (uintmax_t)data->size); - } else if (is_atom("objectsize:disk", atom, len)) { - if (data->mark_query) - data->info.disk_sizep = &data->disk_size; - else - strbuf_addf(sb, "%"PRIuMAX, (uintmax_t)data->disk_size); - } else if (is_atom("rest", atom, len)) { - if (data->mark_query) - data->split_on_whitespace = 1; - else if (data->rest) - strbuf_addstr(sb, data->rest); - } else if (is_atom("deltabase", atom, len)) { - if (data->mark_query) - data->info.delta_base_oid = &data->delta_base_oid; - else - strbuf_addstr(sb, - oid_to_hex(&data->delta_base_oid)); - } else - die("unknown format element: %.*s", len, atom); -} - -static size_t expand_format(struct strbuf *sb, const char *start, void *data) -{ - const char *end; - - if (*start != '(') - return 0; - end = strchr(start + 1, ')'); - if (!end) - die("format element '%s' does not end in ')'", start); - - expand_atom(sb, start + 1, end - start - 1, data); - - return end - start + 1; -} - -static void batch_write(struct batch_options *opt, const void *data, int len) -{ - if (opt->buffer_output) { - if (fwrite(data, 1, len, stdout) != len) - die_errno("unable to write to stdout"); - } else - write_or_die(1, data, len); -} - -static void print_object_or_die(struct batch_options *opt, struct expand_data *data) -{ - const struct object_id *oid = &data->oid; - - assert(data->info.typep); - - if (data->type == OBJ_BLOB) { - if (opt->buffer_output) - fflush(stdout); - if (opt->cmdmode) { - char *contents; - unsigned long size; - - if (!data->rest) - die("missing path for '%s'", oid_to_hex(oid)); - - if (opt->cmdmode == 'w') { - if (filter_object(data->rest, 0100644, oid, - &contents, &size)) - die("could not convert '%s' %s", - oid_to_hex(oid), data->rest); - } else if (opt->cmdmode == 'c') { - enum object_type type; - if (!textconv_object(the_repository, - data->rest, 0100644, oid, - 1, &contents, &size)) - contents = read_object_file(oid, - &type, - &size); - if (!contents) - die("could not convert '%s' %s", - oid_to_hex(oid), data->rest); - } else - BUG("invalid cmdmode: %c", opt->cmdmode); - batch_write(opt, contents, size); - free(contents); - } else { - stream_blob(oid); - } - } - else { - enum object_type type; - unsigned long size; - void *contents; - - contents = read_object_file(oid, &type, &size); - if (!contents) - die("object %s disappeared", oid_to_hex(oid)); - if (type != data->type) - die("object %s changed type!?", oid_to_hex(oid)); - if (data->info.sizep && size != data->size) - die("object %s changed size!?", oid_to_hex(oid)); - - batch_write(opt, contents, size); - free(contents); - } -} - -static void batch_object_write(const char *obj_name, - struct strbuf *scratch, - struct batch_options *opt, - struct expand_data *data) -{ - if (!data->skip_object_info && - oid_object_info_extended(the_repository, &data->oid, &data->info, - OBJECT_INFO_LOOKUP_REPLACE) < 0) { - printf("%s missing\n", - obj_name ? obj_name : oid_to_hex(&data->oid)); - fflush(stdout); - return; - } - - strbuf_reset(scratch); - strbuf_expand(scratch, opt->format, expand_format, data); - strbuf_addch(scratch, '\n'); - batch_write(opt, scratch->buf, scratch->len); - - if (opt->print_contents) { - print_object_or_die(opt, data); - batch_write(opt, "\n", 1); - } -} - -static void batch_one_object(const char *obj_name, - struct strbuf *scratch, - struct batch_options *opt, - struct expand_data *data) -{ - struct object_context ctx; - int flags = opt->follow_symlinks ? GET_OID_FOLLOW_SYMLINKS : 0; - enum get_oid_result result; - - result = get_oid_with_context(the_repository, obj_name, - flags, &data->oid, &ctx); - if (result != FOUND) { - switch (result) { - case MISSING_OBJECT: - printf("%s missing\n", obj_name); - break; - case SHORT_NAME_AMBIGUOUS: - printf("%s ambiguous\n", obj_name); - break; - case DANGLING_SYMLINK: - printf("dangling %"PRIuMAX"\n%s\n", - (uintmax_t)strlen(obj_name), obj_name); - break; - case SYMLINK_LOOP: - printf("loop %"PRIuMAX"\n%s\n", - (uintmax_t)strlen(obj_name), obj_name); - break; - case NOT_DIR: - printf("notdir %"PRIuMAX"\n%s\n", - (uintmax_t)strlen(obj_name), obj_name); - break; - default: - BUG("unknown get_sha1_with_context result %d\n", - result); - break; - } - fflush(stdout); - return; - } - - if (ctx.mode == 0) { - printf("symlink %"PRIuMAX"\n%s\n", - (uintmax_t)ctx.symlink_path.len, - ctx.symlink_path.buf); - fflush(stdout); - return; - } - - batch_object_write(obj_name, scratch, opt, data); -} - -struct object_cb_data { - struct batch_options *opt; - struct expand_data *expand; - struct oidset *seen; - struct strbuf *scratch; -}; - -static int batch_object_cb(const struct object_id *oid, void *vdata) -{ - struct object_cb_data *data = vdata; - oidcpy(&data->expand->oid, oid); - batch_object_write(NULL, data->scratch, data->opt, data->expand); - return 0; -} - -static int collect_loose_object(const struct object_id *oid, - const char *path, - void *data) -{ - oid_array_append(data, oid); - return 0; -} - -static int collect_packed_object(const struct object_id *oid, - struct packed_git *pack, - uint32_t pos, - void *data) -{ - oid_array_append(data, oid); - return 0; -} - -static int batch_unordered_object(const struct object_id *oid, void *vdata) -{ - struct object_cb_data *data = vdata; - - if (oidset_insert(data->seen, oid)) - return 0; - - return batch_object_cb(oid, data); -} - -static int batch_unordered_loose(const struct object_id *oid, - const char *path, - void *data) -{ - return batch_unordered_object(oid, data); -} - -static int batch_unordered_packed(const struct object_id *oid, - struct packed_git *pack, - uint32_t pos, - void *data) -{ - return batch_unordered_object(oid, data); -} - -static int batch_objects(struct batch_options *opt) -{ - struct strbuf input = STRBUF_INIT; - struct strbuf output = STRBUF_INIT; - struct expand_data data; - int save_warning; - int retval = 0; - - if (!opt->format) - opt->format = "%(objectname) %(objecttype) %(objectsize)"; - - /* - * Expand once with our special mark_query flag, which will prime the - * object_info to be handed to oid_object_info_extended for each - * object. - */ - memset(&data, 0, sizeof(data)); - data.mark_query = 1; - strbuf_expand(&output, opt->format, expand_format, &data); - data.mark_query = 0; - strbuf_release(&output); - if (opt->cmdmode) - data.split_on_whitespace = 1; - - if (opt->all_objects) { - struct object_info empty = OBJECT_INFO_INIT; - if (!memcmp(&data.info, &empty, sizeof(empty))) - data.skip_object_info = 1; - } - - /* - * If we are printing out the object, then always fill in the type, - * since we will want to decide whether or not to stream. - */ - if (opt->print_contents) - data.info.typep = &data.type; - - if (opt->all_objects) { - struct object_cb_data cb; - - if (has_promisor_remote()) - warning("This repository uses promisor remotes. Some objects may not be loaded."); - - cb.opt = opt; - cb.expand = &data; - cb.scratch = &output; - - if (opt->unordered) { - struct oidset seen = OIDSET_INIT; - - cb.seen = &seen; - - for_each_loose_object(batch_unordered_loose, &cb, 0); - for_each_packed_object(batch_unordered_packed, &cb, - FOR_EACH_OBJECT_PACK_ORDER); - - oidset_clear(&seen); - } else { - struct oid_array sa = OID_ARRAY_INIT; - - for_each_loose_object(collect_loose_object, &sa, 0); - for_each_packed_object(collect_packed_object, &sa, 0); - - oid_array_for_each_unique(&sa, batch_object_cb, &cb); - - oid_array_clear(&sa); - } - - strbuf_release(&output); - return 0; - } - - /* - * We are going to call get_sha1 on a potentially very large number of - * objects. In most large cases, these will be actual object sha1s. The - * cost to double-check that each one is not also a ref (just so we can - * warn) ends up dwarfing the actual cost of the object lookups - * themselves. We can work around it by just turning off the warning. - */ - save_warning = warn_on_object_refname_ambiguity; - warn_on_object_refname_ambiguity = 0; - - while (strbuf_getline(&input, stdin) != EOF) { - if (data.split_on_whitespace) { - /* - * Split at first whitespace, tying off the beginning - * of the string and saving the remainder (or NULL) in - * data.rest. - */ - char *p = strpbrk(input.buf, " \t"); - if (p) { - while (*p && strchr(" \t", *p)) - *p++ = '\0'; - } - data.rest = p; - } - - batch_one_object(input.buf, &output, opt, &data); - } - - strbuf_release(&input); - strbuf_release(&output); - warn_on_object_refname_ambiguity = save_warning; - return retval; -} - -static const char * const cat_file_usage[] = { - N_("git cat-file (-t [--allow-unknown-type] | -s [--allow-unknown-type] | -e | -p | <type> | --textconv | --filters) [--path=<path>] <object>"), - N_("git cat-file (--batch[=<format>] | --batch-check[=<format>]) [--follow-symlinks] [--textconv | --filters]"), - NULL -}; - -static int git_cat_file_config(const char *var, const char *value, void *cb) -{ - if (userdiff_config(var, value) < 0) - return -1; - - return git_default_config(var, value, cb); -} - -static int batch_option_callback(const struct option *opt, - const char *arg, - int unset) -{ - struct batch_options *bo = opt->value; - - BUG_ON_OPT_NEG(unset); - - if (bo->enabled) { - return error(_("only one batch option may be specified")); - } - - bo->enabled = 1; - bo->print_contents = !strcmp(opt->long_name, "batch"); - bo->format = arg; - - return 0; -} - -int cmd_cat_file(int argc, const char **argv, const char *prefix) -{ - int opt = 0; - const char *exp_type = NULL, *obj_name = NULL; - struct batch_options batch = {0}; - int unknown_type = 0; - - const struct option options[] = { - OPT_GROUP(N_("<type> can be one of: blob, tree, commit, tag")), - OPT_CMDMODE('t', NULL, &opt, N_("show object type"), 't'), - OPT_CMDMODE('s', NULL, &opt, N_("show object size"), 's'), - OPT_CMDMODE('e', NULL, &opt, - N_("exit with zero when there's no error"), 'e'), - OPT_CMDMODE('p', NULL, &opt, N_("pretty-print object's content"), 'p'), - OPT_CMDMODE(0, "textconv", &opt, - N_("for blob objects, run textconv on object's content"), 'c'), - OPT_CMDMODE(0, "filters", &opt, - N_("for blob objects, run filters on object's content"), 'w'), - OPT_STRING(0, "path", &force_path, N_("blob"), - N_("use a specific path for --textconv/--filters")), - OPT_BOOL(0, "allow-unknown-type", &unknown_type, - N_("allow -s and -t to work with broken/corrupt objects")), - OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")), - OPT_CALLBACK_F(0, "batch", &batch, "format", - N_("show info and content of objects fed from the standard input"), - PARSE_OPT_OPTARG | PARSE_OPT_NONEG, - batch_option_callback), - OPT_CALLBACK_F(0, "batch-check", &batch, "format", - N_("show info about objects fed from the standard input"), - PARSE_OPT_OPTARG | PARSE_OPT_NONEG, - batch_option_callback), - OPT_BOOL(0, "follow-symlinks", &batch.follow_symlinks, - N_("follow in-tree symlinks (used with --batch or --batch-check)")), - OPT_BOOL(0, "batch-all-objects", &batch.all_objects, - N_("show all objects with --batch or --batch-check")), - OPT_BOOL(0, "unordered", &batch.unordered, - N_("do not order --batch-all-objects output")), - OPT_END() - }; - - git_config(git_cat_file_config, NULL); - - batch.buffer_output = -1; - argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0); - - if (opt) { - if (batch.enabled && (opt == 'c' || opt == 'w')) - batch.cmdmode = opt; - else if (argc == 1) - obj_name = argv[0]; - else - usage_with_options(cat_file_usage, options); - } - if (!opt && !batch.enabled) { - if (argc == 2) { - exp_type = argv[0]; - obj_name = argv[1]; - } else - usage_with_options(cat_file_usage, options); - } - if (batch.enabled) { - if (batch.cmdmode != opt || argc) - usage_with_options(cat_file_usage, options); - if (batch.cmdmode && batch.all_objects) - die("--batch-all-objects cannot be combined with " - "--textconv nor with --filters"); - } - - if ((batch.follow_symlinks || batch.all_objects) && !batch.enabled) { - usage_with_options(cat_file_usage, options); - } - - if (force_path && opt != 'c' && opt != 'w') { - error("--path=<path> needs --textconv or --filters"); - usage_with_options(cat_file_usage, options); - } - - if (force_path && batch.enabled) { - error("--path=<path> incompatible with --batch"); - usage_with_options(cat_file_usage, options); - } - - if (batch.buffer_output < 0) - batch.buffer_output = batch.all_objects; - - if (batch.enabled) - return batch_objects(&batch); - - if (unknown_type && opt != 't' && opt != 's') - die("git cat-file --allow-unknown-type: use with -s or -t"); - return cat_one_file(opt, exp_type, obj_name, unknown_type); -} diff --git a/third_party/git/builtin/check-attr.c b/third_party/git/builtin/check-attr.c deleted file mode 100644 index dd833977864d..000000000000 --- a/third_party/git/builtin/check-attr.c +++ /dev/null @@ -1,189 +0,0 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "attr.h" -#include "quote.h" -#include "parse-options.h" - -static int all_attrs; -static int cached_attrs; -static int stdin_paths; -static const char * const check_attr_usage[] = { -N_("git check-attr [-a | --all | <attr>...] [--] <pathname>..."), -N_("git check-attr --stdin [-z] [-a | --all | <attr>...]"), -NULL -}; - -static int nul_term_line; - -static const struct option check_attr_options[] = { - OPT_BOOL('a', "all", &all_attrs, N_("report all attributes set on file")), - OPT_BOOL(0, "cached", &cached_attrs, N_("use .gitattributes only from the index")), - OPT_BOOL(0 , "stdin", &stdin_paths, N_("read file names from stdin")), - OPT_BOOL('z', NULL, &nul_term_line, - N_("terminate input and output records by a NUL character")), - OPT_END() -}; - -static void output_attr(struct attr_check *check, const char *file) -{ - int j; - int cnt = check->nr; - - for (j = 0; j < cnt; j++) { - const char *value = check->items[j].value; - - if (ATTR_TRUE(value)) - value = "set"; - else if (ATTR_FALSE(value)) - value = "unset"; - else if (ATTR_UNSET(value)) - value = "unspecified"; - - if (nul_term_line) { - printf("%s%c" /* path */ - "%s%c" /* attrname */ - "%s%c" /* attrvalue */, - file, 0, - git_attr_name(check->items[j].attr), 0, value, 0); - } else { - quote_c_style(file, NULL, stdout, 0); - printf(": %s: %s\n", - git_attr_name(check->items[j].attr), value); - } - } -} - -static void check_attr(const char *prefix, - struct attr_check *check, - int collect_all, - const char *file) -{ - char *full_path = - prefix_path(prefix, prefix ? strlen(prefix) : 0, file); - - if (collect_all) { - git_all_attrs(&the_index, full_path, check); - } else { - git_check_attr(&the_index, full_path, check); - } - output_attr(check, file); - - free(full_path); -} - -static void check_attr_stdin_paths(const char *prefix, - struct attr_check *check, - int collect_all) -{ - struct strbuf buf = STRBUF_INIT; - struct strbuf unquoted = STRBUF_INIT; - strbuf_getline_fn getline_fn; - - getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; - while (getline_fn(&buf, stdin) != EOF) { - if (!nul_term_line && buf.buf[0] == '"') { - strbuf_reset(&unquoted); - if (unquote_c_style(&unquoted, buf.buf, NULL)) - die("line is badly quoted"); - strbuf_swap(&buf, &unquoted); - } - check_attr(prefix, check, collect_all, buf.buf); - maybe_flush_or_die(stdout, "attribute to stdout"); - } - strbuf_release(&buf); - strbuf_release(&unquoted); -} - -static NORETURN void error_with_usage(const char *msg) -{ - error("%s", msg); - usage_with_options(check_attr_usage, check_attr_options); -} - -int cmd_check_attr(int argc, const char **argv, const char *prefix) -{ - struct attr_check *check; - int cnt, i, doubledash, filei; - - if (!is_bare_repository()) - setup_work_tree(); - - git_config(git_default_config, NULL); - - argc = parse_options(argc, argv, prefix, check_attr_options, - check_attr_usage, PARSE_OPT_KEEP_DASHDASH); - - if (read_cache() < 0) { - die("invalid cache"); - } - - if (cached_attrs) - git_attr_set_direction(GIT_ATTR_INDEX); - - doubledash = -1; - for (i = 0; doubledash < 0 && i < argc; i++) { - if (!strcmp(argv[i], "--")) - doubledash = i; - } - - /* Process --all and/or attribute arguments: */ - if (all_attrs) { - if (doubledash >= 1) - error_with_usage("Attributes and --all both specified"); - - cnt = 0; - filei = doubledash + 1; - } else if (doubledash == 0) { - error_with_usage("No attribute specified"); - } else if (doubledash < 0) { - if (!argc) - error_with_usage("No attribute specified"); - - if (stdin_paths) { - /* Treat all arguments as attribute names. */ - cnt = argc; - filei = argc; - } else { - /* Treat exactly one argument as an attribute name. */ - cnt = 1; - filei = 1; - } - } else { - cnt = doubledash; - filei = doubledash + 1; - } - - /* Check file argument(s): */ - if (stdin_paths) { - if (filei < argc) - error_with_usage("Can't specify files with --stdin"); - } else { - if (filei >= argc) - error_with_usage("No file specified"); - } - - check = attr_check_alloc(); - if (!all_attrs) { - for (i = 0; i < cnt; i++) { - const struct git_attr *a = git_attr(argv[i]); - - if (!a) - return error("%s: not a valid attribute name", - argv[i]); - attr_check_append(check, a); - } - } - - if (stdin_paths) - check_attr_stdin_paths(prefix, check, all_attrs); - else { - for (i = filei; i < argc; i++) - check_attr(prefix, check, all_attrs, argv[i]); - maybe_flush_or_die(stdout, "attribute to stdout"); - } - - attr_check_free(check); - return 0; -} diff --git a/third_party/git/builtin/check-ignore.c b/third_party/git/builtin/check-ignore.c deleted file mode 100644 index 3c652748d58c..000000000000 --- a/third_party/git/builtin/check-ignore.c +++ /dev/null @@ -1,196 +0,0 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "dir.h" -#include "quote.h" -#include "pathspec.h" -#include "parse-options.h" -#include "submodule.h" - -static int quiet, verbose, stdin_paths, show_non_matching, no_index; -static const char * const check_ignore_usage[] = { -"git check-ignore [<options>] <pathname>...", -"git check-ignore [<options>] --stdin", -NULL -}; - -static int nul_term_line; - -static const struct option check_ignore_options[] = { - OPT__QUIET(&quiet, N_("suppress progress reporting")), - OPT__VERBOSE(&verbose, N_("be verbose")), - OPT_GROUP(""), - OPT_BOOL(0, "stdin", &stdin_paths, - N_("read file names from stdin")), - OPT_BOOL('z', NULL, &nul_term_line, - N_("terminate input and output records by a NUL character")), - OPT_BOOL('n', "non-matching", &show_non_matching, - N_("show non-matching input paths")), - OPT_BOOL(0, "no-index", &no_index, - N_("ignore index when checking")), - OPT_END() -}; - -static void output_pattern(const char *path, struct path_pattern *pattern) -{ - char *bang = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE) ? "!" : ""; - char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : ""; - if (!nul_term_line) { - if (!verbose) { - write_name_quoted(path, stdout, '\n'); - } else { - if (pattern) { - quote_c_style(pattern->pl->src, NULL, stdout, 0); - printf(":%d:%s%s%s\t", - pattern->srcpos, - bang, pattern->pattern, slash); - } - else { - printf("::\t"); - } - quote_c_style(path, NULL, stdout, 0); - fputc('\n', stdout); - } - } else { - if (!verbose) { - printf("%s%c", path, '\0'); - } else { - if (pattern) - printf("%s%c%d%c%s%s%s%c%s%c", - pattern->pl->src, '\0', - pattern->srcpos, '\0', - bang, pattern->pattern, slash, '\0', - path, '\0'); - else - printf("%c%c%c%s%c", '\0', '\0', '\0', path, '\0'); - } - } -} - -static int check_ignore(struct dir_struct *dir, - const char *prefix, int argc, const char **argv) -{ - const char *full_path; - char *seen; - int num_ignored = 0, i; - struct path_pattern *pattern; - struct pathspec pathspec; - - if (!argc) { - if (!quiet) - fprintf(stderr, "no pathspec given.\n"); - return 0; - } - - /* - * check-ignore just needs paths. Magic beyond :/ is really - * irrelevant. - */ - parse_pathspec(&pathspec, - PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP, - PATHSPEC_SYMLINK_LEADING_PATH | - PATHSPEC_KEEP_ORDER, - prefix, argv); - - die_path_inside_submodule(&the_index, &pathspec); - - /* - * look for pathspecs matching entries in the index, since these - * should not be ignored, in order to be consistent with - * 'git status', 'git add' etc. - */ - seen = find_pathspecs_matching_against_index(&pathspec, &the_index); - for (i = 0; i < pathspec.nr; i++) { - full_path = pathspec.items[i].match; - pattern = NULL; - if (!seen[i]) { - int dtype = DT_UNKNOWN; - pattern = last_matching_pattern(dir, &the_index, - full_path, &dtype); - if (!verbose && pattern && - pattern->flags & PATTERN_FLAG_NEGATIVE) - pattern = NULL; - } - if (!quiet && (pattern || show_non_matching)) - output_pattern(pathspec.items[i].original, pattern); - if (pattern) - num_ignored++; - } - free(seen); - - return num_ignored; -} - -static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix) -{ - struct strbuf buf = STRBUF_INIT; - struct strbuf unquoted = STRBUF_INIT; - char *pathspec[2] = { NULL, NULL }; - strbuf_getline_fn getline_fn; - int num_ignored = 0; - - getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; - while (getline_fn(&buf, stdin) != EOF) { - if (!nul_term_line && buf.buf[0] == '"') { - strbuf_reset(&unquoted); - if (unquote_c_style(&unquoted, buf.buf, NULL)) - die("line is badly quoted"); - strbuf_swap(&buf, &unquoted); - } - pathspec[0] = buf.buf; - num_ignored += check_ignore(dir, prefix, - 1, (const char **)pathspec); - maybe_flush_or_die(stdout, "check-ignore to stdout"); - } - strbuf_release(&buf); - strbuf_release(&unquoted); - return num_ignored; -} - -int cmd_check_ignore(int argc, const char **argv, const char *prefix) -{ - int num_ignored; - struct dir_struct dir; - - git_config(git_default_config, NULL); - - argc = parse_options(argc, argv, prefix, check_ignore_options, - check_ignore_usage, 0); - - if (stdin_paths) { - if (argc > 0) - die(_("cannot specify pathnames with --stdin")); - } else { - if (nul_term_line) - die(_("-z only makes sense with --stdin")); - if (argc == 0) - die(_("no path specified")); - } - if (quiet) { - if (argc > 1) - die(_("--quiet is only valid with a single pathname")); - if (verbose) - die(_("cannot have both --quiet and --verbose")); - } - if (show_non_matching && !verbose) - die(_("--non-matching is only valid with --verbose")); - - /* read_cache() is only necessary so we can watch out for submodules. */ - if (!no_index && read_cache() < 0) - die(_("index file corrupt")); - - dir_init(&dir); - setup_standard_excludes(&dir); - - if (stdin_paths) { - num_ignored = check_ignore_stdin_paths(&dir, prefix); - } else { - num_ignored = check_ignore(&dir, prefix, argc, argv); - maybe_flush_or_die(stdout, "ignore to stdout"); - } - - dir_clear(&dir); - - return !num_ignored; -} diff --git a/third_party/git/builtin/check-mailmap.c b/third_party/git/builtin/check-mailmap.c deleted file mode 100644 index cdce144f3b7f..000000000000 --- a/third_party/git/builtin/check-mailmap.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "builtin.h" -#include "config.h" -#include "mailmap.h" -#include "parse-options.h" -#include "string-list.h" - -static int use_stdin; -static const char * const check_mailmap_usage[] = { -N_("git check-mailmap [<options>] <contact>..."), -NULL -}; - -static const struct option check_mailmap_options[] = { - OPT_BOOL(0, "stdin", &use_stdin, N_("also read contacts from stdin")), - OPT_END() -}; - -static void check_mailmap(struct string_list *mailmap, const char *contact) -{ - const char *name, *mail; - size_t namelen, maillen; - struct ident_split ident; - - if (split_ident_line(&ident, contact, strlen(contact))) - die(_("unable to parse contact: %s"), contact); - - name = ident.name_begin; - namelen = ident.name_end - ident.name_begin; - mail = ident.mail_begin; - maillen = ident.mail_end - ident.mail_begin; - - map_user(mailmap, &mail, &maillen, &name, &namelen); - - if (namelen) - printf("%.*s ", (int)namelen, name); - printf("<%.*s>\n", (int)maillen, mail); -} - -int cmd_check_mailmap(int argc, const char **argv, const char *prefix) -{ - int i; - struct string_list mailmap = STRING_LIST_INIT_NODUP; - - git_config(git_default_config, NULL); - argc = parse_options(argc, argv, prefix, check_mailmap_options, - check_mailmap_usage, 0); - if (argc == 0 && !use_stdin) - die(_("no contacts specified")); - - read_mailmap(&mailmap, NULL); - - for (i = 0; i < argc; ++i) - check_mailmap(&mailmap, argv[i]); - maybe_flush_or_die(stdout, "stdout"); - - if (use_stdin) { - struct strbuf buf = STRBUF_INIT; - while (strbuf_getline_lf(&buf, stdin) != EOF) { - check_mailmap(&mailmap, buf.buf); - maybe_flush_or_die(stdout, "stdout"); - } - strbuf_release(&buf); - } - - clear_mailmap(&mailmap); - return 0; -} diff --git a/third_party/git/builtin/check-ref-format.c b/third_party/git/builtin/check-ref-format.c deleted file mode 100644 index bc67d3f0a83d..000000000000 --- a/third_party/git/builtin/check-ref-format.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * GIT - The information manager from hell - */ - -#include "cache.h" -#include "refs.h" -#include "builtin.h" -#include "strbuf.h" - -static const char builtin_check_ref_format_usage[] = -"git check-ref-format [--normalize] [<options>] <refname>\n" -" or: git check-ref-format --branch <branchname-shorthand>"; - -/* - * Return a copy of refname but with leading slashes removed and runs - * of adjacent slashes replaced with single slashes. - * - * This function is similar to normalize_path_copy(), but stripped down - * to meet check_ref_format's simpler needs. - */ -static char *collapse_slashes(const char *refname) -{ - char *ret = xmallocz(strlen(refname)); - char ch; - char prev = '/'; - char *cp = ret; - - while ((ch = *refname++) != '\0') { - if (prev == '/' && ch == prev) - continue; - - *cp++ = ch; - prev = ch; - } - *cp = '\0'; - return ret; -} - -static int check_ref_format_branch(const char *arg) -{ - struct strbuf sb = STRBUF_INIT; - const char *name; - int nongit; - - setup_git_directory_gently(&nongit); - if (strbuf_check_branch_ref(&sb, arg) || - !skip_prefix(sb.buf, "refs/heads/", &name)) - die("'%s' is not a valid branch name", arg); - printf("%s\n", name); - strbuf_release(&sb); - return 0; -} - -int cmd_check_ref_format(int argc, const char **argv, const char *prefix) -{ - int i; - int normalize = 0; - int flags = 0; - const char *refname; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage(builtin_check_ref_format_usage); - - if (argc == 3 && !strcmp(argv[1], "--branch")) - return check_ref_format_branch(argv[2]); - - for (i = 1; i < argc && argv[i][0] == '-'; i++) { - if (!strcmp(argv[i], "--normalize") || !strcmp(argv[i], "--print")) - normalize = 1; - else if (!strcmp(argv[i], "--allow-onelevel")) - flags |= REFNAME_ALLOW_ONELEVEL; - else if (!strcmp(argv[i], "--no-allow-onelevel")) - flags &= ~REFNAME_ALLOW_ONELEVEL; - else if (!strcmp(argv[i], "--refspec-pattern")) - flags |= REFNAME_REFSPEC_PATTERN; - else - usage(builtin_check_ref_format_usage); - } - if (! (i == argc - 1)) - usage(builtin_check_ref_format_usage); - - refname = argv[i]; - if (normalize) - refname = collapse_slashes(refname); - if (check_refname_format(refname, flags)) - return 1; - if (normalize) - printf("%s\n", refname); - - return 0; -} diff --git a/third_party/git/builtin/checkout-index.c b/third_party/git/builtin/checkout-index.c deleted file mode 100644 index a854fd16e779..000000000000 --- a/third_party/git/builtin/checkout-index.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Check-out files from the "current cache directory" - * - * Copyright (C) 2005 Linus Torvalds - * - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "config.h" -#include "lockfile.h" -#include "quote.h" -#include "cache-tree.h" -#include "parse-options.h" - -#define CHECKOUT_ALL 4 -static int nul_term_line; -static int checkout_stage; /* default to checkout stage0 */ -static int to_tempfile; -static char topath[4][TEMPORARY_FILENAME_LENGTH + 1]; - -static struct checkout state = CHECKOUT_INIT; - -static void write_tempfile_record(const char *name, const char *prefix) -{ - int i; - - if (CHECKOUT_ALL == checkout_stage) { - for (i = 1; i < 4; i++) { - if (i > 1) - putchar(' '); - if (topath[i][0]) - fputs(topath[i], stdout); - else - putchar('.'); - } - } else - fputs(topath[checkout_stage], stdout); - - putchar('\t'); - write_name_quoted_relative(name, prefix, stdout, - nul_term_line ? '\0' : '\n'); - - for (i = 0; i < 4; i++) { - topath[i][0] = 0; - } -} - -static int checkout_file(const char *name, const char *prefix) -{ - int namelen = strlen(name); - int pos = cache_name_pos(name, namelen); - int has_same_name = 0; - int did_checkout = 0; - int errs = 0; - - if (pos < 0) - pos = -pos - 1; - - while (pos < active_nr) { - struct cache_entry *ce = active_cache[pos]; - if (ce_namelen(ce) != namelen || - memcmp(ce->name, name, namelen)) - break; - has_same_name = 1; - pos++; - if (ce_stage(ce) != checkout_stage - && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce))) - continue; - did_checkout = 1; - if (checkout_entry(ce, &state, - to_tempfile ? topath[ce_stage(ce)] : NULL, - NULL) < 0) - errs++; - } - - if (did_checkout) { - if (to_tempfile) - write_tempfile_record(name, prefix); - return errs > 0 ? -1 : 0; - } - - if (!state.quiet) { - fprintf(stderr, "git checkout-index: %s ", name); - if (!has_same_name) - fprintf(stderr, "is not in the cache"); - else if (checkout_stage) - fprintf(stderr, "does not exist at stage %d", - checkout_stage); - else - fprintf(stderr, "is unmerged"); - fputc('\n', stderr); - } - return -1; -} - -static void checkout_all(const char *prefix, int prefix_length) -{ - int i, errs = 0; - struct cache_entry *last_ce = NULL; - - for (i = 0; i < active_nr ; i++) { - struct cache_entry *ce = active_cache[i]; - if (ce_stage(ce) != checkout_stage - && (CHECKOUT_ALL != checkout_stage || !ce_stage(ce))) - continue; - if (prefix && *prefix && - (ce_namelen(ce) <= prefix_length || - memcmp(prefix, ce->name, prefix_length))) - continue; - if (last_ce && to_tempfile) { - if (ce_namelen(last_ce) != ce_namelen(ce) - || memcmp(last_ce->name, ce->name, ce_namelen(ce))) - write_tempfile_record(last_ce->name, prefix); - } - if (checkout_entry(ce, &state, - to_tempfile ? topath[ce_stage(ce)] : NULL, - NULL) < 0) - errs++; - last_ce = ce; - } - if (last_ce && to_tempfile) - write_tempfile_record(last_ce->name, prefix); - if (errs) - /* we have already done our error reporting. - * exit with the same code as die(). - */ - exit(128); -} - -static const char * const builtin_checkout_index_usage[] = { - N_("git checkout-index [<options>] [--] [<file>...]"), - NULL -}; - -static int option_parse_stage(const struct option *opt, - const char *arg, int unset) -{ - BUG_ON_OPT_NEG(unset); - - if (!strcmp(arg, "all")) { - to_tempfile = 1; - checkout_stage = CHECKOUT_ALL; - } else { - int ch = arg[0]; - if ('1' <= ch && ch <= '3') - checkout_stage = arg[0] - '0'; - else - die(_("stage should be between 1 and 3 or all")); - } - return 0; -} - -int cmd_checkout_index(int argc, const char **argv, const char *prefix) -{ - int i; - struct lock_file lock_file = LOCK_INIT; - int all = 0; - int read_from_stdin = 0; - int prefix_length; - int force = 0, quiet = 0, not_new = 0; - int index_opt = 0; - struct option builtin_checkout_index_options[] = { - OPT_BOOL('a', "all", &all, - N_("check out all files in the index")), - OPT__FORCE(&force, N_("force overwrite of existing files"), 0), - OPT__QUIET(&quiet, - N_("no warning for existing files and files not in index")), - OPT_BOOL('n', "no-create", ¬_new, - N_("don't checkout new files")), - OPT_BOOL('u', "index", &index_opt, - N_("update stat information in the index file")), - OPT_BOOL('z', NULL, &nul_term_line, - N_("paths are separated with NUL character")), - OPT_BOOL(0, "stdin", &read_from_stdin, - N_("read list of paths from the standard input")), - OPT_BOOL(0, "temp", &to_tempfile, - N_("write the content to temporary files")), - OPT_STRING(0, "prefix", &state.base_dir, N_("string"), - N_("when creating files, prepend <string>")), - OPT_CALLBACK_F(0, "stage", NULL, "(1|2|3|all)", - N_("copy out the files from named stage"), - PARSE_OPT_NONEG, option_parse_stage), - OPT_END() - }; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage_with_options(builtin_checkout_index_usage, - builtin_checkout_index_options); - git_config(git_default_config, NULL); - prefix_length = prefix ? strlen(prefix) : 0; - - if (read_cache() < 0) { - die("invalid cache"); - } - - argc = parse_options(argc, argv, prefix, builtin_checkout_index_options, - builtin_checkout_index_usage, 0); - state.istate = &the_index; - state.force = force; - state.quiet = quiet; - state.not_new = not_new; - - if (!state.base_dir) - state.base_dir = ""; - state.base_dir_len = strlen(state.base_dir); - - /* - * when --prefix is specified we do not want to update cache. - */ - if (index_opt && !state.base_dir_len && !to_tempfile) { - state.refresh_cache = 1; - state.istate = &the_index; - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); - } - - /* Check out named files first */ - for (i = 0; i < argc; i++) { - const char *arg = argv[i]; - char *p; - - if (all) - die("git checkout-index: don't mix '--all' and explicit filenames"); - if (read_from_stdin) - die("git checkout-index: don't mix '--stdin' and explicit filenames"); - p = prefix_path(prefix, prefix_length, arg); - checkout_file(p, prefix); - free(p); - } - - if (read_from_stdin) { - struct strbuf buf = STRBUF_INIT; - struct strbuf unquoted = STRBUF_INIT; - strbuf_getline_fn getline_fn; - - if (all) - die("git checkout-index: don't mix '--all' and '--stdin'"); - - getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; - while (getline_fn(&buf, stdin) != EOF) { - char *p; - if (!nul_term_line && buf.buf[0] == '"') { - strbuf_reset(&unquoted); - if (unquote_c_style(&unquoted, buf.buf, NULL)) - die("line is badly quoted"); - strbuf_swap(&buf, &unquoted); - } - p = prefix_path(prefix, prefix_length, buf.buf); - checkout_file(p, prefix); - free(p); - } - strbuf_release(&unquoted); - strbuf_release(&buf); - } - - if (all) - checkout_all(prefix, prefix_length); - - if (is_lock_file_locked(&lock_file) && - write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) - die("Unable to write new index file"); - return 0; -} diff --git a/third_party/git/builtin/checkout.c b/third_party/git/builtin/checkout.c deleted file mode 100644 index 0951f8fee5cc..000000000000 --- a/third_party/git/builtin/checkout.c +++ /dev/null @@ -1,1868 +0,0 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "advice.h" -#include "blob.h" -#include "branch.h" -#include "cache-tree.h" -#include "checkout.h" -#include "commit.h" -#include "config.h" -#include "diff.h" -#include "dir.h" -#include "ll-merge.h" -#include "lockfile.h" -#include "merge-recursive.h" -#include "object-store.h" -#include "parse-options.h" -#include "refs.h" -#include "remote.h" -#include "resolve-undo.h" -#include "revision.h" -#include "run-command.h" -#include "submodule.h" -#include "submodule-config.h" -#include "tree.h" -#include "tree-walk.h" -#include "unpack-trees.h" -#include "wt-status.h" -#include "xdiff-interface.h" - -static const char * const checkout_usage[] = { - N_("git checkout [<options>] <branch>"), - N_("git checkout [<options>] [<branch>] -- <file>..."), - NULL, -}; - -static const char * const switch_branch_usage[] = { - N_("git switch [<options>] [<branch>]"), - NULL, -}; - -static const char * const restore_usage[] = { - N_("git restore [<options>] [--source=<branch>] <file>..."), - 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; - const 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; - char *conflict_style; - - int branch_exists; - const char *prefix; - struct pathspec pathspec; - const char *from_treeish; - struct tree *source_tree; -}; - -struct branch_info { - const char *name; /* The short name used */ - const 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 int post_checkout_hook(struct commit *old_commit, struct commit *new_commit, - int changed) -{ - return run_hook_le(NULL, "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, int stage, void *context) -{ - 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_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 = cache_name_pos(ce->name, ce->ce_namelen); - if (pos >= 0) { - struct cache_entry *old = active_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_cache_entry(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_recursive(the_repository, tree, "", 0, 0, - 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 < active_nr && - !strcmp(active_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 < active_nr && - !strcmp(active_cache[pos]->name, ce->name)) { - if (ce_stage(active_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 < active_nr) { - ce = active_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 < active_nr && - !strcmp(active_cache[pos]->name, ce->name)) { - if (ce_stage(active_cache[pos]) == stage) - return checkout_entry(active_cache[pos], state, - NULL, nr_checkouts); - pos++; - } - if (!overlay_mode) { - unlink_entry(ce); - 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 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; - struct ll_merge_options ll_opts; - int renormalize = 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]); - - memset(&ll_opts, 0, sizeof(ll_opts)); - git_config_get_bool("merge.renormalize", &renormalize); - ll_opts.renormalize = renormalize; - 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 (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; -} - -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_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(&the_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_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; - - state.force = 1; - state.refresh_cache = 1; - state.istate = &the_index; - - init_checkout_metadata(&state.meta, info->refname, - info->commit ? &info->commit->object.oid : &info->oid, - NULL); - - 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); - } - - 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(_("'%s' cannot be used with %s"), "--merge", "--patch"); - - if (opts->ignore_unmerged && opts->merge) - die(_("'%s' cannot be used with %s"), - 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"); - - if (opts->checkout_index && !opts->checkout_worktree && - opts->writeout_stage) - die(_("'%s' or '%s' cannot be used with %s"), - "--ours", "--theirs", "--staged"); - - if (opts->checkout_index && !opts->checkout_worktree && - opts->merge) - die(_("'%s' or '%s' cannot be used with %s"), - "--merge", "--conflict", "--staged"); - - if (opts->patch_mode) { - const char *patch_mode; - - if (opts->checkout_index && opts->checkout_worktree) - patch_mode = "--patch=checkout"; - else if (opts->checkout_index && !opts->checkout_worktree) - patch_mode = "--patch=reset"; - else if (!opts->checkout_index && opts->checkout_worktree) - patch_mode = "--patch=worktree"; - else - BUG("either flag must have been set, worktree=%d, index=%d", - opts->checkout_worktree, opts->checkout_index); - return run_add_interactive(new_branch_info->name, patch_mode, &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)) { - 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->ignore_unmerged) { - if (!opts->quiet) - 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 */ - if (opts->checkout_worktree) - errs |= checkout_worktree(opts, new_branch_info); - else - remove_marked_cache_entries(&the_index, 1); - - /* - * Allow updating the index when checking out from the index. - * This is to save new stat info. - */ - if (opts->checkout_worktree && !opts->checkout_index && !opts->source_tree) - checkout_index = 1; - else - checkout_index = opts->checkout_index; - - if (checkout_index) { - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) - die(_("unable to write new index file")); - } else { - /* - * NEEDSWORK: if --worktree is not specified, we - * should save stat info of checked out files in the - * index to avoid the next (potentially costly) - * refresh. But it's a bit tricker to do... - */ - rollback_lock_file(&lock_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 void show_local_changes(struct object *head, - const struct diff_options *opts) -{ - struct rev_info rev; - /* I think we want full paths, even if we're in a subdirectory. */ - repo_init_revisions(the_repository, &rev, NULL); - rev.diffopt.flags = opts->flags; - rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS; - diff_setup_done(&rev.diffopt); - add_pending_object(&rev, head, NULL); - run_diff_index(&rev, 0); -} - -static void describe_detached_head(const char *msg, struct commit *commit) -{ - struct strbuf sb = STRBUF_INIT; - - if (!parse_commit(commit)) - pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb); - if (print_sha1_ellipsis()) { - fprintf(stderr, "%s %s... %s\n", msg, - find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf); - } else { - fprintf(stderr, "%s %s %s\n", msg, - find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV), sb.buf); - } - strbuf_release(&sb); -} - -static int reset_tree(struct tree *tree, const struct checkout_opts *o, - int worktree, int *writeout_error, - struct branch_info *info) -{ - struct unpack_trees_options opts; - struct tree_desc tree_desc; - - memset(&opts, 0, sizeof(opts)); - opts.head_idx = -1; - opts.update = worktree; - opts.skip_unmerged = !worktree; - opts.reset = 1; - opts.merge = 1; - opts.fn = oneway_merge; - opts.verbose_update = o->show_progress; - opts.src_index = &the_index; - opts.dst_index = &the_index; - init_checkout_metadata(&opts.meta, info->refname, - info->commit ? &info->commit->object.oid : &null_oid, - NULL); - parse_tree(tree); - init_tree_desc(&tree_desc, tree->buffer, tree->size); - switch (unpack_trees(1, &tree_desc, &opts)) { - case -2: - *writeout_error = 1; - /* - * We return 0 nevertheless, as the index is all right - * and more importantly we have made best efforts to - * update paths in the work tree, and we cannot revert - * them. - */ - /* fallthrough */ - case 0: - return 0; - default: - return 128; - } -} - -static void setup_branch_path(struct branch_info *branch) -{ - struct strbuf buf = STRBUF_INIT; - - /* - * If this is a ref, resolve it; otherwise, look up the OID for our - * expression. Failure here is okay. - */ - if (!dwim_ref(branch->name, strlen(branch->name), &branch->oid, &branch->refname, 0)) - repo_get_oid_committish(the_repository, branch->name, &branch->oid); - - strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL); - if (strcmp(buf.buf, branch->name)) - branch->name = xstrdup(buf.buf); - strbuf_splice(&buf, 0, 0, "refs/heads/", 11); - branch->path = strbuf_detach(&buf, NULL); -} - -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; - struct tree *new_tree; - - 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->new_orphan_branch && opts->orphan_from_empty_tree) { - if (new_branch_info->commit) - BUG("'switch --orphan' should never accept a commit as starting point"); - new_tree = parse_tree_indirect(the_hash_algo->empty_tree); - } else - new_tree = get_commit_tree(new_branch_info->commit); - if (opts->discard_changes) { - ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info); - 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.quiet = opts->merge && old_branch_info->commit; - topts.verbose_update = opts->show_progress; - topts.fn = twoway_merge; - init_checkout_metadata(&topts.meta, new_branch_info->refname, - new_branch_info->commit ? - &new_branch_info->commit->object.oid : - &new_branch_info->oid, NULL); - 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); - parse_tree(new_tree); - tree = new_tree; - 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 *work; - struct tree *old_tree; - struct merge_options o; - struct strbuf sb = STRBUF_INIT; - struct strbuf old_commit_shortname = STRBUF_INIT; - - 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; - old_tree = get_commit_tree(old_branch_info->commit); - - if (repo_index_has_changes(the_repository, old_tree, &sb)) - die(_("cannot continue with staged changes in " - "the following files:\n%s"), sb.buf); - strbuf_release(&sb); - - /* 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); - init_merge_options(&o, the_repository); - o.verbosity = 0; - work = write_in_core_index_as_tree(the_repository); - - ret = reset_tree(new_tree, - opts, 1, - writeout_error, new_branch_info); - if (ret) - return ret; - o.ancestor = old_branch_info->name; - if (old_branch_info->name == NULL) { - strbuf_add_unique_abbrev(&old_commit_shortname, - &old_branch_info->commit->object.oid, - DEFAULT_ABBREV); - o.ancestor = old_commit_shortname.buf; - } - o.branch1 = new_branch_info->name; - o.branch2 = "local"; - ret = merge_trees(&o, - new_tree, - work, - old_tree); - if (ret < 0) - exit(128); - ret = reset_tree(new_tree, - opts, 0, - writeout_error, new_branch_info); - strbuf_release(&o.obuf); - strbuf_release(&old_commit_shortname); - 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->discard_changes && !opts->quiet && new_branch_info->commit) - show_local_changes(&new_branch_info->commit->object, &opts->diff_options); - - return 0; -} - -static void report_tracking(struct branch_info *new_branch_info) -{ - struct strbuf sb = STRBUF_INIT; - struct branch *branch = branch_get(new_branch_info->name); - - if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL)) - return; - fputs(sb.buf, stdout); - strbuf_release(&sb); -} - -static void update_refs_for_switch(const struct checkout_opts *opts, - struct branch_info *old_branch_info, - struct branch_info *new_branch_info) -{ - struct strbuf msg = STRBUF_INIT; - const char *old_desc, *reflog_msg; - if (opts->new_branch) { - if (opts->new_orphan_branch) { - char *refname; - - refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch); - if (opts->new_branch_log && - !should_autocreate_reflog(refname)) { - int ret; - struct strbuf err = STRBUF_INIT; - - ret = safe_create_reflog(refname, 1, &err); - if (ret) { - fprintf(stderr, _("Can not do reflog for '%s': %s\n"), - opts->new_orphan_branch, err.buf); - strbuf_release(&err); - free(refname); - return; - } - strbuf_release(&err); - } - free(refname); - } - else - create_branch(the_repository, - opts->new_branch, new_branch_info->name, - opts->new_branch_force ? 1 : 0, - opts->new_branch_force ? 1 : 0, - opts->new_branch_log, - opts->quiet, - opts->track); - new_branch_info->name = opts->new_branch; - setup_branch_path(new_branch_info); - } - - old_desc = old_branch_info->name; - if (!old_desc && old_branch_info->commit) - old_desc = oid_to_hex(&old_branch_info->commit->object.oid); - - reflog_msg = getenv("GIT_REFLOG_ACTION"); - if (!reflog_msg) - strbuf_addf(&msg, "checkout: moving from %s to %s", - old_desc ? old_desc : "(invalid)", new_branch_info->name); - else - strbuf_insertstr(&msg, 0, reflog_msg); - - if (!strcmp(new_branch_info->name, "HEAD") && !new_branch_info->path && !opts->force_detach) { - /* Nothing to do. */ - } else if (opts->force_detach || !new_branch_info->path) { /* No longer on any branch. */ - update_ref(msg.buf, "HEAD", &new_branch_info->commit->object.oid, NULL, - REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR); - if (!opts->quiet) { - if (old_branch_info->path && - advice_detached_head && !opts->force_detach) - detach_advice(new_branch_info->name); - describe_detached_head(_("HEAD is now at"), new_branch_info->commit); - } - } else if (new_branch_info->path) { /* Switch branches. */ - if (create_symref("HEAD", new_branch_info->path, msg.buf) < 0) - die(_("unable to update HEAD")); - if (!opts->quiet) { - if (old_branch_info->path && !strcmp(new_branch_info->path, old_branch_info->path)) { - if (opts->new_branch_force) - fprintf(stderr, _("Reset branch '%s'\n"), - new_branch_info->name); - else - fprintf(stderr, _("Already on '%s'\n"), - new_branch_info->name); - } else if (opts->new_branch) { - if (opts->branch_exists) - fprintf(stderr, _("Switched to and reset branch '%s'\n"), new_branch_info->name); - else - fprintf(stderr, _("Switched to a new branch '%s'\n"), new_branch_info->name); - } else { - fprintf(stderr, _("Switched to branch '%s'\n"), - new_branch_info->name); - } - } - if (old_branch_info->path && old_branch_info->name) { - if (!ref_exists(old_branch_info->path) && reflog_exists(old_branch_info->path)) - delete_reflog(old_branch_info->path); - } - } - remove_branch_state(the_repository, !opts->quiet); - strbuf_release(&msg); - if (!opts->quiet && - (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD")))) - report_tracking(new_branch_info); -} - -static int add_pending_uninteresting_ref(const char *refname, - const struct object_id *oid, - int flags, void *cb_data) -{ - add_pending_oid(cb_data, refname, oid, UNINTERESTING); - return 0; -} - -static void describe_one_orphan(struct strbuf *sb, struct commit *commit) -{ - strbuf_addstr(sb, " "); - strbuf_add_unique_abbrev(sb, &commit->object.oid, DEFAULT_ABBREV); - strbuf_addch(sb, ' '); - if (!parse_commit(commit)) - pp_commit_easy(CMIT_FMT_ONELINE, commit, sb); - strbuf_addch(sb, '\n'); -} - -#define ORPHAN_CUTOFF 4 -static void suggest_reattach(struct commit *commit, struct rev_info *revs) -{ - struct commit *c, *last = NULL; - struct strbuf sb = STRBUF_INIT; - int lost = 0; - while ((c = get_revision(revs)) != NULL) { - if (lost < ORPHAN_CUTOFF) - describe_one_orphan(&sb, c); - last = c; - lost++; - } - if (ORPHAN_CUTOFF < lost) { - int more = lost - ORPHAN_CUTOFF; - if (more == 1) - describe_one_orphan(&sb, last); - else - strbuf_addf(&sb, _(" ... and %d more.\n"), more); - } - - fprintf(stderr, - Q_( - /* The singular version */ - "Warning: you are leaving %d commit behind, " - "not connected to\n" - "any of your branches:\n\n" - "%s\n", - /* The plural version */ - "Warning: you are leaving %d commits behind, " - "not connected to\n" - "any of your branches:\n\n" - "%s\n", - /* Give ngettext() the count */ - lost), - lost, - sb.buf); - strbuf_release(&sb); - - if (advice_detached_head) - fprintf(stderr, - Q_( - /* The singular version */ - "If you want to keep it by creating a new branch, " - "this may be a good time\nto do so with:\n\n" - " git branch <new-branch-name> %s\n\n", - /* The plural version */ - "If you want to keep them by creating a new branch, " - "this may be a good time\nto do so with:\n\n" - " git branch <new-branch-name> %s\n\n", - /* Give ngettext() the count */ - lost), - find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); -} - -/* - * We are about to leave commit that was at the tip of a detached - * HEAD. If it is not reachable from any ref, this is the last chance - * for the user to do so without resorting to reflog. - */ -static void orphaned_commit_warning(struct commit *old_commit, struct commit *new_commit) -{ - struct rev_info revs; - struct object *object = &old_commit->object; - - repo_init_revisions(the_repository, &revs, NULL); - setup_revisions(0, NULL, &revs, NULL); - - object->flags &= ~UNINTERESTING; - add_pending_object(&revs, object, oid_to_hex(&object->oid)); - - for_each_ref(add_pending_uninteresting_ref, &revs); - if (new_commit) - add_pending_oid(&revs, "HEAD", - &new_commit->object.oid, - UNINTERESTING); - - if (prepare_revision_walk(&revs)) - die(_("internal error in revision walk")); - if (!(old_commit->object.flags & UNINTERESTING)) - suggest_reattach(old_commit, &revs); - else - describe_detached_head(_("Previous HEAD position was"), old_commit); - - /* Clean up objects used, as they will be reused. */ - clear_commit_marks_all(ALL_REV_FLAGS); -} - -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; - int do_merge = 1; - - 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 (opts->new_orphan_branch && opts->orphan_from_empty_tree) { - if (new_branch_info->name) - BUG("'switch --orphan' should never accept a commit as starting point"); - new_branch_info->commit = NULL; - new_branch_info->name = "(empty)"; - do_merge = 1; - } - - 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); - - if (opts->only_merge_on_switching_branches) - do_merge = 0; - } - - if (do_merge) { - 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; -} - -static int git_checkout_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "diff.ignoresubmodules")) { - struct checkout_opts *opts = cb; - handle_ignore_submodules_arg(&opts->diff_options, value); - return 0; - } - - if (starts_with(var, "submodule.")) - return git_default_submodule_config(var, value, NULL); - - return git_xmerge_config(var, value, NULL); -} - -static void setup_new_branch_info_and_source_tree( - struct branch_info *new_branch_info, - struct checkout_opts *opts, - struct object_id *rev, - const char *arg) -{ - struct tree **source_tree = &opts->source_tree; - struct object_id branch_rev; - - new_branch_info->name = arg; - setup_branch_path(new_branch_info); - - if (!check_refname_format(new_branch_info->path, 0) && - !read_ref(new_branch_info->path, &branch_rev)) - oidcpy(rev, &branch_rev); - else { - free((char *)new_branch_info->path); - new_branch_info->path = NULL; /* not an existing branch */ - } - - new_branch_info->commit = lookup_commit_reference_gently(the_repository, rev, 1); - if (!new_branch_info->commit) { - /* not a commit */ - *source_tree = parse_tree_indirect(rev); - } else { - parse_commit_or_die(new_branch_info->commit); - *source_tree = get_commit_tree(new_branch_info->commit); - } -} - -static const char *parse_remote_branch(const char *arg, - struct object_id *rev, - int could_be_checkout_paths) -{ - int num_matches = 0; - const char *remote = unique_tracking_name(arg, rev, &num_matches); - - if (remote && could_be_checkout_paths) { - die(_("'%s' could be both a local file and a tracking branch.\n" - "Please use -- (and optionally --no-guess) to disambiguate"), - arg); - } - - if (!remote && num_matches > 1) { - if (advice_checkout_ambiguous_remote_branch_name) { - advise(_("If you meant to check out a remote tracking branch on, e.g. 'origin',\n" - "you can do so by fully qualifying the name with the --track option:\n" - "\n" - " git checkout --track origin/<name>\n" - "\n" - "If you'd like to always have checkouts of an ambiguous <name> prefer\n" - "one remote, e.g. the 'origin' remote, consider setting\n" - "checkout.defaultRemote=origin in your config.")); - } - - die(_("'%s' matched multiple (%d) remote tracking branches"), - arg, num_matches); - } - - return remote; -} - -static int parse_branchname_arg(int argc, const char **argv, - int dwim_new_local_branch_ok, - struct branch_info *new_branch_info, - struct checkout_opts *opts, - struct object_id *rev) -{ - const char **new_branch = &opts->new_branch; - int argcount = 0; - const char *arg; - int dash_dash_pos; - int has_dash_dash = 0; - int i; - - /* - * case 1: git checkout <ref> -- [<paths>] - * - * <ref> must be a valid tree, everything after the '--' must be - * a path. - * - * case 2: git checkout -- [<paths>] - * - * everything after the '--' must be paths. - * - * case 3: git checkout <something> [--] - * - * (a) If <something> is a commit, that is to - * switch to the branch or detach HEAD at it. As a special case, - * if <something> is A...B (missing A or B means HEAD but you can - * omit at most one side), and if there is a unique merge base - * between A and B, A...B names that merge base. - * - * (b) If <something> is _not_ a commit, either "--" is present - * or <something> is not a path, no -t or -b was given, and - * and there is a tracking branch whose name is <something> - * in one and only one remote (or if the branch exists on the - * remote named in checkout.defaultRemote), then this is a - * short-hand to fork local <something> from that - * remote-tracking branch. - * - * (c) Otherwise, if "--" is present, treat it like case (1). - * - * (d) Otherwise : - * - if it's a reference, treat it like case (1) - * - else if it's a path, treat it like case (2) - * - else: fail. - * - * case 4: git checkout <something> <paths> - * - * The first argument must not be ambiguous. - * - If it's *only* a reference, treat it like case (1). - * - If it's only a path, treat it like case (2). - * - else: fail. - * - */ - if (!argc) - return 0; - - if (!opts->accept_pathspec) { - if (argc > 1) - die(_("only one reference expected")); - has_dash_dash = 1; /* helps disambiguate */ - } - - arg = argv[0]; - dash_dash_pos = -1; - for (i = 0; i < argc; i++) { - if (opts->accept_pathspec && !strcmp(argv[i], "--")) { - dash_dash_pos = i; - break; - } - } - if (dash_dash_pos == 0) - return 1; /* case (2) */ - else if (dash_dash_pos == 1) - has_dash_dash = 1; /* case (3) or (1) */ - else if (dash_dash_pos >= 2) - die(_("only one reference expected, %d given."), dash_dash_pos); - opts->count_checkout_paths = !opts->quiet && !has_dash_dash; - - if (!strcmp(arg, "-")) - arg = "@{-1}"; - - if (get_oid_mb(arg, rev)) { - /* - * Either case (3) or (4), with <something> not being - * a commit, or an attempt to use case (1) with an - * invalid ref. - * - * It's likely an error, but we need to find out if - * we should auto-create the branch, case (3).(b). - */ - int recover_with_dwim = dwim_new_local_branch_ok; - - int could_be_checkout_paths = !has_dash_dash && - check_filename(opts->prefix, arg); - - if (!has_dash_dash && !no_wildcard(arg)) - recover_with_dwim = 0; - - /* - * Accept "git checkout foo", "git checkout foo --" - * and "git switch foo" as candidates for dwim. - */ - if (!(argc == 1 && !has_dash_dash) && - !(argc == 2 && has_dash_dash) && - opts->accept_pathspec) - recover_with_dwim = 0; - - if (recover_with_dwim) { - const char *remote = parse_remote_branch(arg, rev, - could_be_checkout_paths); - if (remote) { - *new_branch = arg; - arg = remote; - /* DWIMmed to create local branch, case (3).(b) */ - } else { - recover_with_dwim = 0; - } - } - - if (!recover_with_dwim) { - if (has_dash_dash) - die(_("invalid reference: %s"), arg); - return argcount; - } - } - - /* we can't end up being in (2) anymore, eat the argument */ - argcount++; - argv++; - argc--; - - setup_new_branch_info_and_source_tree(new_branch_info, opts, rev, arg); - - if (!opts->source_tree) /* case (1): want a tree */ - die(_("reference is not a tree: %s"), arg); - - if (!has_dash_dash) { /* case (3).(d) -> (1) */ - /* - * Do not complain the most common case - * git checkout branch - * even if there happen to be a file called 'branch'; - * it would be extremely annoying. - */ - if (argc) - verify_non_filename(opts->prefix, arg); - } else if (opts->accept_pathspec) { - argcount++; - argv++; - argc--; - } - - return argcount; -} - -static int switch_unborn_to_new_branch(const struct checkout_opts *opts) -{ - int status; - struct strbuf branch_ref = STRBUF_INIT; - - trace2_cmd_mode("unborn"); - - if (!opts->new_branch) - die(_("You are on a branch yet to be born")); - strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch); - status = create_symref("HEAD", branch_ref.buf, "checkout -b"); - strbuf_release(&branch_ref); - if (!opts->quiet) - fprintf(stderr, _("Switched to a new branch '%s'\n"), - opts->new_branch); - return status; -} - -static void die_expecting_a_branch(const struct branch_info *branch_info) -{ - struct object_id oid; - char *to_free; - - if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free, 0) == 1) { - const char *ref = to_free; - - if (skip_prefix(ref, "refs/tags/", &ref)) - die(_("a branch is expected, got tag '%s'"), ref); - if (skip_prefix(ref, "refs/remotes/", &ref)) - die(_("a branch is expected, got remote branch '%s'"), ref); - die(_("a branch is expected, got '%s'"), ref); - } - if (branch_info->commit) - die(_("a branch is expected, got commit '%s'"), branch_info->name); - /* - * This case should never happen because we already die() on - * non-commit, but just in case. - */ - die(_("a branch is expected, got '%s'"), branch_info->name); -} - -static void die_if_some_operation_in_progress(void) -{ - struct wt_status_state state; - - memset(&state, 0, sizeof(state)); - wt_status_get_state(the_repository, &state, 0); - - if (state.merge_in_progress) - die(_("cannot switch branch while merging\n" - "Consider \"git merge --quit\" " - "or \"git worktree add\".")); - if (state.am_in_progress) - die(_("cannot switch branch in the middle of an am session\n" - "Consider \"git am --quit\" " - "or \"git worktree add\".")); - if (state.rebase_interactive_in_progress || state.rebase_in_progress) - die(_("cannot switch branch while rebasing\n" - "Consider \"git rebase --quit\" " - "or \"git worktree add\".")); - if (state.cherry_pick_in_progress) - die(_("cannot switch branch while cherry-picking\n" - "Consider \"git cherry-pick --quit\" " - "or \"git worktree add\".")); - if (state.revert_in_progress) - die(_("cannot switch branch while reverting\n" - "Consider \"git revert --quit\" " - "or \"git worktree add\".")); - if (state.bisect_in_progress) - warning(_("you are switching branch while bisecting")); -} - -static int checkout_branch(struct checkout_opts *opts, - struct branch_info *new_branch_info) -{ - if (opts->pathspec.nr) - die(_("paths cannot be used with switching branches")); - - if (opts->patch_mode) - die(_("'%s' cannot be used with switching branches"), - "--patch"); - - if (opts->overlay_mode != -1) - die(_("'%s' cannot be used with switching branches"), - "--[no]-overlay"); - - if (opts->writeout_stage) - die(_("'%s' cannot be used with switching branches"), - "--ours/--theirs"); - - if (opts->force && opts->merge) - die(_("'%s' cannot be used with '%s'"), "-f", "-m"); - - if (opts->discard_changes && opts->merge) - die(_("'%s' cannot be used with '%s'"), "--discard-changes", "--merge"); - - if (opts->force_detach && opts->new_branch) - die(_("'%s' cannot be used with '%s'"), - "--detach", "-b/-B/--orphan"); - - if (opts->new_orphan_branch) { - if (opts->track != BRANCH_TRACK_UNSPECIFIED) - die(_("'%s' cannot be used with '%s'"), "--orphan", "-t"); - if (opts->orphan_from_empty_tree && new_branch_info->name) - die(_("'%s' cannot take <start-point>"), "--orphan"); - } else if (opts->force_detach) { - if (opts->track != BRANCH_TRACK_UNSPECIFIED) - die(_("'%s' cannot be used with '%s'"), "--detach", "-t"); - } else if (opts->track == BRANCH_TRACK_UNSPECIFIED) - opts->track = git_branch_track; - - if (new_branch_info->name && !new_branch_info->commit) - die(_("Cannot switch branch to a non-commit '%s'"), - new_branch_info->name); - - if (!opts->switch_branch_doing_nothing_is_ok && - !new_branch_info->name && - !opts->new_branch && - !opts->force_detach) - die(_("missing branch or commit argument")); - - if (!opts->implicit_detach && - !opts->force_detach && - !opts->new_branch && - !opts->new_branch_force && - new_branch_info->name && - !new_branch_info->path) - die_expecting_a_branch(new_branch_info); - - if (!opts->can_switch_when_in_progress) - die_if_some_operation_in_progress(); - - if (new_branch_info->path && !opts->force_detach && !opts->new_branch && - !opts->ignore_other_worktrees) { - int flag; - char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag); - if (head_ref && - (!(flag & REF_ISSYMREF) || strcmp(head_ref, new_branch_info->path))) - die_if_checked_out(new_branch_info->path, 1); - free(head_ref); - } - - if (!new_branch_info->commit && opts->new_branch) { - struct object_id rev; - int flag; - - if (!read_ref_full("HEAD", 0, &rev, &flag) && - (flag & REF_ISSYMREF) && is_null_oid(&rev)) - return switch_unborn_to_new_branch(opts); - } - return switch_branches(opts, new_branch_info); -} - -static struct option *add_common_options(struct checkout_opts *opts, - struct option *prevopts) -{ - struct option options[] = { - OPT__QUIET(&opts->quiet, N_("suppress progress reporting")), - OPT_CALLBACK_F(0, "recurse-submodules", NULL, - "checkout", "control recursive updating of submodules", - PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), - OPT_BOOL(0, "progress", &opts->show_progress, N_("force progress reporting")), - OPT_BOOL('m', "merge", &opts->merge, N_("perform a 3-way merge with the new branch")), - OPT_STRING(0, "conflict", &opts->conflict_style, N_("style"), - N_("conflict style (merge or diff3)")), - OPT_END() - }; - struct option *newopts = parse_options_concat(prevopts, options); - free(prevopts); - return newopts; -} - -static struct option *add_common_switch_branch_options( - struct checkout_opts *opts, struct option *prevopts) -{ - struct option options[] = { - OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")), - OPT_SET_INT('t', "track", &opts->track, N_("set upstream info for new branch"), - BRANCH_TRACK_EXPLICIT), - OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"), - PARSE_OPT_NOCOMPLETE), - OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")), - OPT_BOOL_F(0, "overwrite-ignore", &opts->overwrite_ignore, - N_("update ignored files (default)"), - PARSE_OPT_NOCOMPLETE), - OPT_BOOL(0, "ignore-other-worktrees", &opts->ignore_other_worktrees, - N_("do not check if another worktree is holding the given ref")), - OPT_END() - }; - struct option *newopts = parse_options_concat(prevopts, options); - free(prevopts); - return newopts; -} - -static struct option *add_checkout_path_options(struct checkout_opts *opts, - struct option *prevopts) -{ - struct option options[] = { - OPT_SET_INT_F('2', "ours", &opts->writeout_stage, - N_("checkout our version for unmerged files"), - 2, PARSE_OPT_NONEG), - OPT_SET_INT_F('3', "theirs", &opts->writeout_stage, - N_("checkout their version for unmerged files"), - 3, PARSE_OPT_NONEG), - OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")), - OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree, - N_("do not limit pathspecs to sparse entries only")), - OPT_PATHSPEC_FROM_FILE(&opts->pathspec_from_file), - OPT_PATHSPEC_FILE_NUL(&opts->pathspec_file_nul), - OPT_END() - }; - struct option *newopts = parse_options_concat(prevopts, options); - free(prevopts); - return newopts; -} - -/* create-branch option (either b or c) */ -static char cb_option = 'b'; - -static int checkout_main(int argc, const char **argv, const char *prefix, - struct checkout_opts *opts, struct option *options, - const char * const usagestr[]) -{ - struct branch_info new_branch_info; - int parseopt_flags = 0; - - memset(&new_branch_info, 0, sizeof(new_branch_info)); - opts->overwrite_ignore = 1; - opts->prefix = prefix; - opts->show_progress = -1; - - git_config(git_checkout_config, opts); - - opts->track = BRANCH_TRACK_UNSPECIFIED; - - if (!opts->accept_pathspec && !opts->accept_ref) - BUG("make up your mind, you need to take _something_"); - if (opts->accept_pathspec && opts->accept_ref) - parseopt_flags = PARSE_OPT_KEEP_DASHDASH; - - argc = parse_options(argc, argv, prefix, options, - usagestr, parseopt_flags); - - if (opts->show_progress < 0) { - if (opts->quiet) - opts->show_progress = 0; - else - opts->show_progress = isatty(2); - } - - if (opts->conflict_style) { - opts->merge = 1; /* implied */ - git_xmerge_config("merge.conflictstyle", opts->conflict_style, NULL); - } - if (opts->force) { - opts->discard_changes = 1; - opts->ignore_unmerged_opt = "--force"; - opts->ignore_unmerged = 1; - } - - if ((!!opts->new_branch + !!opts->new_branch_force + !!opts->new_orphan_branch) > 1) - die(_("-%c, -%c and --orphan are mutually exclusive"), - cb_option, toupper(cb_option)); - - if (opts->overlay_mode == 1 && opts->patch_mode) - die(_("-p and --overlay are mutually exclusive")); - - if (opts->checkout_index >= 0 || opts->checkout_worktree >= 0) { - if (opts->checkout_index < 0) - opts->checkout_index = 0; - if (opts->checkout_worktree < 0) - opts->checkout_worktree = 0; - } else { - if (opts->checkout_index < 0) - opts->checkout_index = -opts->checkout_index - 1; - if (opts->checkout_worktree < 0) - opts->checkout_worktree = -opts->checkout_worktree - 1; - } - if (opts->checkout_index < 0 || opts->checkout_worktree < 0) - BUG("these flags should be non-negative by now"); - /* - * convenient shortcut: "git restore --staged [--worktree]" equals - * "git restore --staged [--worktree] --source HEAD" - */ - if (!opts->from_treeish && opts->checkout_index) - opts->from_treeish = "HEAD"; - - /* - * From here on, new_branch will contain the branch to be checked out, - * and new_branch_force and new_orphan_branch will tell us which one of - * -b/-B/-c/-C/--orphan is being used. - */ - if (opts->new_branch_force) - opts->new_branch = opts->new_branch_force; - - if (opts->new_orphan_branch) - opts->new_branch = opts->new_orphan_branch; - - /* --track without -c/-C/-b/-B/--orphan should DWIM */ - if (opts->track != BRANCH_TRACK_UNSPECIFIED && !opts->new_branch) { - const char *argv0 = argv[0]; - if (!argc || !strcmp(argv0, "--")) - die(_("--track needs a branch name")); - skip_prefix(argv0, "refs/", &argv0); - skip_prefix(argv0, "remotes/", &argv0); - argv0 = strchr(argv0, '/'); - if (!argv0 || !argv0[1]) - die(_("missing branch name; try -%c"), cb_option); - opts->new_branch = argv0 + 1; - } - - /* - * Extract branch name from command line arguments, so - * all that is left is pathspecs. - * - * Handle - * - * 1) git checkout <tree> -- [<paths>] - * 2) git checkout -- [<paths>] - * 3) git checkout <something> [<paths>] - * - * including "last branch" syntax and DWIM-ery for names of - * remote branches, erroring out for invalid or ambiguous cases. - */ - if (argc && opts->accept_ref) { - struct object_id rev; - int dwim_ok = - !opts->patch_mode && - opts->dwim_new_local_branch && - opts->track == BRANCH_TRACK_UNSPECIFIED && - !opts->new_branch; - int n = parse_branchname_arg(argc, argv, dwim_ok, - &new_branch_info, opts, &rev); - argv += n; - argc -= n; - } else if (!opts->accept_ref && opts->from_treeish) { - struct object_id rev; - - if (get_oid_mb(opts->from_treeish, &rev)) - die(_("could not resolve %s"), opts->from_treeish); - - setup_new_branch_info_and_source_tree(&new_branch_info, - opts, &rev, - opts->from_treeish); - - if (!opts->source_tree) - die(_("reference is not a tree: %s"), opts->from_treeish); - } - - if (argc) { - parse_pathspec(&opts->pathspec, 0, - opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0, - prefix, argv); - - if (!opts->pathspec.nr) - die(_("invalid path specification")); - - /* - * Try to give more helpful suggestion. - * new_branch && argc > 1 will be caught later. - */ - if (opts->new_branch && argc == 1 && !new_branch_info.commit) - die(_("'%s' is not a commit and a branch '%s' cannot be created from it"), - argv[0], opts->new_branch); - - if (opts->force_detach) - die(_("git checkout: --detach does not take a path argument '%s'"), - argv[0]); - } - - if (opts->pathspec_from_file) { - if (opts->pathspec.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); - - if (opts->force_detach) - die(_("--pathspec-from-file is incompatible with --detach")); - - if (opts->patch_mode) - die(_("--pathspec-from-file is incompatible with --patch")); - - parse_pathspec_file(&opts->pathspec, 0, - 0, - prefix, opts->pathspec_from_file, opts->pathspec_file_nul); - } else if (opts->pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); - } - - opts->pathspec.recursive = 1; - - if (opts->pathspec.nr) { - if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge) - die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n" - "checking out of the index.")); - } else { - if (opts->accept_pathspec && !opts->empty_pathspec_ok && - !opts->patch_mode) /* patch mode is special */ - die(_("you must specify path(s) to restore")); - } - - if (opts->new_branch) { - struct strbuf buf = STRBUF_INIT; - - if (opts->new_branch_force) - opts->branch_exists = validate_branchname(opts->new_branch, &buf); - else - opts->branch_exists = - validate_new_branchname(opts->new_branch, &buf, 0); - strbuf_release(&buf); - } - - UNLEAK(opts); - if (opts->patch_mode || opts->pathspec.nr) - return checkout_paths(opts, &new_branch_info); - else - return checkout_branch(opts, &new_branch_info); -} - -int cmd_checkout(int argc, const char **argv, const char *prefix) -{ - struct checkout_opts opts; - struct option *options; - struct option checkout_options[] = { - OPT_STRING('b', NULL, &opts.new_branch, N_("branch"), - N_("create and checkout a new branch")), - OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"), - N_("create/reset and checkout a branch")), - OPT_BOOL('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")), - OPT_BOOL(0, "guess", &opts.dwim_new_local_branch, - N_("second guess 'git checkout <no-such-branch>' (default)")), - OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")), - OPT_END() - }; - int ret; - - memset(&opts, 0, sizeof(opts)); - opts.dwim_new_local_branch = 1; - opts.switch_branch_doing_nothing_is_ok = 1; - opts.only_merge_on_switching_branches = 0; - opts.accept_ref = 1; - opts.accept_pathspec = 1; - opts.implicit_detach = 1; - opts.can_switch_when_in_progress = 1; - opts.orphan_from_empty_tree = 0; - opts.empty_pathspec_ok = 1; - opts.overlay_mode = -1; - opts.checkout_index = -2; /* default on */ - opts.checkout_worktree = -2; /* default on */ - - if (argc == 3 && !strcmp(argv[1], "-b")) { - /* - * User ran 'git checkout -b <branch>' and expects - * the same behavior as 'git switch -c <branch>'. - */ - opts.switch_branch_doing_nothing_is_ok = 0; - opts.only_merge_on_switching_branches = 1; - } - - options = parse_options_dup(checkout_options); - options = add_common_options(&opts, options); - options = add_common_switch_branch_options(&opts, options); - options = add_checkout_path_options(&opts, options); - - ret = checkout_main(argc, argv, prefix, &opts, - options, checkout_usage); - FREE_AND_NULL(options); - return ret; -} - -int cmd_switch(int argc, const char **argv, const char *prefix) -{ - struct checkout_opts opts; - struct option *options = NULL; - struct option switch_options[] = { - OPT_STRING('c', "create", &opts.new_branch, N_("branch"), - N_("create and switch to a new branch")), - OPT_STRING('C', "force-create", &opts.new_branch_force, N_("branch"), - N_("create/reset and switch to a branch")), - OPT_BOOL(0, "guess", &opts.dwim_new_local_branch, - N_("second guess 'git switch <no-such-branch>'")), - OPT_BOOL(0, "discard-changes", &opts.discard_changes, - N_("throw away local modifications")), - OPT_END() - }; - int ret; - - memset(&opts, 0, sizeof(opts)); - opts.dwim_new_local_branch = 1; - opts.accept_ref = 1; - opts.accept_pathspec = 0; - opts.switch_branch_doing_nothing_is_ok = 0; - opts.only_merge_on_switching_branches = 1; - opts.implicit_detach = 0; - opts.can_switch_when_in_progress = 0; - opts.orphan_from_empty_tree = 1; - opts.overlay_mode = -1; - - options = parse_options_dup(switch_options); - options = add_common_options(&opts, options); - options = add_common_switch_branch_options(&opts, options); - - cb_option = 'c'; - - ret = checkout_main(argc, argv, prefix, &opts, - options, switch_branch_usage); - FREE_AND_NULL(options); - return ret; -} - -int cmd_restore(int argc, const char **argv, const char *prefix) -{ - struct checkout_opts opts; - struct option *options; - struct option restore_options[] = { - OPT_STRING('s', "source", &opts.from_treeish, "<tree-ish>", - N_("which tree-ish to checkout from")), - OPT_BOOL('S', "staged", &opts.checkout_index, - N_("restore the index")), - OPT_BOOL('W', "worktree", &opts.checkout_worktree, - N_("restore the working tree (default)")), - OPT_BOOL(0, "ignore-unmerged", &opts.ignore_unmerged, - N_("ignore unmerged entries")), - OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode")), - OPT_END() - }; - int ret; - - memset(&opts, 0, sizeof(opts)); - opts.accept_ref = 0; - opts.accept_pathspec = 1; - opts.empty_pathspec_ok = 0; - opts.overlay_mode = 0; - opts.checkout_index = -1; /* default off */ - opts.checkout_worktree = -2; /* default on */ - opts.ignore_unmerged_opt = "--ignore-unmerged"; - - options = parse_options_dup(restore_options); - options = add_common_options(&opts, options); - options = add_checkout_path_options(&opts, options); - - ret = checkout_main(argc, argv, prefix, &opts, - options, restore_usage); - FREE_AND_NULL(options); - return ret; -} diff --git a/third_party/git/builtin/clean.c b/third_party/git/builtin/clean.c deleted file mode 100644 index 687ab473c20c..000000000000 --- a/third_party/git/builtin/clean.c +++ /dev/null @@ -1,1073 +0,0 @@ -/* - * "git clean" builtin command - * - * Copyright (C) 2007 Shawn Bohrer - * - * Based on git-clean.sh by Pavel Roskin - */ - -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "dir.h" -#include "parse-options.h" -#include "string-list.h" -#include "quote.h" -#include "column.h" -#include "color.h" -#include "pathspec.h" -#include "help.h" -#include "prompt.h" - -static int force = -1; /* unset */ -static int interactive; -static struct string_list del_list = STRING_LIST_INIT_DUP; -static unsigned int colopts; - -static const char *const builtin_clean_usage[] = { - N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."), - NULL -}; - -static const char *msg_remove = N_("Removing %s\n"); -static const char *msg_would_remove = N_("Would remove %s\n"); -static const char *msg_skip_git_dir = N_("Skipping repository %s\n"); -static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n"); -static const char *msg_warn_remove_failed = N_("failed to remove %s"); -static const char *msg_warn_lstat_failed = N_("could not lstat %s\n"); - -enum color_clean { - CLEAN_COLOR_RESET = 0, - CLEAN_COLOR_PLAIN = 1, - CLEAN_COLOR_PROMPT = 2, - CLEAN_COLOR_HEADER = 3, - CLEAN_COLOR_HELP = 4, - CLEAN_COLOR_ERROR = 5 -}; - -static const char *color_interactive_slots[] = { - [CLEAN_COLOR_ERROR] = "error", - [CLEAN_COLOR_HEADER] = "header", - [CLEAN_COLOR_HELP] = "help", - [CLEAN_COLOR_PLAIN] = "plain", - [CLEAN_COLOR_PROMPT] = "prompt", - [CLEAN_COLOR_RESET] = "reset", -}; - -static int clean_use_color = -1; -static char clean_colors[][COLOR_MAXLEN] = { - [CLEAN_COLOR_ERROR] = GIT_COLOR_BOLD_RED, - [CLEAN_COLOR_HEADER] = GIT_COLOR_BOLD, - [CLEAN_COLOR_HELP] = GIT_COLOR_BOLD_RED, - [CLEAN_COLOR_PLAIN] = GIT_COLOR_NORMAL, - [CLEAN_COLOR_PROMPT] = GIT_COLOR_BOLD_BLUE, - [CLEAN_COLOR_RESET] = GIT_COLOR_RESET, -}; - -#define MENU_OPTS_SINGLETON 01 -#define MENU_OPTS_IMMEDIATE 02 -#define MENU_OPTS_LIST_ONLY 04 - -struct menu_opts { - const char *header; - const char *prompt; - int flags; -}; - -#define MENU_RETURN_NO_LOOP 10 - -struct menu_item { - char hotkey; - const char *title; - int selected; - int (*fn)(void); -}; - -enum menu_stuff_type { - MENU_STUFF_TYPE_STRING_LIST = 1, - MENU_STUFF_TYPE_MENU_ITEM -}; - -struct menu_stuff { - enum menu_stuff_type type; - int nr; - void *stuff; -}; - -define_list_config_array(color_interactive_slots); - -static int git_clean_config(const char *var, const char *value, void *cb) -{ - const char *slot_name; - - if (starts_with(var, "column.")) - return git_column_config(var, value, "clean", &colopts); - - /* honors the color.interactive* config variables which also - applied in git-add--interactive and git-stash */ - if (!strcmp(var, "color.interactive")) { - clean_use_color = git_config_colorbool(var, value); - return 0; - } - if (skip_prefix(var, "color.interactive.", &slot_name)) { - int slot = LOOKUP_CONFIG(color_interactive_slots, slot_name); - if (slot < 0) - return 0; - if (!value) - return config_error_nonbool(var); - return color_parse(value, clean_colors[slot]); - } - - if (!strcmp(var, "clean.requireforce")) { - force = !git_config_bool(var, value); - return 0; - } - - /* inspect the color.ui config variable and others */ - return git_color_default_config(var, value, cb); -} - -static const char *clean_get_color(enum color_clean ix) -{ - if (want_color(clean_use_color)) - return clean_colors[ix]; - return ""; -} - -static void clean_print_color(enum color_clean ix) -{ - printf("%s", clean_get_color(ix)); -} - -static int exclude_cb(const struct option *opt, const char *arg, int unset) -{ - struct string_list *exclude_list = opt->value; - BUG_ON_OPT_NEG(unset); - string_list_append(exclude_list, arg); - return 0; -} - -static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, - int dry_run, int quiet, int *dir_gone) -{ - DIR *dir; - struct strbuf quoted = STRBUF_INIT; - struct dirent *e; - int res = 0, ret = 0, gone = 1, original_len = path->len, len; - struct string_list dels = STRING_LIST_INIT_DUP; - - *dir_gone = 1; - - if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && - is_nonbare_repository_dir(path)) { - if (!quiet) { - quote_path(path->buf, prefix, "ed, 0); - printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir), - quoted.buf); - } - - *dir_gone = 0; - goto out; - } - - dir = opendir(path->buf); - if (!dir) { - /* an empty dir could be removed even if it is unreadble */ - res = dry_run ? 0 : rmdir(path->buf); - if (res) { - int saved_errno = errno; - quote_path(path->buf, prefix, "ed, 0); - errno = saved_errno; - warning_errno(_(msg_warn_remove_failed), quoted.buf); - *dir_gone = 0; - } - ret = res; - goto out; - } - - strbuf_complete(path, '/'); - - len = path->len; - while ((e = readdir(dir)) != NULL) { - struct stat st; - if (is_dot_or_dotdot(e->d_name)) - continue; - - strbuf_setlen(path, len); - strbuf_addstr(path, e->d_name); - if (lstat(path->buf, &st)) - warning_errno(_(msg_warn_lstat_failed), path->buf); - else if (S_ISDIR(st.st_mode)) { - if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone)) - ret = 1; - if (gone) { - quote_path(path->buf, prefix, "ed, 0); - string_list_append(&dels, quoted.buf); - } else - *dir_gone = 0; - continue; - } else { - res = dry_run ? 0 : unlink(path->buf); - if (!res) { - quote_path(path->buf, prefix, "ed, 0); - string_list_append(&dels, quoted.buf); - } else { - int saved_errno = errno; - quote_path(path->buf, prefix, "ed, 0); - errno = saved_errno; - warning_errno(_(msg_warn_remove_failed), quoted.buf); - *dir_gone = 0; - ret = 1; - } - continue; - } - - /* path too long, stat fails, or non-directory still exists */ - *dir_gone = 0; - ret = 1; - break; - } - closedir(dir); - - strbuf_setlen(path, original_len); - - if (*dir_gone) { - res = dry_run ? 0 : rmdir(path->buf); - if (!res) - *dir_gone = 1; - else { - int saved_errno = errno; - quote_path(path->buf, prefix, "ed, 0); - errno = saved_errno; - warning_errno(_(msg_warn_remove_failed), quoted.buf); - *dir_gone = 0; - ret = 1; - } - } - - if (!*dir_gone && !quiet) { - int i; - for (i = 0; i < dels.nr; i++) - printf(dry_run ? _(msg_would_remove) : _(msg_remove), dels.items[i].string); - } -out: - strbuf_release("ed); - string_list_clear(&dels, 0); - return ret; -} - -static void pretty_print_dels(void) -{ - struct string_list list = STRING_LIST_INIT_DUP; - struct string_list_item *item; - struct strbuf buf = STRBUF_INIT; - const char *qname; - struct column_options copts; - - for_each_string_list_item(item, &del_list) { - qname = quote_path(item->string, NULL, &buf, 0); - string_list_append(&list, qname); - } - - /* - * always enable column display, we only consult column.* - * about layout strategy and stuff - */ - colopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED; - memset(&copts, 0, sizeof(copts)); - copts.indent = " "; - copts.padding = 2; - print_columns(&list, colopts, &copts); - strbuf_release(&buf); - string_list_clear(&list, 0); -} - -static void pretty_print_menus(struct string_list *menu_list) -{ - unsigned int local_colopts = 0; - struct column_options copts; - - local_colopts = COL_ENABLED | COL_ROW; - memset(&copts, 0, sizeof(copts)); - copts.indent = " "; - copts.padding = 2; - print_columns(menu_list, local_colopts, &copts); -} - -static void prompt_help_cmd(int singleton) -{ - clean_print_color(CLEAN_COLOR_HELP); - printf(singleton ? - _("Prompt help:\n" - "1 - select a numbered item\n" - "foo - select item based on unique prefix\n" - " - (empty) select nothing\n") : - _("Prompt help:\n" - "1 - select a single item\n" - "3-5 - select a range of items\n" - "2-3,6-9 - select multiple ranges\n" - "foo - select item based on unique prefix\n" - "-... - unselect specified items\n" - "* - choose all items\n" - " - (empty) finish selecting\n")); - clean_print_color(CLEAN_COLOR_RESET); -} - -/* - * display menu stuff with number prefix and hotkey highlight - */ -static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen) -{ - struct string_list menu_list = STRING_LIST_INIT_DUP; - struct strbuf menu = STRBUF_INIT; - struct menu_item *menu_item; - struct string_list_item *string_list_item; - int i; - - switch (stuff->type) { - default: - die("Bad type of menu_stuff when print menu"); - case MENU_STUFF_TYPE_MENU_ITEM: - menu_item = (struct menu_item *)stuff->stuff; - for (i = 0; i < stuff->nr; i++, menu_item++) { - const char *p; - int highlighted = 0; - - p = menu_item->title; - if ((*chosen)[i] < 0) - (*chosen)[i] = menu_item->selected ? 1 : 0; - strbuf_addf(&menu, "%s%2d: ", (*chosen)[i] ? "*" : " ", i+1); - for (; *p; p++) { - if (!highlighted && *p == menu_item->hotkey) { - strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_PROMPT)); - strbuf_addch(&menu, *p); - strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_RESET)); - highlighted = 1; - } else { - strbuf_addch(&menu, *p); - } - } - string_list_append(&menu_list, menu.buf); - strbuf_reset(&menu); - } - break; - case MENU_STUFF_TYPE_STRING_LIST: - i = 0; - for_each_string_list_item(string_list_item, (struct string_list *)stuff->stuff) { - if ((*chosen)[i] < 0) - (*chosen)[i] = 0; - strbuf_addf(&menu, "%s%2d: %s", - (*chosen)[i] ? "*" : " ", i+1, string_list_item->string); - string_list_append(&menu_list, menu.buf); - strbuf_reset(&menu); - i++; - } - break; - } - - pretty_print_menus(&menu_list); - - strbuf_release(&menu); - string_list_clear(&menu_list, 0); -} - -static int find_unique(const char *choice, struct menu_stuff *menu_stuff) -{ - struct menu_item *menu_item; - struct string_list_item *string_list_item; - int i, len, found = 0; - - len = strlen(choice); - switch (menu_stuff->type) { - default: - die("Bad type of menu_stuff when parse choice"); - case MENU_STUFF_TYPE_MENU_ITEM: - - menu_item = (struct menu_item *)menu_stuff->stuff; - for (i = 0; i < menu_stuff->nr; i++, menu_item++) { - if (len == 1 && *choice == menu_item->hotkey) { - found = i + 1; - break; - } - if (!strncasecmp(choice, menu_item->title, len)) { - if (found) { - if (len == 1) { - /* continue for hotkey matching */ - found = -1; - } else { - found = 0; - break; - } - } else { - found = i + 1; - } - } - } - break; - case MENU_STUFF_TYPE_STRING_LIST: - string_list_item = ((struct string_list *)menu_stuff->stuff)->items; - for (i = 0; i < menu_stuff->nr; i++, string_list_item++) { - if (!strncasecmp(choice, string_list_item->string, len)) { - if (found) { - found = 0; - break; - } - found = i + 1; - } - } - break; - } - return found; -} - -/* - * Parse user input, and return choice(s) for menu (menu_stuff). - * - * Input - * (for single choice) - * 1 - select a numbered item - * foo - select item based on menu title - * - (empty) select nothing - * - * (for multiple choice) - * 1 - select a single item - * 3-5 - select a range of items - * 2-3,6-9 - select multiple ranges - * foo - select item based on menu title - * -... - unselect specified items - * * - choose all items - * - (empty) finish selecting - * - * The parse result will be saved in array **chosen, and - * return number of total selections. - */ -static int parse_choice(struct menu_stuff *menu_stuff, - int is_single, - struct strbuf input, - int **chosen) -{ - struct strbuf **choice_list, **ptr; - int nr = 0; - int i; - - if (is_single) { - choice_list = strbuf_split_max(&input, '\n', 0); - } else { - char *p = input.buf; - do { - if (*p == ',') - *p = ' '; - } while (*p++); - choice_list = strbuf_split_max(&input, ' ', 0); - } - - for (ptr = choice_list; *ptr; ptr++) { - char *p; - int choose = 1; - int bottom = 0, top = 0; - int is_range, is_number; - - strbuf_trim(*ptr); - if (!(*ptr)->len) - continue; - - /* Input that begins with '-'; unchoose */ - if (*(*ptr)->buf == '-') { - choose = 0; - strbuf_remove((*ptr), 0, 1); - } - - is_range = 0; - is_number = 1; - for (p = (*ptr)->buf; *p; p++) { - if ('-' == *p) { - if (!is_range) { - is_range = 1; - is_number = 0; - } else { - is_number = 0; - is_range = 0; - break; - } - } else if (!isdigit(*p)) { - is_number = 0; - is_range = 0; - break; - } - } - - if (is_number) { - bottom = atoi((*ptr)->buf); - top = bottom; - } else if (is_range) { - bottom = atoi((*ptr)->buf); - /* a range can be specified like 5-7 or 5- */ - if (!*(strchr((*ptr)->buf, '-') + 1)) - top = menu_stuff->nr; - else - top = atoi(strchr((*ptr)->buf, '-') + 1); - } else if (!strcmp((*ptr)->buf, "*")) { - bottom = 1; - top = menu_stuff->nr; - } else { - bottom = find_unique((*ptr)->buf, menu_stuff); - top = bottom; - } - - if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top || - (is_single && bottom != top)) { - clean_print_color(CLEAN_COLOR_ERROR); - printf(_("Huh (%s)?\n"), (*ptr)->buf); - clean_print_color(CLEAN_COLOR_RESET); - continue; - } - - for (i = bottom; i <= top; i++) - (*chosen)[i-1] = choose; - } - - strbuf_list_free(choice_list); - - for (i = 0; i < menu_stuff->nr; i++) - nr += (*chosen)[i]; - return nr; -} - -/* - * Implement a git-add-interactive compatible UI, which is borrowed - * from git-add--interactive.perl. - * - * Return value: - * - * - Return an array of integers - * - , and it is up to you to free the allocated memory. - * - The array ends with EOF. - * - If user pressed CTRL-D (i.e. EOF), no selection returned. - */ -static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff) -{ - struct strbuf choice = STRBUF_INIT; - int *chosen, *result; - int nr = 0; - int eof = 0; - int i; - - ALLOC_ARRAY(chosen, stuff->nr); - /* set chosen as uninitialized */ - for (i = 0; i < stuff->nr; i++) - chosen[i] = -1; - - for (;;) { - if (opts->header) { - printf_ln("%s%s%s", - clean_get_color(CLEAN_COLOR_HEADER), - _(opts->header), - clean_get_color(CLEAN_COLOR_RESET)); - } - - /* chosen will be initialized by print_highlight_menu_stuff */ - print_highlight_menu_stuff(stuff, &chosen); - - if (opts->flags & MENU_OPTS_LIST_ONLY) - break; - - if (opts->prompt) { - printf("%s%s%s%s", - clean_get_color(CLEAN_COLOR_PROMPT), - _(opts->prompt), - opts->flags & MENU_OPTS_SINGLETON ? "> " : ">> ", - clean_get_color(CLEAN_COLOR_RESET)); - } - - if (git_read_line_interactively(&choice) == EOF) { - eof = 1; - break; - } - - /* help for prompt */ - if (!strcmp(choice.buf, "?")) { - prompt_help_cmd(opts->flags & MENU_OPTS_SINGLETON); - continue; - } - - /* for a multiple-choice menu, press ENTER (empty) will return back */ - if (!(opts->flags & MENU_OPTS_SINGLETON) && !choice.len) - break; - - nr = parse_choice(stuff, - opts->flags & MENU_OPTS_SINGLETON, - choice, - &chosen); - - if (opts->flags & MENU_OPTS_SINGLETON) { - if (nr) - break; - } else if (opts->flags & MENU_OPTS_IMMEDIATE) { - break; - } - } - - if (eof) { - result = xmalloc(sizeof(int)); - *result = EOF; - } else { - int j = 0; - - /* - * recalculate nr, if return back from menu directly with - * default selections. - */ - if (!nr) { - for (i = 0; i < stuff->nr; i++) - nr += chosen[i]; - } - - result = xcalloc(st_add(nr, 1), sizeof(int)); - for (i = 0; i < stuff->nr && j < nr; i++) { - if (chosen[i]) - result[j++] = i; - } - result[j] = EOF; - } - - free(chosen); - strbuf_release(&choice); - return result; -} - -static int clean_cmd(void) -{ - return MENU_RETURN_NO_LOOP; -} - -static int filter_by_patterns_cmd(void) -{ - struct dir_struct dir; - struct strbuf confirm = STRBUF_INIT; - struct strbuf **ignore_list; - struct string_list_item *item; - struct pattern_list *pl; - int changed = -1, i; - - for (;;) { - if (!del_list.nr) - break; - - if (changed) - pretty_print_dels(); - - clean_print_color(CLEAN_COLOR_PROMPT); - printf(_("Input ignore patterns>> ")); - clean_print_color(CLEAN_COLOR_RESET); - if (git_read_line_interactively(&confirm) == EOF) - putchar('\n'); - - /* quit filter_by_pattern mode if press ENTER or Ctrl-D */ - if (!confirm.len) - break; - - dir_init(&dir); - pl = add_pattern_list(&dir, EXC_CMDL, "manual exclude"); - ignore_list = strbuf_split_max(&confirm, ' ', 0); - - for (i = 0; ignore_list[i]; i++) { - strbuf_trim(ignore_list[i]); - if (!ignore_list[i]->len) - continue; - - add_pattern(ignore_list[i]->buf, "", 0, pl, -(i+1)); - } - - changed = 0; - for_each_string_list_item(item, &del_list) { - int dtype = DT_UNKNOWN; - - if (is_excluded(&dir, &the_index, item->string, &dtype)) { - *item->string = '\0'; - changed++; - } - } - - if (changed) { - string_list_remove_empty_items(&del_list, 0); - } else { - clean_print_color(CLEAN_COLOR_ERROR); - printf_ln(_("WARNING: Cannot find items matched by: %s"), confirm.buf); - clean_print_color(CLEAN_COLOR_RESET); - } - - strbuf_list_free(ignore_list); - dir_clear(&dir); - } - - strbuf_release(&confirm); - return 0; -} - -static int select_by_numbers_cmd(void) -{ - struct menu_opts menu_opts; - struct menu_stuff menu_stuff; - struct string_list_item *items; - int *chosen; - int i, j; - - menu_opts.header = NULL; - menu_opts.prompt = N_("Select items to delete"); - menu_opts.flags = 0; - - menu_stuff.type = MENU_STUFF_TYPE_STRING_LIST; - menu_stuff.stuff = &del_list; - menu_stuff.nr = del_list.nr; - - chosen = list_and_choose(&menu_opts, &menu_stuff); - items = del_list.items; - for (i = 0, j = 0; i < del_list.nr; i++) { - if (i < chosen[j]) { - *(items[i].string) = '\0'; - } else if (i == chosen[j]) { - /* delete selected item */ - j++; - continue; - } else { - /* end of chosen (chosen[j] == EOF), won't delete */ - *(items[i].string) = '\0'; - } - } - - string_list_remove_empty_items(&del_list, 0); - - free(chosen); - return 0; -} - -static int ask_each_cmd(void) -{ - struct strbuf confirm = STRBUF_INIT; - struct strbuf buf = STRBUF_INIT; - struct string_list_item *item; - const char *qname; - int changed = 0, eof = 0; - - for_each_string_list_item(item, &del_list) { - /* Ctrl-D should stop removing files */ - if (!eof) { - qname = quote_path(item->string, NULL, &buf, 0); - /* TRANSLATORS: Make sure to keep [y/N] as is */ - printf(_("Remove %s [y/N]? "), qname); - if (git_read_line_interactively(&confirm) == EOF) { - putchar('\n'); - eof = 1; - } - } - if (!confirm.len || strncasecmp(confirm.buf, "yes", confirm.len)) { - *item->string = '\0'; - changed++; - } - } - - if (changed) - string_list_remove_empty_items(&del_list, 0); - - strbuf_release(&buf); - strbuf_release(&confirm); - return MENU_RETURN_NO_LOOP; -} - -static int quit_cmd(void) -{ - string_list_clear(&del_list, 0); - printf(_("Bye.\n")); - return MENU_RETURN_NO_LOOP; -} - -static int help_cmd(void) -{ - clean_print_color(CLEAN_COLOR_HELP); - printf_ln(_( - "clean - start cleaning\n" - "filter by pattern - exclude items from deletion\n" - "select by numbers - select items to be deleted by numbers\n" - "ask each - confirm each deletion (like \"rm -i\")\n" - "quit - stop cleaning\n" - "help - this screen\n" - "? - help for prompt selection" - )); - clean_print_color(CLEAN_COLOR_RESET); - return 0; -} - -static void interactive_main_loop(void) -{ - while (del_list.nr) { - struct menu_opts menu_opts; - struct menu_stuff menu_stuff; - struct menu_item menus[] = { - {'c', "clean", 0, clean_cmd}, - {'f', "filter by pattern", 0, filter_by_patterns_cmd}, - {'s', "select by numbers", 0, select_by_numbers_cmd}, - {'a', "ask each", 0, ask_each_cmd}, - {'q', "quit", 0, quit_cmd}, - {'h', "help", 0, help_cmd}, - }; - int *chosen; - - menu_opts.header = N_("*** Commands ***"); - menu_opts.prompt = N_("What now"); - menu_opts.flags = MENU_OPTS_SINGLETON; - - menu_stuff.type = MENU_STUFF_TYPE_MENU_ITEM; - menu_stuff.stuff = menus; - menu_stuff.nr = sizeof(menus) / sizeof(struct menu_item); - - clean_print_color(CLEAN_COLOR_HEADER); - printf_ln(Q_("Would remove the following item:", - "Would remove the following items:", - del_list.nr)); - clean_print_color(CLEAN_COLOR_RESET); - - pretty_print_dels(); - - chosen = list_and_choose(&menu_opts, &menu_stuff); - - if (*chosen != EOF) { - int ret; - ret = menus[*chosen].fn(); - if (ret != MENU_RETURN_NO_LOOP) { - FREE_AND_NULL(chosen); - if (!del_list.nr) { - clean_print_color(CLEAN_COLOR_ERROR); - printf_ln(_("No more files to clean, exiting.")); - clean_print_color(CLEAN_COLOR_RESET); - break; - } - continue; - } - } else { - quit_cmd(); - } - - FREE_AND_NULL(chosen); - break; - } -} - -static void correct_untracked_entries(struct dir_struct *dir) -{ - int src, dst, ign; - - for (src = dst = ign = 0; src < dir->nr; src++) { - /* skip paths in ignored[] that cannot be inside entries[src] */ - while (ign < dir->ignored_nr && - 0 <= cmp_dir_entry(&dir->entries[src], &dir->ignored[ign])) - ign++; - - if (ign < dir->ignored_nr && - check_dir_entry_contains(dir->entries[src], dir->ignored[ign])) { - /* entries[src] contains an ignored path, so we drop it */ - free(dir->entries[src]); - } else { - struct dir_entry *ent = dir->entries[src++]; - - /* entries[src] does not contain an ignored path, so we keep it */ - dir->entries[dst++] = ent; - - /* then discard paths in entries[] contained inside entries[src] */ - while (src < dir->nr && - check_dir_entry_contains(ent, dir->entries[src])) - free(dir->entries[src++]); - - /* compensate for the outer loop's loop control */ - src--; - } - } - dir->nr = dst; -} - -int cmd_clean(int argc, const char **argv, const char *prefix) -{ - int i, res; - int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0; - int ignored_only = 0, config_set = 0, errors = 0, gone = 1; - int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT; - struct strbuf abs_path = STRBUF_INIT; - struct dir_struct dir; - struct pathspec pathspec; - struct strbuf buf = STRBUF_INIT; - struct string_list exclude_list = STRING_LIST_INIT_NODUP; - struct pattern_list *pl; - struct string_list_item *item; - const char *qname; - struct option options[] = { - OPT__QUIET(&quiet, N_("do not print names of files removed")), - OPT__DRY_RUN(&dry_run, N_("dry run")), - OPT__FORCE(&force, N_("force"), PARSE_OPT_NOCOMPLETE), - OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")), - OPT_BOOL('d', NULL, &remove_directories, - N_("remove whole directories")), - OPT_CALLBACK_F('e', "exclude", &exclude_list, N_("pattern"), - N_("add <pattern> to ignore rules"), PARSE_OPT_NONEG, exclude_cb), - OPT_BOOL('x', NULL, &ignored, N_("remove ignored files, too")), - OPT_BOOL('X', NULL, &ignored_only, - N_("remove only ignored files")), - OPT_END() - }; - - git_config(git_clean_config, NULL); - if (force < 0) - force = 0; - else - config_set = 1; - - argc = parse_options(argc, argv, prefix, options, builtin_clean_usage, - 0); - - dir_init(&dir); - if (!interactive && !dry_run && !force) { - if (config_set) - die(_("clean.requireForce set to true and neither -i, -n, nor -f given; " - "refusing to clean")); - else - die(_("clean.requireForce defaults to true and neither -i, -n, nor -f given;" - " refusing to clean")); - } - - if (force > 1) - rm_flags = 0; - else - dir.flags |= DIR_SKIP_NESTED_GIT; - - dir.flags |= DIR_SHOW_OTHER_DIRECTORIES; - - if (ignored && ignored_only) - die(_("-x and -X cannot be used together")); - if (!ignored) - setup_standard_excludes(&dir); - if (ignored_only) - dir.flags |= DIR_SHOW_IGNORED; - - if (argc) { - /* - * Remaining args implies pathspecs specified, and we should - * recurse within those. - */ - remove_directories = 1; - } - - if (remove_directories && !ignored_only) { - /* - * We need to know about ignored files too: - * - * If (ignored), then we will delete ignored files as well. - * - * If (!ignored), then even though we not are doing - * anything with ignored files, we need to know about them - * so that we can avoid deleting a directory of untracked - * files that also contains an ignored file within it. - * - * For the (!ignored) case, since we only need to avoid - * deleting ignored files, we can set - * DIR_SHOW_IGNORED_TOO_MODE_MATCHING in order to avoid - * recursing into a directory which is itself ignored. - */ - dir.flags |= DIR_SHOW_IGNORED_TOO; - if (!ignored) - dir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING; - - /* - * Let the fill_directory() machinery know that we aren't - * just recursing to collect the ignored files; we want all - * the untracked ones so that we can delete them. (Note: - * we could also set DIR_KEEP_UNTRACKED_CONTENTS when - * ignored_only is true, since DIR_KEEP_UNTRACKED_CONTENTS - * only has effect in combination with DIR_SHOW_IGNORED_TOO. It makes - * the code clearer to exclude it, though. - */ - dir.flags |= DIR_KEEP_UNTRACKED_CONTENTS; - } - - if (read_cache() < 0) - die(_("index file corrupt")); - - pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option"); - for (i = 0; i < exclude_list.nr; i++) - add_pattern(exclude_list.items[i].string, "", 0, pl, -(i+1)); - - parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_CWD, - prefix, argv); - - fill_directory(&dir, &the_index, &pathspec); - correct_untracked_entries(&dir); - - for (i = 0; i < dir.nr; i++) { - struct dir_entry *ent = dir.entries[i]; - int matches = 0; - struct stat st; - const char *rel; - - if (!cache_name_is_other(ent->name, ent->len)) - continue; - - if (lstat(ent->name, &st)) - die_errno("Cannot lstat '%s'", ent->name); - - if (S_ISDIR(st.st_mode) && !remove_directories && - matches != MATCHED_EXACTLY) - continue; - - rel = relative_path(ent->name, prefix, &buf); - string_list_append(&del_list, rel); - } - - dir_clear(&dir); - - if (interactive && del_list.nr > 0) - interactive_main_loop(); - - for_each_string_list_item(item, &del_list) { - struct stat st; - - strbuf_reset(&abs_path); - if (prefix) - strbuf_addstr(&abs_path, prefix); - - strbuf_addstr(&abs_path, item->string); - - /* - * we might have removed this as part of earlier - * recursive directory removal, so lstat() here could - * fail with ENOENT. - */ - if (lstat(abs_path.buf, &st)) - continue; - - if (S_ISDIR(st.st_mode)) { - if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone)) - errors++; - if (gone && !quiet) { - qname = quote_path(item->string, NULL, &buf, 0); - printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname); - } - } else { - res = dry_run ? 0 : unlink(abs_path.buf); - if (res) { - int saved_errno = errno; - qname = quote_path(item->string, NULL, &buf, 0); - errno = saved_errno; - warning_errno(_(msg_warn_remove_failed), qname); - errors++; - } else if (!quiet) { - qname = quote_path(item->string, NULL, &buf, 0); - printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname); - } - } - } - - strbuf_release(&abs_path); - strbuf_release(&buf); - string_list_clear(&del_list, 0); - string_list_clear(&exclude_list, 0); - return (errors != 0); -} diff --git a/third_party/git/builtin/clone.c b/third_party/git/builtin/clone.c deleted file mode 100644 index 391aa41075ee..000000000000 --- a/third_party/git/builtin/clone.c +++ /dev/null @@ -1,1337 +0,0 @@ -/* - * Builtin "git clone" - * - * Copyright (c) 2007 Kristian Hรธgsberg <krh@redhat.com>, - * 2008 Daniel Barkalow <barkalow@iabervon.org> - * Based on git-commit.sh by Junio C Hamano and Linus Torvalds - * - * Clone a repository into a different directory that does not yet exist. - */ - -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "config.h" -#include "lockfile.h" -#include "parse-options.h" -#include "fetch-pack.h" -#include "refs.h" -#include "refspec.h" -#include "object-store.h" -#include "tree.h" -#include "tree-walk.h" -#include "unpack-trees.h" -#include "transport.h" -#include "strbuf.h" -#include "dir.h" -#include "dir-iterator.h" -#include "iterator.h" -#include "sigchain.h" -#include "branch.h" -#include "remote.h" -#include "run-command.h" -#include "connected.h" -#include "packfile.h" -#include "list-objects-filter-options.h" - -/* - * Overall FIXMEs: - * - respect DB_ENVIRONMENT for .git/objects. - * - * Implementation notes: - * - dropping use-separate-remote and no-separate-remote compatibility - * - */ -static const char * const builtin_clone_usage[] = { - N_("git clone [<options>] [--] <repo> [<dir>]"), - NULL -}; - -static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1; -static int option_local = -1, option_no_hardlinks, option_shared; -static int option_no_tags; -static int option_shallow_submodules; -static int deepen; -static char *option_template, *option_depth, *option_since; -static char *option_origin = NULL; -static char *option_branch = NULL; -static struct string_list option_not = STRING_LIST_INIT_NODUP; -static const char *real_git_dir; -static char *option_upload_pack = "git-upload-pack"; -static int option_verbosity; -static int option_progress = -1; -static int option_sparse_checkout; -static enum transport_family family; -static struct string_list option_config = STRING_LIST_INIT_NODUP; -static struct string_list option_required_reference = STRING_LIST_INIT_NODUP; -static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP; -static int option_dissociate; -static int max_jobs = -1; -static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP; -static struct list_objects_filter_options filter_options; -static struct string_list server_options = STRING_LIST_INIT_NODUP; -static int option_remote_submodules; - -static int recurse_submodules_cb(const struct option *opt, - const char *arg, int unset) -{ - if (unset) - string_list_clear((struct string_list *)opt->value, 0); - else if (arg) - string_list_append((struct string_list *)opt->value, arg); - else - string_list_append((struct string_list *)opt->value, - (const char *)opt->defval); - - return 0; -} - -static struct option builtin_clone_options[] = { - OPT__VERBOSITY(&option_verbosity), - OPT_BOOL(0, "progress", &option_progress, - N_("force progress reporting")), - OPT_BOOL('n', "no-checkout", &option_no_checkout, - N_("don't create a checkout")), - OPT_BOOL(0, "bare", &option_bare, N_("create a bare repository")), - OPT_HIDDEN_BOOL(0, "naked", &option_bare, - N_("create a bare repository")), - OPT_BOOL(0, "mirror", &option_mirror, - N_("create a mirror repository (implies bare)")), - OPT_BOOL('l', "local", &option_local, - N_("to clone from a local repository")), - OPT_BOOL(0, "no-hardlinks", &option_no_hardlinks, - N_("don't use local hardlinks, always copy")), - OPT_BOOL('s', "shared", &option_shared, - N_("setup as shared repository")), - { OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules, - N_("pathspec"), N_("initialize submodules in the clone"), - PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." }, - OPT_ALIAS(0, "recursive", "recurse-submodules"), - OPT_INTEGER('j', "jobs", &max_jobs, - N_("number of submodules cloned in parallel")), - OPT_STRING(0, "template", &option_template, N_("template-directory"), - N_("directory from which templates will be used")), - OPT_STRING_LIST(0, "reference", &option_required_reference, N_("repo"), - N_("reference repository")), - OPT_STRING_LIST(0, "reference-if-able", &option_optional_reference, - N_("repo"), N_("reference repository")), - OPT_BOOL(0, "dissociate", &option_dissociate, - N_("use --reference only while cloning")), - OPT_STRING('o', "origin", &option_origin, N_("name"), - N_("use <name> instead of 'origin' to track upstream")), - OPT_STRING('b', "branch", &option_branch, N_("branch"), - N_("checkout <branch> instead of the remote's HEAD")), - OPT_STRING('u', "upload-pack", &option_upload_pack, N_("path"), - N_("path to git-upload-pack on the remote")), - OPT_STRING(0, "depth", &option_depth, N_("depth"), - N_("create a shallow clone of that depth")), - OPT_STRING(0, "shallow-since", &option_since, N_("time"), - N_("create a shallow clone since a specific time")), - OPT_STRING_LIST(0, "shallow-exclude", &option_not, N_("revision"), - N_("deepen history of shallow clone, excluding rev")), - OPT_BOOL(0, "single-branch", &option_single_branch, - N_("clone only one branch, HEAD or --branch")), - OPT_BOOL(0, "no-tags", &option_no_tags, - N_("don't clone any tags, and make later fetches not to follow them")), - OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules, - N_("any cloned submodules will be shallow")), - OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), - N_("separate git dir from working tree")), - OPT_STRING_LIST('c', "config", &option_config, N_("key=value"), - N_("set config inside the new repository")), - OPT_STRING_LIST(0, "server-option", &server_options, - N_("server-specific"), N_("option to transmit")), - OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), - TRANSPORT_FAMILY_IPV4), - OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), - TRANSPORT_FAMILY_IPV6), - OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), - OPT_BOOL(0, "remote-submodules", &option_remote_submodules, - N_("any cloned submodules will use their remote-tracking branch")), - OPT_BOOL(0, "sparse", &option_sparse_checkout, - N_("initialize sparse-checkout file to include only files at root")), - OPT_END() -}; - -static const char *get_repo_path_1(struct strbuf *path, int *is_bundle) -{ - static char *suffix[] = { "/.git", "", ".git/.git", ".git" }; - static char *bundle_suffix[] = { ".bundle", "" }; - size_t baselen = path->len; - struct stat st; - int i; - - for (i = 0; i < ARRAY_SIZE(suffix); i++) { - strbuf_setlen(path, baselen); - strbuf_addstr(path, suffix[i]); - if (stat(path->buf, &st)) - continue; - if (S_ISDIR(st.st_mode) && is_git_directory(path->buf)) { - *is_bundle = 0; - return path->buf; - } else if (S_ISREG(st.st_mode) && st.st_size > 8) { - /* Is it a "gitfile"? */ - char signature[8]; - const char *dst; - int len, fd = open(path->buf, O_RDONLY); - if (fd < 0) - continue; - len = read_in_full(fd, signature, 8); - close(fd); - if (len != 8 || strncmp(signature, "gitdir: ", 8)) - continue; - dst = read_gitfile(path->buf); - if (dst) { - *is_bundle = 0; - return dst; - } - } - } - - for (i = 0; i < ARRAY_SIZE(bundle_suffix); i++) { - strbuf_setlen(path, baselen); - strbuf_addstr(path, bundle_suffix[i]); - if (!stat(path->buf, &st) && S_ISREG(st.st_mode)) { - *is_bundle = 1; - return path->buf; - } - } - - return NULL; -} - -static char *get_repo_path(const char *repo, int *is_bundle) -{ - struct strbuf path = STRBUF_INIT; - const char *raw; - char *canon; - - strbuf_addstr(&path, repo); - raw = get_repo_path_1(&path, is_bundle); - canon = raw ? absolute_pathdup(raw) : NULL; - strbuf_release(&path); - return canon; -} - -static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) -{ - const char *end = repo + strlen(repo), *start, *ptr; - size_t len; - char *dir; - - /* - * Skip scheme. - */ - start = strstr(repo, "://"); - if (start == NULL) - start = repo; - else - start += 3; - - /* - * Skip authentication data. The stripping does happen - * greedily, such that we strip up to the last '@' inside - * the host part. - */ - for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) { - if (*ptr == '@') - start = ptr + 1; - } - - /* - * Strip trailing spaces, slashes and /.git - */ - while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1]))) - end--; - if (end - start > 5 && is_dir_sep(end[-5]) && - !strncmp(end - 4, ".git", 4)) { - end -= 5; - while (start < end && is_dir_sep(end[-1])) - end--; - } - - /* - * Strip trailing port number if we've got only a - * hostname (that is, there is no dir separator but a - * colon). This check is required such that we do not - * strip URI's like '/foo/bar:2222.git', which should - * result in a dir '2222' being guessed due to backwards - * compatibility. - */ - if (memchr(start, '/', end - start) == NULL - && memchr(start, ':', end - start) != NULL) { - ptr = end; - while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':') - ptr--; - if (start < ptr && ptr[-1] == ':') - end = ptr - 1; - } - - /* - * Find last component. To remain backwards compatible we - * also regard colons as path separators, such that - * cloning a repository 'foo:bar.git' would result in a - * directory 'bar' being guessed. - */ - ptr = end; - while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':') - ptr--; - start = ptr; - - /* - * Strip .{bundle,git}. - */ - len = end - start; - strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git"); - - if (!len || (len == 1 && *start == '/')) - die(_("No directory name could be guessed.\n" - "Please specify a directory on the command line")); - - if (is_bare) - dir = xstrfmt("%.*s.git", (int)len, start); - else - dir = xstrndup(start, len); - /* - * Replace sequences of 'control' characters and whitespace - * with one ascii space, remove leading and trailing spaces. - */ - if (*dir) { - char *out = dir; - int prev_space = 1 /* strip leading whitespace */; - for (end = dir; *end; ++end) { - char ch = *end; - if ((unsigned char)ch < '\x20') - ch = '\x20'; - if (isspace(ch)) { - if (prev_space) - continue; - prev_space = 1; - } else - prev_space = 0; - *out++ = ch; - } - *out = '\0'; - if (out > dir && prev_space) - out[-1] = '\0'; - } - return dir; -} - -static void strip_trailing_slashes(char *dir) -{ - char *end = dir + strlen(dir); - - while (dir < end - 1 && is_dir_sep(end[-1])) - end--; - *end = '\0'; -} - -static int add_one_reference(struct string_list_item *item, void *cb_data) -{ - struct strbuf err = STRBUF_INIT; - int *required = cb_data; - char *ref_git = compute_alternate_path(item->string, &err); - - if (!ref_git) { - if (*required) - die("%s", err.buf); - else - fprintf(stderr, - _("info: Could not add alternate for '%s': %s\n"), - item->string, err.buf); - } else { - struct strbuf sb = STRBUF_INIT; - strbuf_addf(&sb, "%s/objects", ref_git); - add_to_alternates_file(sb.buf); - strbuf_release(&sb); - } - - strbuf_release(&err); - free(ref_git); - return 0; -} - -static void setup_reference(void) -{ - int required = 1; - for_each_string_list(&option_required_reference, - add_one_reference, &required); - required = 0; - for_each_string_list(&option_optional_reference, - add_one_reference, &required); -} - -static void copy_alternates(struct strbuf *src, const char *src_repo) -{ - /* - * Read from the source objects/info/alternates file - * and copy the entries to corresponding file in the - * destination repository with add_to_alternates_file(). - * Both src and dst have "$path/objects/info/alternates". - * - * Instead of copying bit-for-bit from the original, - * we need to append to existing one so that the already - * created entry via "clone -s" is not lost, and also - * to turn entries with paths relative to the original - * absolute, so that they can be used in the new repository. - */ - FILE *in = xfopen(src->buf, "r"); - struct strbuf line = STRBUF_INIT; - - while (strbuf_getline(&line, in) != EOF) { - char *abs_path; - if (!line.len || line.buf[0] == '#') - continue; - if (is_absolute_path(line.buf)) { - add_to_alternates_file(line.buf); - continue; - } - abs_path = mkpathdup("%s/objects/%s", src_repo, line.buf); - if (!normalize_path_copy(abs_path, abs_path)) - add_to_alternates_file(abs_path); - else - warning("skipping invalid relative alternate: %s/%s", - src_repo, line.buf); - free(abs_path); - } - strbuf_release(&line); - fclose(in); -} - -static void mkdir_if_missing(const char *pathname, mode_t mode) -{ - struct stat st; - - if (!mkdir(pathname, mode)) - return; - - if (errno != EEXIST) - die_errno(_("failed to create directory '%s'"), pathname); - else if (stat(pathname, &st)) - die_errno(_("failed to stat '%s'"), pathname); - else if (!S_ISDIR(st.st_mode)) - die(_("%s exists and is not a directory"), pathname); -} - -static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest, - const char *src_repo) -{ - int src_len, dest_len; - struct dir_iterator *iter; - int iter_status; - unsigned int flags; - struct strbuf realpath = STRBUF_INIT; - - mkdir_if_missing(dest->buf, 0777); - - flags = DIR_ITERATOR_PEDANTIC | DIR_ITERATOR_FOLLOW_SYMLINKS; - iter = dir_iterator_begin(src->buf, flags); - - if (!iter) - die_errno(_("failed to start iterator over '%s'"), src->buf); - - strbuf_addch(src, '/'); - src_len = src->len; - strbuf_addch(dest, '/'); - dest_len = dest->len; - - while ((iter_status = dir_iterator_advance(iter)) == ITER_OK) { - strbuf_setlen(src, src_len); - strbuf_addstr(src, iter->relative_path); - strbuf_setlen(dest, dest_len); - strbuf_addstr(dest, iter->relative_path); - - if (S_ISDIR(iter->st.st_mode)) { - mkdir_if_missing(dest->buf, 0777); - continue; - } - - /* Files that cannot be copied bit-for-bit... */ - if (!fspathcmp(iter->relative_path, "info/alternates")) { - copy_alternates(src, src_repo); - continue; - } - - if (unlink(dest->buf) && errno != ENOENT) - die_errno(_("failed to unlink '%s'"), dest->buf); - if (!option_no_hardlinks) { - strbuf_realpath(&realpath, src->buf, 1); - if (!link(realpath.buf, dest->buf)) - continue; - if (option_local > 0) - die_errno(_("failed to create link '%s'"), dest->buf); - option_no_hardlinks = 1; - } - if (copy_file_with_time(dest->buf, src->buf, 0666)) - die_errno(_("failed to copy file to '%s'"), dest->buf); - } - - if (iter_status != ITER_DONE) { - strbuf_setlen(src, src_len); - die(_("failed to iterate over '%s'"), src->buf); - } - - strbuf_release(&realpath); -} - -static void clone_local(const char *src_repo, const char *dest_repo) -{ - if (option_shared) { - struct strbuf alt = STRBUF_INIT; - get_common_dir(&alt, src_repo); - strbuf_addstr(&alt, "/objects"); - add_to_alternates_file(alt.buf); - strbuf_release(&alt); - } else { - struct strbuf src = STRBUF_INIT; - struct strbuf dest = STRBUF_INIT; - get_common_dir(&src, src_repo); - get_common_dir(&dest, dest_repo); - strbuf_addstr(&src, "/objects"); - strbuf_addstr(&dest, "/objects"); - copy_or_link_directory(&src, &dest, src_repo); - strbuf_release(&src); - strbuf_release(&dest); - } - - if (0 <= option_verbosity) - fprintf(stderr, _("done.\n")); -} - -static const char *junk_work_tree; -static int junk_work_tree_flags; -static const char *junk_git_dir; -static int junk_git_dir_flags; -static enum { - JUNK_LEAVE_NONE, - JUNK_LEAVE_REPO, - JUNK_LEAVE_ALL -} junk_mode = JUNK_LEAVE_NONE; - -static const char junk_leave_repo_msg[] = -N_("Clone succeeded, but checkout failed.\n" - "You can inspect what was checked out with 'git status'\n" - "and retry with 'git restore --source=HEAD :/'\n"); - -static void remove_junk(void) -{ - struct strbuf sb = STRBUF_INIT; - - switch (junk_mode) { - case JUNK_LEAVE_REPO: - warning("%s", _(junk_leave_repo_msg)); - /* fall-through */ - case JUNK_LEAVE_ALL: - return; - default: - /* proceed to removal */ - break; - } - - if (junk_git_dir) { - strbuf_addstr(&sb, junk_git_dir); - remove_dir_recursively(&sb, junk_git_dir_flags); - strbuf_reset(&sb); - } - if (junk_work_tree) { - strbuf_addstr(&sb, junk_work_tree); - remove_dir_recursively(&sb, junk_work_tree_flags); - } - strbuf_release(&sb); -} - -static void remove_junk_on_signal(int signo) -{ - remove_junk(); - sigchain_pop(signo); - raise(signo); -} - -static struct ref *find_remote_branch(const struct ref *refs, const char *branch) -{ - struct ref *ref; - struct strbuf head = STRBUF_INIT; - strbuf_addstr(&head, "refs/heads/"); - strbuf_addstr(&head, branch); - ref = find_ref_by_name(refs, head.buf); - strbuf_release(&head); - - if (ref) - return ref; - - strbuf_addstr(&head, "refs/tags/"); - strbuf_addstr(&head, branch); - ref = find_ref_by_name(refs, head.buf); - strbuf_release(&head); - - return ref; -} - -static struct ref *wanted_peer_refs(const struct ref *refs, - struct refspec *refspec) -{ - struct ref *head = copy_ref(find_ref_by_name(refs, "HEAD")); - struct ref *local_refs = head; - struct ref **tail = head ? &head->next : &local_refs; - - if (option_single_branch) { - struct ref *remote_head = NULL; - - if (!option_branch) - remote_head = guess_remote_head(head, refs, 0); - else { - local_refs = NULL; - tail = &local_refs; - remote_head = copy_ref(find_remote_branch(refs, option_branch)); - } - - if (!remote_head && option_branch) - warning(_("Could not find remote branch %s to clone."), - option_branch); - else { - int i; - for (i = 0; i < refspec->nr; i++) - get_fetch_map(remote_head, &refspec->items[i], - &tail, 0); - - /* if --branch=tag, pull the requested tag explicitly */ - get_fetch_map(remote_head, tag_refspec, &tail, 0); - } - } else { - int i; - for (i = 0; i < refspec->nr; i++) - get_fetch_map(refs, &refspec->items[i], &tail, 0); - } - - if (!option_mirror && !option_single_branch && !option_no_tags) - get_fetch_map(refs, tag_refspec, &tail, 0); - - return local_refs; -} - -static void write_remote_refs(const struct ref *local_refs) -{ - const struct ref *r; - - struct ref_transaction *t; - struct strbuf err = STRBUF_INIT; - - t = ref_transaction_begin(&err); - if (!t) - die("%s", err.buf); - - for (r = local_refs; r; r = r->next) { - if (!r->peer_ref) - continue; - if (ref_transaction_create(t, r->peer_ref->name, &r->old_oid, - 0, NULL, &err)) - die("%s", err.buf); - } - - if (initial_ref_transaction_commit(t, &err)) - die("%s", err.buf); - - strbuf_release(&err); - ref_transaction_free(t); -} - -static void write_followtags(const struct ref *refs, const char *msg) -{ - const struct ref *ref; - for (ref = refs; ref; ref = ref->next) { - if (!starts_with(ref->name, "refs/tags/")) - continue; - if (ends_with(ref->name, "^{}")) - continue; - if (!has_object_file_with_flags(&ref->old_oid, - OBJECT_INFO_QUICK | - OBJECT_INFO_SKIP_FETCH_OBJECT)) - continue; - update_ref(msg, ref->name, &ref->old_oid, NULL, 0, - UPDATE_REFS_DIE_ON_ERR); - } -} - -static int iterate_ref_map(void *cb_data, struct object_id *oid) -{ - struct ref **rm = cb_data; - struct ref *ref = *rm; - - /* - * Skip anything missing a peer_ref, which we are not - * actually going to write a ref for. - */ - while (ref && !ref->peer_ref) - ref = ref->next; - /* Returning -1 notes "end of list" to the caller. */ - if (!ref) - return -1; - - oidcpy(oid, &ref->old_oid); - *rm = ref->next; - return 0; -} - -static void update_remote_refs(const struct ref *refs, - const struct ref *mapped_refs, - const struct ref *remote_head_points_at, - const char *branch_top, - const char *msg, - struct transport *transport, - int check_connectivity) -{ - const struct ref *rm = mapped_refs; - - if (check_connectivity) { - struct check_connected_options opt = CHECK_CONNECTED_INIT; - - opt.transport = transport; - opt.progress = transport->progress; - - if (check_connected(iterate_ref_map, &rm, &opt)) - die(_("remote did not send all necessary objects")); - } - - if (refs) { - write_remote_refs(mapped_refs); - if (option_single_branch && !option_no_tags) - write_followtags(refs, msg); - } - - if (remote_head_points_at && !option_bare) { - struct strbuf head_ref = STRBUF_INIT; - strbuf_addstr(&head_ref, branch_top); - strbuf_addstr(&head_ref, "HEAD"); - if (create_symref(head_ref.buf, - remote_head_points_at->peer_ref->name, - msg) < 0) - die(_("unable to update %s"), head_ref.buf); - strbuf_release(&head_ref); - } -} - -static void update_head(const struct ref *our, const struct ref *remote, - const char *msg) -{ - const char *head; - if (our && skip_prefix(our->name, "refs/heads/", &head)) { - /* Local default branch link */ - if (create_symref("HEAD", our->name, NULL) < 0) - die(_("unable to update HEAD")); - if (!option_bare) { - update_ref(msg, "HEAD", &our->old_oid, NULL, 0, - UPDATE_REFS_DIE_ON_ERR); - install_branch_config(0, head, option_origin, our->name); - } - } else if (our) { - struct commit *c = lookup_commit_reference(the_repository, - &our->old_oid); - /* --branch specifies a non-branch (i.e. tags), detach HEAD */ - update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF, - UPDATE_REFS_DIE_ON_ERR); - } else if (remote) { - /* - * We know remote HEAD points to a non-branch, or - * HEAD points to a branch but we don't know which one. - * Detach HEAD in all these cases. - */ - update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF, - UPDATE_REFS_DIE_ON_ERR); - } -} - -static int git_sparse_checkout_init(const char *repo) -{ - struct strvec argv = STRVEC_INIT; - int result = 0; - strvec_pushl(&argv, "-C", repo, "sparse-checkout", "init", NULL); - - /* - * We must apply the setting in the current process - * for the later checkout to use the sparse-checkout file. - */ - core_apply_sparse_checkout = 1; - - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) { - error(_("failed to initialize sparse-checkout")); - result = 1; - } - - strvec_clear(&argv); - return result; -} - -static int checkout(int submodule_progress) -{ - struct object_id oid; - char *head; - struct lock_file lock_file = LOCK_INIT; - struct unpack_trees_options opts; - struct tree *tree; - struct tree_desc t; - int err = 0; - - if (option_no_checkout) - return 0; - - head = resolve_refdup("HEAD", RESOLVE_REF_READING, &oid, NULL); - if (!head) { - warning(_("remote HEAD refers to nonexistent ref, " - "unable to checkout.\n")); - return 0; - } - if (!strcmp(head, "HEAD")) { - if (advice_detached_head) - detach_advice(oid_to_hex(&oid)); - FREE_AND_NULL(head); - } else { - if (!starts_with(head, "refs/heads/")) - die(_("HEAD not found below refs/heads!")); - } - - /* We need to be in the new work tree for the checkout */ - setup_work_tree(); - - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); - - memset(&opts, 0, sizeof opts); - opts.update = 1; - opts.merge = 1; - opts.clone = 1; - opts.fn = oneway_merge; - opts.verbose_update = (option_verbosity >= 0); - opts.src_index = &the_index; - opts.dst_index = &the_index; - init_checkout_metadata(&opts.meta, head, &oid, NULL); - - tree = parse_tree_indirect(&oid); - parse_tree(tree); - init_tree_desc(&t, tree->buffer, tree->size); - if (unpack_trees(1, &t, &opts) < 0) - die(_("unable to checkout working tree")); - - free(head); - - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) - die(_("unable to write new index file")); - - err |= run_hook_le(NULL, "post-checkout", oid_to_hex(&null_oid), - oid_to_hex(&oid), "1", NULL); - - if (!err && (option_recurse_submodules.nr > 0)) { - struct strvec args = STRVEC_INIT; - strvec_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL); - - if (option_shallow_submodules == 1) - strvec_push(&args, "--depth=1"); - - if (max_jobs != -1) - strvec_pushf(&args, "--jobs=%d", max_jobs); - - if (submodule_progress) - strvec_push(&args, "--progress"); - - if (option_verbosity < 0) - strvec_push(&args, "--quiet"); - - if (option_remote_submodules) { - strvec_push(&args, "--remote"); - strvec_push(&args, "--no-fetch"); - } - - if (option_single_branch >= 0) - strvec_push(&args, option_single_branch ? - "--single-branch" : - "--no-single-branch"); - - err = run_command_v_opt(args.v, RUN_GIT_CMD); - strvec_clear(&args); - } - - return err; -} - -static int write_one_config(const char *key, const char *value, void *data) -{ - return git_config_set_multivar_gently(key, - value ? value : "true", - CONFIG_REGEX_NONE, 0); -} - -static void write_config(struct string_list *config) -{ - int i; - - for (i = 0; i < config->nr; i++) { - if (git_config_parse_parameter(config->items[i].string, - write_one_config, NULL) < 0) - die(_("unable to write parameters to config file")); - } -} - -static void write_refspec_config(const char *src_ref_prefix, - const struct ref *our_head_points_at, - const struct ref *remote_head_points_at, - struct strbuf *branch_top) -{ - struct strbuf key = STRBUF_INIT; - struct strbuf value = STRBUF_INIT; - - if (option_mirror || !option_bare) { - if (option_single_branch && !option_mirror) { - if (option_branch) { - if (starts_with(our_head_points_at->name, "refs/tags/")) - strbuf_addf(&value, "+%s:%s", our_head_points_at->name, - our_head_points_at->name); - else - strbuf_addf(&value, "+%s:%s%s", our_head_points_at->name, - branch_top->buf, option_branch); - } else if (remote_head_points_at) { - const char *head = remote_head_points_at->name; - if (!skip_prefix(head, "refs/heads/", &head)) - BUG("remote HEAD points at non-head?"); - - strbuf_addf(&value, "+%s:%s%s", remote_head_points_at->name, - branch_top->buf, head); - } - /* - * otherwise, the next "git fetch" will - * simply fetch from HEAD without updating - * any remote-tracking branch, which is what - * we want. - */ - } else { - strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top->buf); - } - /* Configure the remote */ - if (value.len) { - strbuf_addf(&key, "remote.%s.fetch", option_origin); - git_config_set_multivar(key.buf, value.buf, "^$", 0); - strbuf_reset(&key); - - if (option_mirror) { - strbuf_addf(&key, "remote.%s.mirror", option_origin); - git_config_set(key.buf, "true"); - strbuf_reset(&key); - } - } - } - - strbuf_release(&key); - strbuf_release(&value); -} - -static void dissociate_from_references(void) -{ - static const char* argv[] = { "repack", "-a", "-d", NULL }; - char *alternates = git_pathdup("objects/info/alternates"); - - if (!access(alternates, F_OK)) { - if (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN)) - die(_("cannot repack to clean up")); - if (unlink(alternates) && errno != ENOENT) - die_errno(_("cannot unlink temporary alternates file")); - } - free(alternates); -} - -static int path_exists(const char *path) -{ - struct stat sb; - return !stat(path, &sb); -} - -int cmd_clone(int argc, const char **argv, const char *prefix) -{ - int is_bundle = 0, is_local; - const char *repo_name, *repo, *work_tree, *git_dir; - char *path, *dir, *display_repo = NULL; - int dest_exists, real_dest_exists = 0; - const struct ref *refs, *remote_head; - const struct ref *remote_head_points_at; - const struct ref *our_head_points_at; - struct ref *mapped_refs; - const struct ref *ref; - struct strbuf key = STRBUF_INIT; - struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT; - struct transport *transport = NULL; - const char *src_ref_prefix = "refs/heads/"; - struct remote *remote; - int err = 0, complete_refs_before_fetch = 1; - int submodule_progress; - - struct strvec ref_prefixes = STRVEC_INIT; - - packet_trace_identity("clone"); - argc = parse_options(argc, argv, prefix, builtin_clone_options, - builtin_clone_usage, 0); - - if (argc > 2) - usage_msg_opt(_("Too many arguments."), - builtin_clone_usage, builtin_clone_options); - - if (argc == 0) - usage_msg_opt(_("You must specify a repository to clone."), - builtin_clone_usage, builtin_clone_options); - - if (option_depth || option_since || option_not.nr) - deepen = 1; - if (option_single_branch == -1) - option_single_branch = deepen ? 1 : 0; - - if (option_mirror) - option_bare = 1; - - if (option_bare) { - if (option_origin) - die(_("--bare and --origin %s options are incompatible."), - option_origin); - if (real_git_dir) - die(_("--bare and --separate-git-dir are incompatible.")); - option_no_checkout = 1; - } - - if (!option_origin) - option_origin = "origin"; - - repo_name = argv[0]; - - path = get_repo_path(repo_name, &is_bundle); - if (path) - repo = absolute_pathdup(repo_name); - else if (strchr(repo_name, ':')) { - repo = repo_name; - display_repo = transport_anonymize_url(repo); - } else - die(_("repository '%s' does not exist"), repo_name); - - /* no need to be strict, transport_set_option() will validate it again */ - if (option_depth && atoi(option_depth) < 1) - die(_("depth %s is not a positive number"), option_depth); - - if (argc == 2) - dir = xstrdup(argv[1]); - else - dir = guess_dir_name(repo_name, is_bundle, option_bare); - strip_trailing_slashes(dir); - - dest_exists = path_exists(dir); - if (dest_exists && !is_empty_dir(dir)) - die(_("destination path '%s' already exists and is not " - "an empty directory."), dir); - - if (real_git_dir) { - real_dest_exists = path_exists(real_git_dir); - if (real_dest_exists && !is_empty_dir(real_git_dir)) - die(_("repository path '%s' already exists and is not " - "an empty directory."), real_git_dir); - } - - - strbuf_addf(&reflog_msg, "clone: from %s", - display_repo ? display_repo : repo); - free(display_repo); - - if (option_bare) - work_tree = NULL; - else { - work_tree = getenv("GIT_WORK_TREE"); - if (work_tree && path_exists(work_tree)) - die(_("working tree '%s' already exists."), work_tree); - } - - if (option_bare || work_tree) - git_dir = xstrdup(dir); - else { - work_tree = dir; - git_dir = mkpathdup("%s/.git", dir); - } - - atexit(remove_junk); - sigchain_push_common(remove_junk_on_signal); - - if (!option_bare) { - if (safe_create_leading_directories_const(work_tree) < 0) - die_errno(_("could not create leading directories of '%s'"), - work_tree); - if (dest_exists) - junk_work_tree_flags |= REMOVE_DIR_KEEP_TOPLEVEL; - else if (mkdir(work_tree, 0777)) - die_errno(_("could not create work tree dir '%s'"), - work_tree); - junk_work_tree = work_tree; - set_git_work_tree(work_tree); - } - - if (real_git_dir) { - if (real_dest_exists) - junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL; - junk_git_dir = real_git_dir; - } else { - if (dest_exists) - junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL; - junk_git_dir = git_dir; - } - if (safe_create_leading_directories_const(git_dir) < 0) - die(_("could not create leading directories of '%s'"), git_dir); - - if (0 <= option_verbosity) { - if (option_bare) - fprintf(stderr, _("Cloning into bare repository '%s'...\n"), dir); - else - fprintf(stderr, _("Cloning into '%s'...\n"), dir); - } - - if (option_recurse_submodules.nr > 0) { - struct string_list_item *item; - struct strbuf sb = STRBUF_INIT; - - /* remove duplicates */ - string_list_sort(&option_recurse_submodules); - string_list_remove_duplicates(&option_recurse_submodules, 0); - - /* - * NEEDSWORK: In a multi-working-tree world, this needs to be - * set in the per-worktree config. - */ - for_each_string_list_item(item, &option_recurse_submodules) { - strbuf_addf(&sb, "submodule.active=%s", - item->string); - string_list_append(&option_config, - strbuf_detach(&sb, NULL)); - } - - if (option_required_reference.nr && - option_optional_reference.nr) - die(_("clone --recursive is not compatible with " - "both --reference and --reference-if-able")); - else if (option_required_reference.nr) { - string_list_append(&option_config, - "submodule.alternateLocation=superproject"); - string_list_append(&option_config, - "submodule.alternateErrorStrategy=die"); - } else if (option_optional_reference.nr) { - string_list_append(&option_config, - "submodule.alternateLocation=superproject"); - string_list_append(&option_config, - "submodule.alternateErrorStrategy=info"); - } - } - - init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL, - INIT_DB_QUIET); - - if (real_git_dir) - git_dir = real_git_dir; - - write_config(&option_config); - - git_config(git_default_config, NULL); - - if (option_bare) { - if (option_mirror) - src_ref_prefix = "refs/"; - strbuf_addstr(&branch_top, src_ref_prefix); - - git_config_set("core.bare", "true"); - } else { - strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin); - } - - strbuf_addf(&key, "remote.%s.url", option_origin); - git_config_set(key.buf, repo); - strbuf_reset(&key); - - if (option_no_tags) { - strbuf_addf(&key, "remote.%s.tagOpt", option_origin); - git_config_set(key.buf, "--no-tags"); - strbuf_reset(&key); - } - - if (option_required_reference.nr || option_optional_reference.nr) - setup_reference(); - - if (option_sparse_checkout && git_sparse_checkout_init(dir)) - return 1; - - remote = remote_get(option_origin); - - refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix, - branch_top.buf); - - transport = transport_get(remote, remote->url[0]); - transport_set_verbosity(transport, option_verbosity, option_progress); - transport->family = family; - - path = get_repo_path(remote->url[0], &is_bundle); - is_local = option_local != 0 && path && !is_bundle; - if (is_local) { - if (option_depth) - warning(_("--depth is ignored in local clones; use file:// instead.")); - if (option_since) - warning(_("--shallow-since is ignored in local clones; use file:// instead.")); - if (option_not.nr) - warning(_("--shallow-exclude is ignored in local clones; use file:// instead.")); - if (filter_options.choice) - warning(_("--filter is ignored in local clones; use file:// instead.")); - if (!access(mkpath("%s/shallow", path), F_OK)) { - if (option_local > 0) - warning(_("source repository is shallow, ignoring --local")); - is_local = 0; - } - } - if (option_local > 0 && !is_local) - warning(_("--local is ignored")); - transport->cloning = 1; - - transport_set_option(transport, TRANS_OPT_KEEP, "yes"); - - if (option_depth) - transport_set_option(transport, TRANS_OPT_DEPTH, - option_depth); - if (option_since) - transport_set_option(transport, TRANS_OPT_DEEPEN_SINCE, - option_since); - if (option_not.nr) - transport_set_option(transport, TRANS_OPT_DEEPEN_NOT, - (const char *)&option_not); - if (option_single_branch) - transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); - - if (option_upload_pack) - transport_set_option(transport, TRANS_OPT_UPLOADPACK, - option_upload_pack); - - if (server_options.nr) - transport->server_options = &server_options; - - if (filter_options.choice) { - const char *spec = - expand_list_objects_filter_spec(&filter_options); - transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, - spec); - transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1"); - } - - if (transport->smart_options && !deepen && !filter_options.choice) - transport->smart_options->check_self_contained_and_connected = 1; - - - strvec_push(&ref_prefixes, "HEAD"); - refspec_ref_prefixes(&remote->fetch, &ref_prefixes); - if (option_branch) - expand_ref_prefix(&ref_prefixes, option_branch); - if (!option_no_tags) - strvec_push(&ref_prefixes, "refs/tags/"); - - refs = transport_get_remote_refs(transport, &ref_prefixes); - - if (refs) { - int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport)); - - /* - * Now that we know what algorithm the remote side is using, - * let's set ours to the same thing. - */ - initialize_repository_version(hash_algo, 1); - repo_set_hash_algo(the_repository, hash_algo); - - mapped_refs = wanted_peer_refs(refs, &remote->fetch); - /* - * transport_get_remote_refs() may return refs with null sha-1 - * in mapped_refs (see struct transport->get_refs_list - * comment). In that case we need fetch it early because - * remote_head code below relies on it. - * - * for normal clones, transport_get_remote_refs() should - * return reliable ref set, we can delay cloning until after - * remote HEAD check. - */ - for (ref = refs; ref; ref = ref->next) - if (is_null_oid(&ref->old_oid)) { - complete_refs_before_fetch = 0; - break; - } - - if (!is_local && !complete_refs_before_fetch) - transport_fetch_refs(transport, mapped_refs); - - remote_head = find_ref_by_name(refs, "HEAD"); - remote_head_points_at = - guess_remote_head(remote_head, mapped_refs, 0); - - if (option_branch) { - our_head_points_at = - find_remote_branch(mapped_refs, option_branch); - - if (!our_head_points_at) - die(_("Remote branch %s not found in upstream %s"), - option_branch, option_origin); - } - else - our_head_points_at = remote_head_points_at; - } - else { - if (option_branch) - die(_("Remote branch %s not found in upstream %s"), - option_branch, option_origin); - - warning(_("You appear to have cloned an empty repository.")); - mapped_refs = NULL; - our_head_points_at = NULL; - remote_head_points_at = NULL; - remote_head = NULL; - option_no_checkout = 1; - if (!option_bare) { - const char *branch = git_default_branch_name(); - char *ref = xstrfmt("refs/heads/%s", branch); - - install_branch_config(0, branch, option_origin, ref); - free(ref); - } - } - - write_refspec_config(src_ref_prefix, our_head_points_at, - remote_head_points_at, &branch_top); - - if (filter_options.choice) - partial_clone_register(option_origin, &filter_options); - - if (is_local) - clone_local(path, git_dir); - else if (refs && complete_refs_before_fetch) - transport_fetch_refs(transport, mapped_refs); - - update_remote_refs(refs, mapped_refs, remote_head_points_at, - branch_top.buf, reflog_msg.buf, transport, - !is_local); - - update_head(our_head_points_at, remote_head, reflog_msg.buf); - - /* - * We want to show progress for recursive submodule clones iff - * we did so for the main clone. But only the transport knows - * the final decision for this flag, so we need to rescue the value - * before we free the transport. - */ - submodule_progress = transport->progress; - - transport_unlock_pack(transport); - transport_disconnect(transport); - - if (option_dissociate) { - close_object_store(the_repository->objects); - dissociate_from_references(); - } - - junk_mode = JUNK_LEAVE_REPO; - err = checkout(submodule_progress); - - strbuf_release(&reflog_msg); - strbuf_release(&branch_top); - strbuf_release(&key); - junk_mode = JUNK_LEAVE_ALL; - - strvec_clear(&ref_prefixes); - return err; -} diff --git a/third_party/git/builtin/column.c b/third_party/git/builtin/column.c deleted file mode 100644 index e815e148aa18..000000000000 --- a/third_party/git/builtin/column.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "strbuf.h" -#include "parse-options.h" -#include "string-list.h" -#include "column.h" - -static const char * const builtin_column_usage[] = { - N_("git column [<options>]"), - NULL -}; -static unsigned int colopts; - -static int column_config(const char *var, const char *value, void *cb) -{ - return git_column_config(var, value, cb, &colopts); -} - -int cmd_column(int argc, const char **argv, const char *prefix) -{ - struct string_list list = STRING_LIST_INIT_DUP; - struct strbuf sb = STRBUF_INIT; - struct column_options copts; - const char *command = NULL, *real_command = NULL; - struct option options[] = { - OPT_STRING(0, "command", &real_command, N_("name"), N_("lookup config vars")), - OPT_COLUMN(0, "mode", &colopts, N_("layout to use")), - OPT_INTEGER(0, "raw-mode", &colopts, N_("layout to use")), - OPT_INTEGER(0, "width", &copts.width, N_("Maximum width")), - OPT_STRING(0, "indent", &copts.indent, N_("string"), N_("Padding space on left border")), - OPT_INTEGER(0, "nl", &copts.nl, N_("Padding space on right border")), - OPT_INTEGER(0, "padding", &copts.padding, N_("Padding space between columns")), - OPT_END() - }; - - /* This one is special and must be the first one */ - if (argc > 1 && starts_with(argv[1], "--command=")) { - command = argv[1] + 10; - git_config(column_config, (void *)command); - } else - git_config(column_config, NULL); - - memset(&copts, 0, sizeof(copts)); - copts.padding = 1; - argc = parse_options(argc, argv, prefix, options, builtin_column_usage, 0); - if (argc) - usage_with_options(builtin_column_usage, options); - if (real_command || command) { - if (!real_command || !command || strcmp(real_command, command)) - die(_("--command must be the first argument")); - } - finalize_colopts(&colopts, -1); - while (!strbuf_getline(&sb, stdin)) - string_list_append(&list, sb.buf); - - print_columns(&list, colopts, &copts); - return 0; -} diff --git a/third_party/git/builtin/commit-graph.c b/third_party/git/builtin/commit-graph.c deleted file mode 100644 index 78fa08f43af5..000000000000 --- a/third_party/git/builtin/commit-graph.c +++ /dev/null @@ -1,342 +0,0 @@ -#include "builtin.h" -#include "config.h" -#include "dir.h" -#include "lockfile.h" -#include "parse-options.h" -#include "repository.h" -#include "commit-graph.h" -#include "object-store.h" -#include "progress.h" -#include "tag.h" - -static char const * const builtin_commit_graph_usage[] = { - N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"), - N_("git commit-graph write [--object-dir <objdir>] [--append] " - "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] " - "[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress] " - "<split options>"), - NULL -}; - -static const char * const builtin_commit_graph_verify_usage[] = { - N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"), - NULL -}; - -static const char * const builtin_commit_graph_write_usage[] = { - N_("git commit-graph write [--object-dir <objdir>] [--append] " - "[--split[=<strategy>]] [--reachable|--stdin-packs|--stdin-commits] " - "[--changed-paths] [--[no-]max-new-filters <n>] [--[no-]progress] " - "<split options>"), - NULL -}; - -static struct opts_commit_graph { - const char *obj_dir; - int reachable; - int stdin_packs; - int stdin_commits; - int append; - int split; - int shallow; - int progress; - int enable_changed_paths; -} opts; - -static struct object_directory *find_odb(struct repository *r, - const char *obj_dir) -{ - struct object_directory *odb; - char *obj_dir_real = real_pathdup(obj_dir, 1); - struct strbuf odb_path_real = STRBUF_INIT; - - prepare_alt_odb(r); - for (odb = r->objects->odb; odb; odb = odb->next) { - strbuf_realpath(&odb_path_real, odb->path, 1); - if (!strcmp(obj_dir_real, odb_path_real.buf)) - break; - } - - free(obj_dir_real); - strbuf_release(&odb_path_real); - - if (!odb) - die(_("could not find object directory matching %s"), obj_dir); - return odb; -} - -static int graph_verify(int argc, const char **argv) -{ - struct commit_graph *graph = NULL; - struct object_directory *odb = NULL; - char *graph_name; - int open_ok; - int fd; - struct stat st; - int flags = 0; - - static struct option builtin_commit_graph_verify_options[] = { - OPT_STRING(0, "object-dir", &opts.obj_dir, - N_("dir"), - N_("The object directory to store the graph")), - OPT_BOOL(0, "shallow", &opts.shallow, - N_("if the commit-graph is split, only verify the tip file")), - OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")), - OPT_END(), - }; - - trace2_cmd_mode("verify"); - - opts.progress = isatty(2); - argc = parse_options(argc, argv, NULL, - builtin_commit_graph_verify_options, - builtin_commit_graph_verify_usage, 0); - - if (!opts.obj_dir) - opts.obj_dir = get_object_directory(); - if (opts.shallow) - flags |= COMMIT_GRAPH_VERIFY_SHALLOW; - if (opts.progress) - flags |= COMMIT_GRAPH_WRITE_PROGRESS; - - odb = find_odb(the_repository, opts.obj_dir); - graph_name = get_commit_graph_filename(odb); - open_ok = open_commit_graph(graph_name, &fd, &st); - if (!open_ok && errno != ENOENT) - die_errno(_("Could not open commit-graph '%s'"), graph_name); - - FREE_AND_NULL(graph_name); - - if (open_ok) - graph = load_commit_graph_one_fd_st(the_repository, fd, &st, odb); - else - graph = read_commit_graph_one(the_repository, odb); - - /* Return failure if open_ok predicted success */ - if (!graph) - return !!open_ok; - - UNLEAK(graph); - return verify_commit_graph(the_repository, graph, flags); -} - -extern int read_replace_refs; -static struct commit_graph_opts write_opts; - -static int write_option_parse_split(const struct option *opt, const char *arg, - int unset) -{ - enum commit_graph_split_flags *flags = opt->value; - - BUG_ON_OPT_NEG(unset); - - opts.split = 1; - if (!arg) - return 0; - - if (!strcmp(arg, "no-merge")) - *flags = COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED; - else if (!strcmp(arg, "replace")) - *flags = COMMIT_GRAPH_SPLIT_REPLACE; - else - die(_("unrecognized --split argument, %s"), arg); - - return 0; -} - -static int read_one_commit(struct oidset *commits, struct progress *progress, - const char *hash) -{ - struct object *result; - struct object_id oid; - const char *end; - - if (parse_oid_hex(hash, &oid, &end)) - return error(_("unexpected non-hex object ID: %s"), hash); - - result = deref_tag(the_repository, parse_object(the_repository, &oid), - NULL, 0); - if (!result) - return error(_("invalid object: %s"), hash); - else if (object_as_type(result, OBJ_COMMIT, 1)) - oidset_insert(commits, &result->oid); - - display_progress(progress, oidset_size(commits)); - - return 0; -} - -static int write_option_max_new_filters(const struct option *opt, - const char *arg, - int unset) -{ - int *to = opt->value; - if (unset) - *to = -1; - else { - const char *s; - *to = strtol(arg, (char **)&s, 10); - if (*s) - return error(_("%s expects a numerical value"), - optname(opt, opt->flags)); - } - return 0; -} - -static int git_commit_graph_write_config(const char *var, const char *value, - void *cb) -{ - if (!strcmp(var, "commitgraph.maxnewfilters")) - write_opts.max_new_filters = git_config_int(var, value); - /* - * No need to fall-back to 'git_default_config', since this was already - * called in 'cmd_commit_graph()'. - */ - return 0; -} - -static int graph_write(int argc, const char **argv) -{ - struct string_list pack_indexes = STRING_LIST_INIT_NODUP; - struct strbuf buf = STRBUF_INIT; - struct oidset commits = OIDSET_INIT; - struct object_directory *odb = NULL; - int result = 0; - enum commit_graph_write_flags flags = 0; - struct progress *progress = NULL; - - static struct option builtin_commit_graph_write_options[] = { - OPT_STRING(0, "object-dir", &opts.obj_dir, - N_("dir"), - N_("The object directory to store the graph")), - OPT_BOOL(0, "reachable", &opts.reachable, - N_("start walk at all refs")), - OPT_BOOL(0, "stdin-packs", &opts.stdin_packs, - N_("scan pack-indexes listed by stdin for commits")), - OPT_BOOL(0, "stdin-commits", &opts.stdin_commits, - N_("start walk at commits listed by stdin")), - OPT_BOOL(0, "append", &opts.append, - N_("include all commits already in the commit-graph file")), - OPT_BOOL(0, "changed-paths", &opts.enable_changed_paths, - N_("enable computation for changed paths")), - OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")), - OPT_CALLBACK_F(0, "split", &write_opts.split_flags, NULL, - N_("allow writing an incremental commit-graph file"), - PARSE_OPT_OPTARG | PARSE_OPT_NONEG, - write_option_parse_split), - OPT_INTEGER(0, "max-commits", &write_opts.max_commits, - N_("maximum number of commits in a non-base split commit-graph")), - OPT_INTEGER(0, "size-multiple", &write_opts.size_multiple, - N_("maximum ratio between two levels of a split commit-graph")), - OPT_EXPIRY_DATE(0, "expire-time", &write_opts.expire_time, - N_("only expire files older than a given date-time")), - OPT_CALLBACK_F(0, "max-new-filters", &write_opts.max_new_filters, - NULL, N_("maximum number of changed-path Bloom filters to compute"), - 0, write_option_max_new_filters), - OPT_END(), - }; - - opts.progress = isatty(2); - opts.enable_changed_paths = -1; - write_opts.size_multiple = 2; - write_opts.max_commits = 0; - write_opts.expire_time = 0; - write_opts.max_new_filters = -1; - - trace2_cmd_mode("write"); - - git_config(git_commit_graph_write_config, &opts); - - argc = parse_options(argc, argv, NULL, - builtin_commit_graph_write_options, - builtin_commit_graph_write_usage, 0); - - if (opts.reachable + opts.stdin_packs + opts.stdin_commits > 1) - die(_("use at most one of --reachable, --stdin-commits, or --stdin-packs")); - if (!opts.obj_dir) - opts.obj_dir = get_object_directory(); - if (opts.append) - flags |= COMMIT_GRAPH_WRITE_APPEND; - if (opts.split) - flags |= COMMIT_GRAPH_WRITE_SPLIT; - if (opts.progress) - flags |= COMMIT_GRAPH_WRITE_PROGRESS; - if (!opts.enable_changed_paths) - flags |= COMMIT_GRAPH_NO_WRITE_BLOOM_FILTERS; - if (opts.enable_changed_paths == 1 || - git_env_bool(GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS, 0)) - flags |= COMMIT_GRAPH_WRITE_BLOOM_FILTERS; - - read_replace_refs = 0; - odb = find_odb(the_repository, opts.obj_dir); - - if (opts.reachable) { - if (write_commit_graph_reachable(odb, flags, &write_opts)) - return 1; - return 0; - } - - if (opts.stdin_packs) { - while (strbuf_getline(&buf, stdin) != EOF) - string_list_append(&pack_indexes, - strbuf_detach(&buf, NULL)); - } else if (opts.stdin_commits) { - oidset_init(&commits, 0); - if (opts.progress) - progress = start_delayed_progress( - _("Collecting commits from input"), 0); - - while (strbuf_getline(&buf, stdin) != EOF) { - if (read_one_commit(&commits, progress, buf.buf)) { - result = 1; - goto cleanup; - } - } - - stop_progress(&progress); - } - - if (write_commit_graph(odb, - opts.stdin_packs ? &pack_indexes : NULL, - opts.stdin_commits ? &commits : NULL, - flags, - &write_opts)) - result = 1; - -cleanup: - string_list_clear(&pack_indexes, 0); - strbuf_release(&buf); - return result; -} - -int cmd_commit_graph(int argc, const char **argv, const char *prefix) -{ - static struct option builtin_commit_graph_options[] = { - OPT_STRING(0, "object-dir", &opts.obj_dir, - N_("dir"), - N_("The object directory to store the graph")), - OPT_END(), - }; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage_with_options(builtin_commit_graph_usage, - builtin_commit_graph_options); - - git_config(git_default_config, NULL); - argc = parse_options(argc, argv, prefix, - builtin_commit_graph_options, - builtin_commit_graph_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - - save_commit_buffer = 0; - - if (argc > 0) { - if (!strcmp(argv[0], "verify")) - return graph_verify(argc, argv); - if (!strcmp(argv[0], "write")) - return graph_write(argc, argv); - } - - usage_with_options(builtin_commit_graph_usage, - builtin_commit_graph_options); -} diff --git a/third_party/git/builtin/commit-tree.c b/third_party/git/builtin/commit-tree.c deleted file mode 100644 index 1031b9a491c5..000000000000 --- a/third_party/git/builtin/commit-tree.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * GIT - The information manager from hell - * - * Copyright (C) Linus Torvalds, 2005 - */ -#include "cache.h" -#include "config.h" -#include "object-store.h" -#include "repository.h" -#include "commit.h" -#include "tree.h" -#include "builtin.h" -#include "utf8.h" -#include "gpg-interface.h" -#include "parse-options.h" - -static const char * const commit_tree_usage[] = { - N_("git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] " - "[(-F <file>)...] <tree>"), - NULL -}; - -static const char *sign_commit; - -static void new_parent(struct commit *parent, struct commit_list **parents_p) -{ - struct object_id *oid = &parent->object.oid; - struct commit_list *parents; - for (parents = *parents_p; parents; parents = parents->next) { - if (parents->item == parent) { - error(_("duplicate parent %s ignored"), oid_to_hex(oid)); - return; - } - parents_p = &parents->next; - } - commit_list_insert(parent, parents_p); -} - -static int commit_tree_config(const char *var, const char *value, void *cb) -{ - int status = git_gpg_config(var, value, NULL); - if (status) - return status; - return git_default_config(var, value, cb); -} - -static int parse_parent_arg_callback(const struct option *opt, - const char *arg, int unset) -{ - struct object_id oid; - struct commit_list **parents = opt->value; - - BUG_ON_OPT_NEG_NOARG(unset, arg); - - if (get_oid_commit(arg, &oid)) - die(_("not a valid object name %s"), arg); - - assert_oid_type(&oid, OBJ_COMMIT); - new_parent(lookup_commit(the_repository, &oid), parents); - return 0; -} - -static int parse_message_arg_callback(const struct option *opt, - const char *arg, int unset) -{ - struct strbuf *buf = opt->value; - - BUG_ON_OPT_NEG_NOARG(unset, arg); - - if (buf->len) - strbuf_addch(buf, '\n'); - strbuf_addstr(buf, arg); - strbuf_complete_line(buf); - - return 0; -} - -static int parse_file_arg_callback(const struct option *opt, - const char *arg, int unset) -{ - int fd; - struct strbuf *buf = opt->value; - - BUG_ON_OPT_NEG_NOARG(unset, arg); - - if (buf->len) - strbuf_addch(buf, '\n'); - if (!strcmp(arg, "-")) - fd = 0; - else { - fd = open(arg, O_RDONLY); - if (fd < 0) - die_errno(_("git commit-tree: failed to open '%s'"), arg); - } - if (strbuf_read(buf, fd, 0) < 0) - die_errno(_("git commit-tree: failed to read '%s'"), arg); - if (fd && close(fd)) - die_errno(_("git commit-tree: failed to close '%s'"), arg); - - return 0; -} - -int cmd_commit_tree(int argc, const char **argv, const char *prefix) -{ - static struct strbuf buffer = STRBUF_INIT; - struct commit_list *parents = NULL; - struct object_id tree_oid; - struct object_id commit_oid; - - struct option options[] = { - OPT_CALLBACK_F('p', NULL, &parents, N_("parent"), - N_("id of a parent commit object"), PARSE_OPT_NONEG, - parse_parent_arg_callback), - OPT_CALLBACK_F('m', NULL, &buffer, N_("message"), - N_("commit message"), PARSE_OPT_NONEG, - parse_message_arg_callback), - OPT_CALLBACK_F('F', NULL, &buffer, N_("file"), - N_("read commit log message from file"), PARSE_OPT_NONEG, - parse_file_arg_callback), - { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), - N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - OPT_END() - }; - - git_config(commit_tree_config, NULL); - - if (argc < 2 || !strcmp(argv[1], "-h")) - usage_with_options(commit_tree_usage, options); - - argc = parse_options(argc, argv, prefix, options, commit_tree_usage, 0); - - if (argc != 1) - die(_("must give exactly one tree")); - - if (get_oid_tree(argv[0], &tree_oid)) - die(_("not a valid object name %s"), argv[0]); - - if (!buffer.len) { - if (strbuf_read(&buffer, 0, 0) < 0) - die_errno(_("git commit-tree: failed to read")); - } - - if (commit_tree(buffer.buf, buffer.len, &tree_oid, parents, &commit_oid, - NULL, sign_commit)) { - strbuf_release(&buffer); - return 1; - } - - printf("%s\n", oid_to_hex(&commit_oid)); - strbuf_release(&buffer); - return 0; -} diff --git a/third_party/git/builtin/commit.c b/third_party/git/builtin/commit.c deleted file mode 100644 index 1dfd799ec518..000000000000 --- a/third_party/git/builtin/commit.c +++ /dev/null @@ -1,1724 +0,0 @@ -/* - * Builtin "git commit" - * - * Copyright (c) 2007 Kristian Hรธgsberg <krh@redhat.com> - * Based on git-commit.sh by Junio C Hamano and Linus Torvalds - */ - -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "lockfile.h" -#include "cache-tree.h" -#include "color.h" -#include "dir.h" -#include "builtin.h" -#include "diff.h" -#include "diffcore.h" -#include "commit.h" -#include "revision.h" -#include "wt-status.h" -#include "run-command.h" -#include "refs.h" -#include "log-tree.h" -#include "strbuf.h" -#include "utf8.h" -#include "parse-options.h" -#include "string-list.h" -#include "rerere.h" -#include "unpack-trees.h" -#include "quote.h" -#include "submodule.h" -#include "gpg-interface.h" -#include "column.h" -#include "sequencer.h" -#include "mailmap.h" -#include "help.h" -#include "commit-reach.h" -#include "commit-graph.h" - -static const char * const builtin_commit_usage[] = { - N_("git commit [<options>] [--] <pathspec>..."), - NULL -}; - -static const char * const builtin_status_usage[] = { - N_("git status [<options>] [--] <pathspec>..."), - NULL -}; - -static const char empty_amend_advice[] = -N_("You asked to amend the most recent commit, but doing so would make\n" -"it empty. You can repeat your command with --allow-empty, or you can\n" -"remove the commit entirely with \"git reset HEAD^\".\n"); - -static const char empty_cherry_pick_advice[] = -N_("The previous cherry-pick is now empty, possibly due to conflict resolution.\n" -"If you wish to commit it anyway, use:\n" -"\n" -" git commit --allow-empty\n" -"\n"); - -static const char empty_rebase_pick_advice[] = -N_("Otherwise, please use 'git rebase --skip'\n"); - -static const char empty_cherry_pick_advice_single[] = -N_("Otherwise, please use 'git cherry-pick --skip'\n"); - -static const char empty_cherry_pick_advice_multi[] = -N_("and then use:\n" -"\n" -" git cherry-pick --continue\n" -"\n" -"to resume cherry-picking the remaining commits.\n" -"If you wish to skip this commit, use:\n" -"\n" -" git cherry-pick --skip\n" -"\n"); - -static const char *color_status_slots[] = { - [WT_STATUS_HEADER] = "header", - [WT_STATUS_UPDATED] = "updated", - [WT_STATUS_CHANGED] = "changed", - [WT_STATUS_UNTRACKED] = "untracked", - [WT_STATUS_NOBRANCH] = "noBranch", - [WT_STATUS_UNMERGED] = "unmerged", - [WT_STATUS_LOCAL_BRANCH] = "localBranch", - [WT_STATUS_REMOTE_BRANCH] = "remoteBranch", - [WT_STATUS_ONBRANCH] = "branch", -}; - -static const char *use_message_buffer; -static struct lock_file index_lock; /* real index */ -static struct lock_file false_lock; /* used only for partial commits */ -static enum { - COMMIT_AS_IS = 1, - COMMIT_NORMAL, - COMMIT_PARTIAL -} commit_style; - -static const char *logfile, *force_author; -static const char *template_file; -/* - * The _message variables are commit names from which to take - * the commit message and/or authorship. - */ -static const char *author_message, *author_message_buffer; -static char *edit_message, *use_message; -static char *fixup_message, *squash_message; -static int all, also, interactive, patch_interactive, only, amend, signoff; -static int edit_flag = -1; /* unspecified */ -static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; -static int config_commit_verbose = -1; /* unspecified */ -static int no_post_rewrite, allow_empty_message, pathspec_file_nul; -static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg; -static char *sign_commit, *pathspec_from_file; - -/* - * The default commit message cleanup mode will remove the lines - * beginning with # (shell comments) and leading and trailing - * whitespaces (empty lines or containing only whitespaces) - * if editor is used, and only the whitespaces if the message - * is specified explicitly. - */ -static enum commit_msg_cleanup_mode cleanup_mode; -static const char *cleanup_arg; - -static enum commit_whence whence; -static int use_editor = 1, include_status = 1; -static int have_option_m; -static struct strbuf message = STRBUF_INIT; - -static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED; - -static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset) -{ - enum wt_status_format *value = (enum wt_status_format *)opt->value; - if (unset) - *value = STATUS_FORMAT_NONE; - else if (!arg) - *value = STATUS_FORMAT_PORCELAIN; - else if (!strcmp(arg, "v1") || !strcmp(arg, "1")) - *value = STATUS_FORMAT_PORCELAIN; - else if (!strcmp(arg, "v2") || !strcmp(arg, "2")) - *value = STATUS_FORMAT_PORCELAIN_V2; - else - die("unsupported porcelain version '%s'", arg); - - return 0; -} - -static int opt_parse_m(const struct option *opt, const char *arg, int unset) -{ - struct strbuf *buf = opt->value; - if (unset) { - have_option_m = 0; - strbuf_setlen(buf, 0); - } else { - have_option_m = 1; - if (buf->len) - strbuf_addch(buf, '\n'); - strbuf_addstr(buf, arg); - strbuf_complete_line(buf); - } - return 0; -} - -static int opt_parse_rename_score(const struct option *opt, const char *arg, int unset) -{ - const char **value = opt->value; - - BUG_ON_OPT_NEG(unset); - - if (arg != NULL && *arg == '=') - arg = arg + 1; - - *value = arg; - return 0; -} - -static void determine_whence(struct wt_status *s) -{ - if (file_exists(git_path_merge_head(the_repository))) - whence = FROM_MERGE; - else if (!sequencer_determine_whence(the_repository, &whence)) - whence = FROM_COMMIT; - if (s) - s->whence = whence; -} - -static void status_init_config(struct wt_status *s, config_fn_t fn) -{ - wt_status_prepare(the_repository, s); - init_diff_ui_defaults(); - git_config(fn, s); - determine_whence(s); - s->hints = advice_status_hints; /* must come after git_config() */ -} - -static void rollback_index_files(void) -{ - switch (commit_style) { - case COMMIT_AS_IS: - break; /* nothing to do */ - case COMMIT_NORMAL: - rollback_lock_file(&index_lock); - break; - case COMMIT_PARTIAL: - rollback_lock_file(&index_lock); - rollback_lock_file(&false_lock); - break; - } -} - -static int commit_index_files(void) -{ - int err = 0; - - switch (commit_style) { - case COMMIT_AS_IS: - break; /* nothing to do */ - case COMMIT_NORMAL: - err = commit_lock_file(&index_lock); - break; - case COMMIT_PARTIAL: - err = commit_lock_file(&index_lock); - rollback_lock_file(&false_lock); - break; - } - - return err; -} - -/* - * Take a union of paths in the index and the named tree (typically, "HEAD"), - * and return the paths that match the given pattern in list. - */ -static int list_paths(struct string_list *list, const char *with_tree, - const struct pathspec *pattern) -{ - int i, ret; - char *m; - - if (!pattern->nr) - return 0; - - m = xcalloc(1, pattern->nr); - - if (with_tree) { - char *max_prefix = common_prefix(pattern); - overlay_tree_on_index(&the_index, with_tree, max_prefix); - free(max_prefix); - } - - for (i = 0; i < active_nr; i++) { - const struct cache_entry *ce = active_cache[i]; - struct string_list_item *item; - - if (ce->ce_flags & CE_UPDATE) - continue; - if (!ce_path_match(&the_index, ce, pattern, m)) - continue; - item = string_list_insert(list, ce->name); - if (ce_skip_worktree(ce)) - item->util = item; /* better a valid pointer than a fake one */ - } - - ret = report_path_error(m, pattern); - free(m); - return ret; -} - -static void add_remove_files(struct string_list *list) -{ - int i; - for (i = 0; i < list->nr; i++) { - struct stat st; - struct string_list_item *p = &(list->items[i]); - - /* p->util is skip-worktree */ - if (p->util) - continue; - - if (!lstat(p->string, &st)) { - if (add_to_cache(p->string, &st, 0)) - die(_("updating files failed")); - } else - remove_file_from_cache(p->string); - } -} - -static void create_base_index(const struct commit *current_head) -{ - struct tree *tree; - struct unpack_trees_options opts; - struct tree_desc t; - - if (!current_head) { - discard_cache(); - return; - } - - memset(&opts, 0, sizeof(opts)); - opts.head_idx = 1; - opts.index_only = 1; - opts.merge = 1; - opts.src_index = &the_index; - opts.dst_index = &the_index; - - opts.fn = oneway_merge; - tree = parse_tree_indirect(¤t_head->object.oid); - if (!tree) - die(_("failed to unpack HEAD tree object")); - parse_tree(tree); - init_tree_desc(&t, tree->buffer, tree->size); - if (unpack_trees(1, &t, &opts)) - exit(128); /* We've already reported the error, finish dying */ -} - -static void refresh_cache_or_die(int refresh_flags) -{ - /* - * refresh_flags contains REFRESH_QUIET, so the only errors - * are for unmerged entries. - */ - if (refresh_cache(refresh_flags | REFRESH_IN_PORCELAIN)) - die_resolve_conflict("commit"); -} - -static const char *prepare_index(const char **argv, const char *prefix, - const struct commit *current_head, int is_status) -{ - struct string_list partial = STRING_LIST_INIT_DUP; - struct pathspec pathspec; - int refresh_flags = REFRESH_QUIET; - const char *ret; - - if (is_status) - refresh_flags |= REFRESH_UNMERGED; - parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_FULL, - prefix, argv); - - if (pathspec_from_file) { - if (interactive) - die(_("--pathspec-from-file is incompatible with --interactive/--patch")); - - if (all) - die(_("--pathspec-from-file with -a does not make sense")); - - if (pathspec.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); - - parse_pathspec_file(&pathspec, 0, - PATHSPEC_PREFER_FULL, - prefix, pathspec_from_file, pathspec_file_nul); - } else if (pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); - } - - if (!pathspec.nr && (also || (only && !amend && !allow_empty))) - die(_("No paths with --include/--only does not make sense.")); - - if (read_cache_preload(&pathspec) < 0) - die(_("index file corrupt")); - - if (interactive) { - char *old_index_env = NULL, *old_repo_index_file; - hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); - - refresh_cache_or_die(refresh_flags); - - if (write_locked_index(&the_index, &index_lock, 0)) - die(_("unable to create temporary index")); - - old_repo_index_file = the_repository->index_file; - the_repository->index_file = - (char *)get_lock_file_path(&index_lock); - old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); - setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - - if (interactive_add(argv, prefix, patch_interactive) != 0) - die(_("interactive add failed")); - - the_repository->index_file = old_repo_index_file; - if (old_index_env && *old_index_env) - setenv(INDEX_ENVIRONMENT, old_index_env, 1); - else - unsetenv(INDEX_ENVIRONMENT); - FREE_AND_NULL(old_index_env); - - discard_cache(); - read_cache_from(get_lock_file_path(&index_lock)); - if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) { - if (reopen_lock_file(&index_lock) < 0) - die(_("unable to write index file")); - if (write_locked_index(&the_index, &index_lock, 0)) - die(_("unable to update temporary index")); - } else - warning(_("Failed to update main cache tree")); - - commit_style = COMMIT_NORMAL; - ret = get_lock_file_path(&index_lock); - goto out; - } - - /* - * 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)) { - hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); - add_files_to_cache(also ? prefix : NULL, &pathspec, 0); - refresh_cache_or_die(refresh_flags); - update_main_cache_tree(WRITE_TREE_SILENT); - if (write_locked_index(&the_index, &index_lock, 0)) - die(_("unable to write new_index file")); - commit_style = COMMIT_NORMAL; - ret = get_lock_file_path(&index_lock); - goto out; - } - - /* - * 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) { - hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); - refresh_cache_or_die(refresh_flags); - if (active_cache_changed - || !cache_tree_fully_valid(active_cache_tree)) - update_main_cache_tree(WRITE_TREE_SILENT); - if (write_locked_index(&the_index, &index_lock, - COMMIT_LOCK | SKIP_IF_UNCHANGED)) - die(_("unable to write new_index file")); - commit_style = COMMIT_AS_IS; - ret = get_index_file(); - goto out; - } - - /* - * 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 (is_from_cherry_pick(whence)) - die(_("cannot do a partial commit during a cherry-pick.")); - else if (is_from_rebase(whence)) - die(_("cannot do a partial commit during a rebase.")); - } - - if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec)) - exit(1); - - discard_cache(); - if (read_cache() < 0) - die(_("cannot read the index")); - - hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); - add_remove_files(&partial); - refresh_cache(REFRESH_QUIET); - update_main_cache_tree(WRITE_TREE_SILENT); - if (write_locked_index(&the_index, &index_lock, 0)) - die(_("unable to write new_index file")); - - 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_locked_index(&the_index, &false_lock, 0)) - die(_("unable to write temporary index file")); - - discard_cache(); - ret = get_lock_file_path(&false_lock); - read_cache_from(ret); -out: - string_list_clear(&partial, 0); - clear_pathspec(&pathspec); - return ret; -} - -static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn, - struct wt_status *s) -{ - struct object_id oid; - - if (s->relative_paths) - s->prefix = prefix; - - if (amend) { - s->amend = 1; - s->reference = "HEAD^1"; - } - s->verbose = verbose; - s->index_file = index_file; - s->fp = fp; - s->nowarn = nowarn; - s->is_initial = get_oid(s->reference, &oid) ? 1 : 0; - if (!s->is_initial) - oidcpy(&s->oid_commit, &oid); - s->status_format = status_format; - s->ignore_submodule_arg = ignore_submodule_arg; - - wt_status_collect(s); - wt_status_print(s); - wt_status_collect_free_buffers(s); - - return s->committable; -} - -static int is_a_merge(const struct commit *current_head) -{ - return !!(current_head->parents && current_head->parents->next); -} - -static void assert_split_ident(struct ident_split *id, const struct strbuf *buf) -{ - if (split_ident_line(id, buf->buf, buf->len) || !id->date_begin) - BUG("unable to parse our own ident: %s", buf->buf); -} - -static void export_one(const char *var, const char *s, const char *e, int hack) -{ - struct strbuf buf = STRBUF_INIT; - if (hack) - strbuf_addch(&buf, hack); - strbuf_add(&buf, s, e - s); - setenv(var, buf.buf, 1); - strbuf_release(&buf); -} - -static int parse_force_date(const char *in, struct strbuf *out) -{ - strbuf_addch(out, '@'); - - if (parse_date(in, out) < 0) { - int errors = 0; - unsigned long t = approxidate_careful(in, &errors); - if (errors) - return -1; - strbuf_addf(out, "%lu", t); - } - - return 0; -} - -static void set_ident_var(char **buf, char *val) -{ - free(*buf); - *buf = val; -} - -static void determine_author_info(struct strbuf *author_ident) -{ - char *name, *email, *date; - struct ident_split author; - - name = xstrdup_or_null(getenv("GIT_AUTHOR_NAME")); - email = xstrdup_or_null(getenv("GIT_AUTHOR_EMAIL")); - date = xstrdup_or_null(getenv("GIT_AUTHOR_DATE")); - - if (author_message) { - struct ident_split ident; - size_t len; - const char *a; - - a = find_commit_header(author_message_buffer, "author", &len); - if (!a) - die(_("commit '%s' lacks author header"), author_message); - if (split_ident_line(&ident, a, len) < 0) - die(_("commit '%s' has malformed author line"), author_message); - - set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin)); - set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin)); - - if (ident.date_begin) { - struct strbuf date_buf = STRBUF_INIT; - strbuf_addch(&date_buf, '@'); - strbuf_add(&date_buf, ident.date_begin, ident.date_end - ident.date_begin); - strbuf_addch(&date_buf, ' '); - strbuf_add(&date_buf, ident.tz_begin, ident.tz_end - ident.tz_begin); - set_ident_var(&date, strbuf_detach(&date_buf, NULL)); - } - } - - if (force_author) { - struct ident_split ident; - - if (split_ident_line(&ident, force_author, strlen(force_author)) < 0) - die(_("malformed --author parameter")); - set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin)); - set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin)); - } - - if (force_date) { - struct strbuf date_buf = STRBUF_INIT; - if (parse_force_date(force_date, &date_buf)) - die(_("invalid date format: %s"), force_date); - set_ident_var(&date, strbuf_detach(&date_buf, NULL)); - } - - strbuf_addstr(author_ident, fmt_ident(name, email, WANT_AUTHOR_IDENT, date, - IDENT_STRICT)); - assert_split_ident(&author, author_ident); - export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0); - export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0); - export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@'); - free(name); - free(email); - free(date); -} - -static int author_date_is_interesting(void) -{ - return author_message || force_date; -} - -static void adjust_comment_line_char(const struct strbuf *sb) -{ - char candidates[] = "#;@!$%^&|:"; - char *candidate; - const char *p; - - comment_line_char = candidates[0]; - if (!memchr(sb->buf, comment_line_char, sb->len)) - return; - - p = sb->buf; - candidate = strchr(candidates, *p); - if (candidate) - *candidate = ' '; - for (p = sb->buf; *p; p++) { - if ((p[0] == '\n' || p[0] == '\r') && p[1]) { - candidate = strchr(candidates, p[1]); - if (candidate) - *candidate = ' '; - } - } - - for (p = candidates; *p == ' '; p++) - ; - if (!*p) - die(_("unable to select a comment character that is not used\n" - "in the current commit message")); - comment_line_char = *p; -} - -static int prepare_to_commit(const char *index_file, const char *prefix, - struct commit *current_head, - struct wt_status *s, - struct strbuf *author_ident) -{ - struct stat statbuf; - struct strbuf committer_ident = STRBUF_INIT; - int committable; - struct strbuf sb = STRBUF_INIT; - const char *hook_arg1 = NULL; - const char *hook_arg2 = NULL; - int clean_message_contents = (cleanup_mode != COMMIT_MSG_CLEANUP_NONE); - int old_display_comment_prefix; - int merge_contains_scissors = 0; - - /* This checks and barfs if author is badly specified */ - determine_author_info(author_ident); - - if (!no_verify && run_commit_hook(use_editor, index_file, "pre-commit", NULL)) - return 0; - - if (squash_message) { - /* - * Insert the proper subject line before other commit - * message options add their content. - */ - if (use_message && !strcmp(use_message, squash_message)) - strbuf_addstr(&sb, "squash! "); - else { - struct pretty_print_context ctx = {0}; - struct commit *c; - c = lookup_commit_reference_by_name(squash_message); - if (!c) - die(_("could not lookup commit %s"), squash_message); - ctx.output_encoding = get_commit_output_encoding(); - format_commit_message(c, "squash! %s\n\n", &sb, - &ctx); - } - } - - if (have_option_m && !fixup_message) { - strbuf_addbuf(&sb, &message); - hook_arg1 = "message"; - } else if (logfile && !strcmp(logfile, "-")) { - if (isatty(0)) - fprintf(stderr, _("(reading log message from standard input)\n")); - if (strbuf_read(&sb, 0, 0) < 0) - die_errno(_("could not read log from standard input")); - hook_arg1 = "message"; - } else if (logfile) { - if (strbuf_read_file(&sb, logfile, 0) < 0) - die_errno(_("could not read log file '%s'"), - logfile); - hook_arg1 = "message"; - } else if (use_message) { - char *buffer; - buffer = strstr(use_message_buffer, "\n\n"); - if (buffer) - strbuf_addstr(&sb, skip_blank_lines(buffer + 2)); - hook_arg1 = "commit"; - hook_arg2 = use_message; - } else if (fixup_message) { - struct pretty_print_context ctx = {0}; - struct commit *commit; - commit = lookup_commit_reference_by_name(fixup_message); - if (!commit) - die(_("could not lookup commit %s"), fixup_message); - ctx.output_encoding = get_commit_output_encoding(); - format_commit_message(commit, "fixup! %s\n\n", - &sb, &ctx); - if (have_option_m) - strbuf_addbuf(&sb, &message); - hook_arg1 = "message"; - } else if (!stat(git_path_merge_msg(the_repository), &statbuf)) { - size_t merge_msg_start; - - /* - * prepend SQUASH_MSG here if it exists and a - * "merge --squash" was originally performed - */ - if (!stat(git_path_squash_msg(the_repository), &statbuf)) { - if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0) - die_errno(_("could not read SQUASH_MSG")); - hook_arg1 = "squash"; - } else - hook_arg1 = "merge"; - - merge_msg_start = sb.len; - if (strbuf_read_file(&sb, git_path_merge_msg(the_repository), 0) < 0) - die_errno(_("could not read MERGE_MSG")); - - if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS && - wt_status_locate_end(sb.buf + merge_msg_start, - sb.len - merge_msg_start) < - sb.len - merge_msg_start) - merge_contains_scissors = 1; - } else if (!stat(git_path_squash_msg(the_repository), &statbuf)) { - if (strbuf_read_file(&sb, git_path_squash_msg(the_repository), 0) < 0) - die_errno(_("could not read SQUASH_MSG")); - hook_arg1 = "squash"; - } else if (template_file) { - if (strbuf_read_file(&sb, template_file, 0) < 0) - die_errno(_("could not read '%s'"), template_file); - hook_arg1 = "template"; - clean_message_contents = 0; - } - - /* - * The remaining cases don't modify the template message, but - * just set the argument(s) to the prepare-commit-msg hook. - */ - else if (whence == FROM_MERGE) - hook_arg1 = "merge"; - else if (is_from_cherry_pick(whence) || whence == FROM_REBASE_PICK) { - hook_arg1 = "commit"; - hook_arg2 = "CHERRY_PICK_HEAD"; - } - - if (squash_message) { - /* - * If squash_commit was used for the commit subject, - * then we're possibly hijacking other commit log options. - * Reset the hook args to tell the real story. - */ - hook_arg1 = "message"; - hook_arg2 = ""; - } - - s->fp = fopen_for_writing(git_path_commit_editmsg()); - if (s->fp == NULL) - die_errno(_("could not open '%s'"), git_path_commit_editmsg()); - - /* Ignore status.displayCommentPrefix: we do need comments in COMMIT_EDITMSG. */ - old_display_comment_prefix = s->display_comment_prefix; - s->display_comment_prefix = 1; - - /* - * Most hints are counter-productive when the commit has - * already started. - */ - s->hints = 0; - - if (clean_message_contents) - strbuf_stripspace(&sb, 0); - - if (signoff) - append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0); - - if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len) - die_errno(_("could not write commit template")); - - if (auto_comment_line_char) - adjust_comment_line_char(&sb); - strbuf_release(&sb); - - /* This checks if committer ident is explicitly given */ - strbuf_addstr(&committer_ident, git_committer_info(IDENT_STRICT)); - if (use_editor && include_status) { - int ident_shown = 0; - int saved_color_setting; - struct ident_split ci, ai; - - if (whence != FROM_COMMIT) { - if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS && - !merge_contains_scissors) - wt_status_add_cut_line(s->fp); - status_printf_ln( - s, GIT_COLOR_NORMAL, - whence == FROM_MERGE ? - _("\n" - "It looks like you may be committing a merge.\n" - "If this is not correct, please run\n" - " git update-ref -d MERGE_HEAD\n" - "and try again.\n") : - _("\n" - "It looks like you may be committing a cherry-pick.\n" - "If this is not correct, please run\n" - " git update-ref -d CHERRY_PICK_HEAD\n" - "and try again.\n")); - } - - fprintf(s->fp, "\n"); - if (cleanup_mode == COMMIT_MSG_CLEANUP_ALL) - status_printf(s, GIT_COLOR_NORMAL, - _("Please enter the commit message for your changes." - " Lines starting\nwith '%c' will be ignored, and an empty" - " message aborts the commit.\n"), comment_line_char); - else if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) { - if (whence == FROM_COMMIT && !merge_contains_scissors) - wt_status_add_cut_line(s->fp); - } else /* COMMIT_MSG_CLEANUP_SPACE, that is. */ - status_printf(s, GIT_COLOR_NORMAL, - _("Please enter the commit message for your changes." - " Lines starting\n" - "with '%c' will be kept; you may remove them" - " yourself if you want to.\n" - "An empty message aborts the commit.\n"), comment_line_char); - - /* - * These should never fail because they come from our own - * fmt_ident. They may fail the sane_ident test, but we know - * that the name and mail pointers will at least be valid, - * which is enough for our tests and printing here. - */ - assert_split_ident(&ai, author_ident); - assert_split_ident(&ci, &committer_ident); - - if (ident_cmp(&ai, &ci)) - status_printf_ln(s, GIT_COLOR_NORMAL, - _("%s" - "Author: %.*s <%.*s>"), - ident_shown++ ? "" : "\n", - (int)(ai.name_end - ai.name_begin), ai.name_begin, - (int)(ai.mail_end - ai.mail_begin), ai.mail_begin); - - if (author_date_is_interesting()) - status_printf_ln(s, GIT_COLOR_NORMAL, - _("%s" - "Date: %s"), - ident_shown++ ? "" : "\n", - show_ident_date(&ai, DATE_MODE(NORMAL))); - - if (!committer_ident_sufficiently_given()) - status_printf_ln(s, GIT_COLOR_NORMAL, - _("%s" - "Committer: %.*s <%.*s>"), - ident_shown++ ? "" : "\n", - (int)(ci.name_end - ci.name_begin), ci.name_begin, - (int)(ci.mail_end - ci.mail_begin), ci.mail_begin); - - status_printf_ln(s, GIT_COLOR_NORMAL, "%s", ""); /* Add new line for clarity */ - - saved_color_setting = s->use_color; - s->use_color = 0; - committable = run_status(s->fp, index_file, prefix, 1, s); - s->use_color = saved_color_setting; - string_list_clear(&s->change, 1); - } else { - struct object_id oid; - const char *parent = "HEAD"; - - if (!active_nr && read_cache() < 0) - die(_("Cannot read index")); - - if (amend) - parent = "HEAD^1"; - - if (get_oid(parent, &oid)) { - int i, ita_nr = 0; - - for (i = 0; i < active_nr; i++) - if (ce_intent_to_add(active_cache[i])) - ita_nr++; - committable = active_nr - ita_nr > 0; - } else { - /* - * Unless the user did explicitly request a submodule - * ignore mode by passing a command line option we do - * not ignore any changed submodule SHA-1s when - * comparing index and parent, no matter what is - * configured. Otherwise we won't commit any - * submodules which were manually staged, which would - * be really confusing. - */ - struct diff_flags flags = DIFF_FLAGS_INIT; - flags.override_submodule_config = 1; - if (ignore_submodule_arg && - !strcmp(ignore_submodule_arg, "all")) - flags.ignore_submodules = 1; - committable = index_differs_from(the_repository, - parent, &flags, 1); - } - } - strbuf_release(&committer_ident); - - fclose(s->fp); - - /* - * Reject an attempt to record a non-merge empty commit without - * explicit --allow-empty. In the cherry-pick case, it may be - * empty due to conflict resolution, which the user should okay. - */ - if (!committable && whence != FROM_MERGE && !allow_empty && - !(amend && is_a_merge(current_head))) { - s->hints = advice_status_hints; - s->display_comment_prefix = old_display_comment_prefix; - run_status(stdout, index_file, prefix, 0, s); - if (amend) - fputs(_(empty_amend_advice), stderr); - else if (is_from_cherry_pick(whence) || - whence == FROM_REBASE_PICK) { - fputs(_(empty_cherry_pick_advice), stderr); - if (whence == FROM_CHERRY_PICK_SINGLE) - fputs(_(empty_cherry_pick_advice_single), stderr); - else if (whence == FROM_CHERRY_PICK_MULTI) - fputs(_(empty_cherry_pick_advice_multi), stderr); - else - fputs(_(empty_rebase_pick_advice), stderr); - } - return 0; - } - - if (!no_verify && find_hook("pre-commit")) { - /* - * Re-read the index as pre-commit hook could have updated it, - * and write it out as a tree. We must do this before we invoke - * the editor and after we invoke run_status above. - */ - discard_cache(); - } - read_cache_from(index_file); - - if (update_main_cache_tree(0)) { - error(_("Error building trees")); - return 0; - } - - if (run_commit_hook(use_editor, index_file, "prepare-commit-msg", - git_path_commit_editmsg(), hook_arg1, hook_arg2, NULL)) - return 0; - - if (use_editor) { - struct strvec env = STRVEC_INIT; - - strvec_pushf(&env, "GIT_INDEX_FILE=%s", index_file); - if (launch_editor(git_path_commit_editmsg(), NULL, env.v)) { - fprintf(stderr, - _("Please supply the message using either -m or -F option.\n")); - exit(1); - } - strvec_clear(&env); - } - - if (!no_verify && - run_commit_hook(use_editor, index_file, "commit-msg", git_path_commit_editmsg(), NULL)) { - return 0; - } - - return 1; -} - -static const char *find_author_by_nickname(const char *name) -{ - struct rev_info revs; - struct commit *commit; - struct strbuf buf = STRBUF_INIT; - struct string_list mailmap = STRING_LIST_INIT_NODUP; - const char *av[20]; - int ac = 0; - - repo_init_revisions(the_repository, &revs, NULL); - strbuf_addf(&buf, "--author=%s", name); - av[++ac] = "--all"; - av[++ac] = "-i"; - av[++ac] = buf.buf; - av[++ac] = NULL; - setup_revisions(ac, av, &revs, NULL); - revs.mailmap = &mailmap; - read_mailmap(revs.mailmap, NULL); - - if (prepare_revision_walk(&revs)) - die(_("revision walk setup failed")); - commit = get_revision(&revs); - if (commit) { - struct pretty_print_context ctx = {0}; - ctx.date_mode.type = DATE_NORMAL; - strbuf_release(&buf); - format_commit_message(commit, "%aN <%aE>", &buf, &ctx); - clear_mailmap(&mailmap); - return strbuf_detach(&buf, NULL); - } - die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name); -} - -static void handle_ignored_arg(struct wt_status *s) -{ - if (!ignored_arg) - ; /* default already initialized */ - else if (!strcmp(ignored_arg, "traditional")) - s->show_ignored_mode = SHOW_TRADITIONAL_IGNORED; - else if (!strcmp(ignored_arg, "no")) - s->show_ignored_mode = SHOW_NO_IGNORED; - else if (!strcmp(ignored_arg, "matching")) - s->show_ignored_mode = SHOW_MATCHING_IGNORED; - else - die(_("Invalid ignored mode '%s'"), ignored_arg); -} - -static void handle_untracked_files_arg(struct wt_status *s) -{ - if (!untracked_files_arg) - ; /* default already initialized */ - else if (!strcmp(untracked_files_arg, "no")) - s->show_untracked_files = SHOW_NO_UNTRACKED_FILES; - else if (!strcmp(untracked_files_arg, "normal")) - s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; - else if (!strcmp(untracked_files_arg, "all")) - s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES; - /* - * Please update $__git_untracked_file_modes in - * git-completion.bash when you add new options - */ - else - die(_("Invalid untracked files mode '%s'"), untracked_files_arg); -} - -static const char *read_commit_message(const char *name) -{ - const char *out_enc; - struct commit *commit; - - commit = lookup_commit_reference_by_name(name); - if (!commit) - die(_("could not lookup commit %s"), name); - out_enc = get_commit_output_encoding(); - return logmsg_reencode(commit, NULL, out_enc); -} - -/* - * Enumerate what needs to be propagated when --porcelain - * is not in effect here. - */ -static struct status_deferred_config { - enum wt_status_format status_format; - int show_branch; - enum ahead_behind_flags ahead_behind; -} status_deferred_config = { - STATUS_FORMAT_UNSPECIFIED, - -1, /* unspecified */ - AHEAD_BEHIND_UNSPECIFIED, -}; - -static void finalize_deferred_config(struct wt_status *s) -{ - int use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN && - status_format != STATUS_FORMAT_PORCELAIN_V2 && - !s->null_termination); - - if (s->null_termination) { - if (status_format == STATUS_FORMAT_NONE || - status_format == STATUS_FORMAT_UNSPECIFIED) - status_format = STATUS_FORMAT_PORCELAIN; - else if (status_format == STATUS_FORMAT_LONG) - die(_("--long and -z are incompatible")); - } - - if (use_deferred_config && status_format == STATUS_FORMAT_UNSPECIFIED) - status_format = status_deferred_config.status_format; - if (status_format == STATUS_FORMAT_UNSPECIFIED) - status_format = STATUS_FORMAT_NONE; - - if (use_deferred_config && s->show_branch < 0) - s->show_branch = status_deferred_config.show_branch; - if (s->show_branch < 0) - s->show_branch = 0; - - /* - * If the user did not give a "--[no]-ahead-behind" command - * line argument *AND* we will print in a human-readable format - * (short, long etc.) then we inherit from the status.aheadbehind - * config setting. In all other cases (and porcelain V[12] formats - * in particular), we inherit _FULL for backwards compatibility. - */ - if (use_deferred_config && - s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED) - s->ahead_behind_flags = status_deferred_config.ahead_behind; - - if (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED) - s->ahead_behind_flags = AHEAD_BEHIND_FULL; -} - -static int parse_and_validate_options(int argc, const char *argv[], - const struct option *options, - const char * const usage[], - const char *prefix, - struct commit *current_head, - struct wt_status *s) -{ - int f = 0; - - argc = parse_options(argc, argv, prefix, options, usage, 0); - finalize_deferred_config(s); - - if (force_author && !strchr(force_author, '>')) - force_author = find_author_by_nickname(force_author); - - if (force_author && renew_authorship) - die(_("Using both --reset-author and --author does not make sense")); - - if (logfile || have_option_m || use_message || fixup_message) - use_editor = 0; - if (0 <= edit_flag) - use_editor = edit_flag; - - /* Sanity check options */ - if (amend && !current_head) - die(_("You have nothing to amend.")); - if (amend && whence != FROM_COMMIT) { - if (whence == FROM_MERGE) - die(_("You are in the middle of a merge -- cannot amend.")); - else if (is_from_cherry_pick(whence)) - die(_("You are in the middle of a cherry-pick -- cannot amend.")); - else if (whence == FROM_REBASE_PICK) - die(_("You are in the middle of a rebase -- cannot amend.")); - } - if (fixup_message && squash_message) - die(_("Options --squash and --fixup cannot be used together")); - if (use_message) - f++; - if (edit_message) - f++; - if (fixup_message) - f++; - if (logfile) - f++; - if (f > 1) - die(_("Only one of -c/-C/-F/--fixup can be used.")); - if (have_option_m && (edit_message || use_message || logfile)) - die((_("Option -m cannot be combined with -c/-C/-F."))); - if (f || have_option_m) - template_file = NULL; - if (edit_message) - use_message = edit_message; - if (amend && !use_message && !fixup_message) - use_message = "HEAD"; - if (!use_message && !is_from_cherry_pick(whence) && - !is_from_rebase(whence) && renew_authorship) - die(_("--reset-author can be used only with -C, -c or --amend.")); - if (use_message) { - use_message_buffer = read_commit_message(use_message); - if (!renew_authorship) { - author_message = use_message; - author_message_buffer = use_message_buffer; - } - } - if ((is_from_cherry_pick(whence) || whence == FROM_REBASE_PICK) && - !renew_authorship) { - author_message = "CHERRY_PICK_HEAD"; - author_message_buffer = read_commit_message(author_message); - } - - if (patch_interactive) - interactive = 1; - - if (also + only + all + interactive > 1) - die(_("Only one of --include/--only/--all/--interactive/--patch can be used.")); - cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor); - - handle_untracked_files_arg(s); - - if (all && argc > 0) - die(_("paths '%s ...' with -a does not make sense"), - argv[0]); - - if (status_format != STATUS_FORMAT_NONE) - dry_run = 1; - - return argc; -} - -static int dry_run_commit(const char **argv, const char *prefix, - const struct commit *current_head, struct wt_status *s) -{ - int committable; - const char *index_file; - - index_file = prepare_index(argv, prefix, current_head, 1); - committable = run_status(stdout, index_file, prefix, 0, s); - rollback_index_files(); - - return committable ? 0 : 1; -} - -define_list_config_array_extra(color_status_slots, {"added"}); - -static int parse_status_slot(const char *slot) -{ - if (!strcasecmp(slot, "added")) - return WT_STATUS_UPDATED; - - return LOOKUP_CONFIG(color_status_slots, slot); -} - -static int git_status_config(const char *k, const char *v, void *cb) -{ - struct wt_status *s = cb; - const char *slot_name; - - if (starts_with(k, "column.")) - return git_column_config(k, v, "status", &s->colopts); - if (!strcmp(k, "status.submodulesummary")) { - int is_bool; - s->submodule_summary = git_config_bool_or_int(k, v, &is_bool); - if (is_bool && s->submodule_summary) - s->submodule_summary = -1; - return 0; - } - if (!strcmp(k, "status.short")) { - if (git_config_bool(k, v)) - status_deferred_config.status_format = STATUS_FORMAT_SHORT; - else - status_deferred_config.status_format = STATUS_FORMAT_NONE; - return 0; - } - if (!strcmp(k, "status.branch")) { - status_deferred_config.show_branch = git_config_bool(k, v); - return 0; - } - if (!strcmp(k, "status.aheadbehind")) { - status_deferred_config.ahead_behind = git_config_bool(k, v); - return 0; - } - if (!strcmp(k, "status.showstash")) { - s->show_stash = git_config_bool(k, v); - return 0; - } - if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) { - s->use_color = git_config_colorbool(k, v); - return 0; - } - if (!strcmp(k, "status.displaycommentprefix")) { - s->display_comment_prefix = git_config_bool(k, v); - return 0; - } - if (skip_prefix(k, "status.color.", &slot_name) || - skip_prefix(k, "color.status.", &slot_name)) { - int slot = parse_status_slot(slot_name); - if (slot < 0) - return 0; - if (!v) - return config_error_nonbool(k); - return color_parse(v, s->color_palette[slot]); - } - if (!strcmp(k, "status.relativepaths")) { - s->relative_paths = git_config_bool(k, v); - return 0; - } - if (!strcmp(k, "status.showuntrackedfiles")) { - if (!v) - return config_error_nonbool(k); - else if (!strcmp(v, "no")) - s->show_untracked_files = SHOW_NO_UNTRACKED_FILES; - else if (!strcmp(v, "normal")) - s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; - else if (!strcmp(v, "all")) - s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES; - else - return error(_("Invalid untracked files mode '%s'"), v); - return 0; - } - if (!strcmp(k, "diff.renamelimit")) { - if (s->rename_limit == -1) - s->rename_limit = git_config_int(k, v); - return 0; - } - if (!strcmp(k, "status.renamelimit")) { - s->rename_limit = git_config_int(k, v); - return 0; - } - if (!strcmp(k, "diff.renames")) { - if (s->detect_rename == -1) - s->detect_rename = git_config_rename(k, v); - return 0; - } - if (!strcmp(k, "status.renames")) { - s->detect_rename = git_config_rename(k, v); - return 0; - } - return git_diff_ui_config(k, v, NULL); -} - -int cmd_status(int argc, const char **argv, const char *prefix) -{ - static int no_renames = -1; - static const char *rename_score_arg = (const char *)-1; - static struct wt_status s; - unsigned int progress_flag = 0; - int fd; - struct object_id oid; - 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_BOOL(0, "show-stash", &s.show_stash, - N_("show stash information")), - OPT_BOOL(0, "ahead-behind", &s.ahead_behind_flags, - N_("compute full ahead/behind values")), - OPT_CALLBACK_F(0, "porcelain", &status_format, - N_("version"), N_("machine-readable output"), - PARSE_OPT_OPTARG, opt_parse_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" }, - { OPTION_STRING, 0, "ignored", &ignored_arg, - N_("mode"), - N_("show ignored files, optional modes: traditional, matching, no. (Default: traditional)"), - PARSE_OPT_OPTARG, NULL, (intptr_t)"traditional" }, - { 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_BOOL(0, "no-renames", &no_renames, N_("do not detect renames")), - OPT_CALLBACK_F('M', "find-renames", &rename_score_arg, - N_("n"), N_("detect renames, optionally set similarity index"), - PARSE_OPT_OPTARG | PARSE_OPT_NONEG, opt_parse_rename_score), - 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); - handle_ignored_arg(&s); - - if (s.show_ignored_mode == SHOW_MATCHING_IGNORED && - s.show_untracked_files == SHOW_NO_UNTRACKED_FILES) - die(_("Unsupported combination of ignored and untracked-files arguments")); - - parse_pathspec(&s.pathspec, 0, - PATHSPEC_PREFER_FULL, - prefix, argv); - - if (status_format != STATUS_FORMAT_PORCELAIN && - status_format != STATUS_FORMAT_PORCELAIN_V2) - progress_flag = REFRESH_PROGRESS; - repo_read_index(the_repository); - refresh_index(&the_index, - REFRESH_QUIET|REFRESH_UNMERGED|progress_flag, - &s.pathspec, NULL, NULL); - - if (use_optional_locks()) - fd = hold_locked_index(&index_lock, 0); - else - fd = -1; - - s.is_initial = get_oid(s.reference, &oid) ? 1 : 0; - if (!s.is_initial) - oidcpy(&s.oid_commit, &oid); - - s.ignore_submodule_arg = ignore_submodule_arg; - s.status_format = status_format; - s.verbose = verbose; - if (no_renames != -1) - s.detect_rename = !no_renames; - if ((intptr_t)rename_score_arg != -1) { - if (s.detect_rename < DIFF_DETECT_RENAME) - s.detect_rename = DIFF_DETECT_RENAME; - if (rename_score_arg) - s.rename_score = parse_rename_score(&rename_score_arg); - } - - wt_status_collect(&s); - - if (0 <= fd) - repo_update_index_if_able(the_repository, &index_lock); - - if (s.relative_paths) - s.prefix = prefix; - - wt_status_print(&s); - wt_status_collect_free_buffers(&s); - - return 0; -} - -static int git_commit_config(const char *k, const char *v, void *cb) -{ - struct wt_status *s = cb; - int status; - - if (!strcmp(k, "commit.template")) - return git_config_pathname(&template_file, k, v); - if (!strcmp(k, "commit.status")) { - include_status = git_config_bool(k, v); - return 0; - } - if (!strcmp(k, "commit.cleanup")) - return git_config_string(&cleanup_arg, k, v); - if (!strcmp(k, "commit.gpgsign")) { - sign_commit = git_config_bool(k, v) ? "" : NULL; - return 0; - } - if (!strcmp(k, "commit.verbose")) { - int is_bool; - config_commit_verbose = git_config_bool_or_int(k, v, &is_bool); - return 0; - } - - status = git_gpg_config(k, v, NULL); - if (status) - return status; - return git_status_config(k, v, s); -} - -int cmd_commit(int argc, const char **argv, const char *prefix) -{ - static struct wt_status s; - static struct option builtin_commit_options[] = { - OPT__QUIET(&quiet, N_("suppress summary after successful commit")), - OPT__VERBOSE(&verbose, N_("show diff in commit message template")), - - OPT_GROUP(N_("Commit message options")), - OPT_FILENAME('F', "file", &logfile, N_("read message from file")), - OPT_STRING(0, "author", &force_author, N_("author"), N_("override author for commit")), - OPT_STRING(0, "date", &force_date, N_("date"), N_("override date for commit")), - OPT_CALLBACK('m', "message", &message, N_("message"), N_("commit message"), opt_parse_m), - OPT_STRING('c', "reedit-message", &edit_message, N_("commit"), N_("reuse and edit message from specified commit")), - OPT_STRING('C', "reuse-message", &use_message, N_("commit"), N_("reuse message from specified commit")), - OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup specified commit")), - OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")), - OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")), - OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")), - OPT_FILENAME('t', "template", &template_file, N_("use specified template file")), - OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")), - OPT_CLEANUP(&cleanup_arg), - OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")), - { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), - N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - /* end commit message options */ - - OPT_GROUP(N_("Commit contents options")), - OPT_BOOL('a', "all", &all, N_("commit all changed files")), - OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")), - OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")), - OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")), - OPT_BOOL('o', "only", &only, N_("commit only specified files")), - OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")), - OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")), - OPT_SET_INT(0, "short", &status_format, N_("show status concisely"), - STATUS_FORMAT_SHORT), - OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")), - OPT_BOOL(0, "ahead-behind", &s.ahead_behind_flags, - N_("compute full ahead/behind values")), - 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")), - OPT_BOOL(0, "amend", &amend, N_("amend previous commit")), - OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")), - { 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_PATHSPEC_FROM_FILE(&pathspec_from_file), - OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), - /* end commit contents options */ - - OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty, - N_("ok to record an empty change")), - OPT_HIDDEN_BOOL(0, "allow-empty-message", &allow_empty_message, - N_("ok to record a change with an empty message")), - - OPT_END() - }; - - struct strbuf sb = STRBUF_INIT; - struct strbuf author_ident = STRBUF_INIT; - const char *index_file, *reflog_msg; - struct object_id oid; - struct commit_list *parents = NULL; - struct stat statbuf; - struct commit *current_head = NULL; - struct commit_extra_header *extra = NULL; - struct strbuf err = STRBUF_INIT; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage_with_options(builtin_commit_usage, builtin_commit_options); - - status_init_config(&s, git_commit_config); - s.commit_template = 1; - status_format = STATUS_FORMAT_NONE; /* Ignore status.short */ - s.colopts = 0; - - if (get_oid("HEAD", &oid)) - current_head = NULL; - else { - current_head = lookup_commit_or_die(&oid, "HEAD"); - if (parse_commit(current_head)) - die(_("could not parse HEAD commit")); - } - verbose = -1; /* unspecified */ - argc = parse_and_validate_options(argc, argv, builtin_commit_options, - builtin_commit_usage, - prefix, current_head, &s); - if (verbose == -1) - verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose; - - if (dry_run) - return dry_run_commit(argv, prefix, current_head, &s); - index_file = prepare_index(argv, prefix, current_head, 0); - - /* Set up everything for writing the commit object. This includes - running hooks, writing the trees, and interacting with the user. */ - if (!prepare_to_commit(index_file, prefix, - current_head, &s, &author_ident)) { - rollback_index_files(); - return 1; - } - - /* Determine parents */ - reflog_msg = getenv("GIT_REFLOG_ACTION"); - if (!current_head) { - if (!reflog_msg) - reflog_msg = "commit (initial)"; - } else if (amend) { - if (!reflog_msg) - reflog_msg = "commit (amend)"; - parents = copy_commit_list(current_head->parents); - } else if (whence == FROM_MERGE) { - struct strbuf m = STRBUF_INIT; - FILE *fp; - int allow_fast_forward = 1; - struct commit_list **pptr = &parents; - - if (!reflog_msg) - reflog_msg = "commit (merge)"; - pptr = commit_list_append(current_head, pptr); - fp = xfopen(git_path_merge_head(the_repository), "r"); - while (strbuf_getline_lf(&m, fp) != EOF) { - struct commit *parent; - - parent = get_merge_parent(m.buf); - if (!parent) - die(_("Corrupt MERGE_HEAD file (%s)"), m.buf); - pptr = commit_list_append(parent, pptr); - } - fclose(fp); - strbuf_release(&m); - if (!stat(git_path_merge_mode(the_repository), &statbuf)) { - if (strbuf_read_file(&sb, git_path_merge_mode(the_repository), 0) < 0) - die_errno(_("could not read MERGE_MODE")); - if (!strcmp(sb.buf, "no-ff")) - allow_fast_forward = 0; - } - if (allow_fast_forward) - reduce_heads_replace(&parents); - } else { - if (!reflog_msg) - reflog_msg = is_from_cherry_pick(whence) - ? "commit (cherry-pick)" - : is_from_rebase(whence) - ? "commit (rebase)" - : "commit"; - commit_list_insert(current_head, &parents); - } - - /* Finally, get the commit message */ - strbuf_reset(&sb); - if (strbuf_read_file(&sb, git_path_commit_editmsg(), 0) < 0) { - int saved_errno = errno; - rollback_index_files(); - die(_("could not read commit message: %s"), strerror(saved_errno)); - } - - cleanup_message(&sb, cleanup_mode, verbose); - - if (message_is_empty(&sb, cleanup_mode) && !allow_empty_message) { - rollback_index_files(); - fprintf(stderr, _("Aborting commit due to empty commit message.\n")); - exit(1); - } - if (template_untouched(&sb, template_file, cleanup_mode) && !allow_empty_message) { - rollback_index_files(); - fprintf(stderr, _("Aborting commit; you did not edit the message.\n")); - exit(1); - } - - if (amend) { - const char *exclude_gpgsig[3] = { "gpgsig", "gpgsig-sha256", NULL }; - extra = read_commit_extra_headers(current_head, exclude_gpgsig); - } else { - struct commit_extra_header **tail = &extra; - append_merge_tag_headers(parents, &tail); - } - - if (commit_tree_extended(sb.buf, sb.len, &active_cache_tree->oid, - parents, &oid, author_ident.buf, NULL, - sign_commit, extra)) { - rollback_index_files(); - die(_("failed to write commit object")); - } - strbuf_release(&author_ident); - free_commit_extra_headers(extra); - - if (update_head_with_reflog(current_head, &oid, reflog_msg, &sb, - &err)) { - rollback_index_files(); - die("%s", err.buf); - } - - sequencer_post_commit_cleanup(the_repository, 0); - unlink(git_path_merge_head(the_repository)); - unlink(git_path_merge_msg(the_repository)); - unlink(git_path_merge_mode(the_repository)); - unlink(git_path_squash_msg(the_repository)); - - if (commit_index_files()) - die(_("repository has been updated, but unable to write\n" - "new_index file. Check that disk is not full and quota is\n" - "not exceeded, and then \"git restore --staged :/\" to recover.")); - - git_test_write_commit_graph_or_die(); - - repo_rerere(the_repository, 0); - run_auto_maintenance(quiet); - run_commit_hook(use_editor, get_index_file(), "post-commit", NULL); - if (amend && !no_post_rewrite) { - commit_post_rewrite(the_repository, current_head, &oid); - } - if (!quiet) { - unsigned int flags = 0; - - if (!current_head) - flags |= SUMMARY_INITIAL_COMMIT; - if (author_date_is_interesting()) - flags |= SUMMARY_SHOW_AUTHOR_DATE; - print_commit_summary(the_repository, prefix, - &oid, flags); - } - - apply_autostash(git_path_merge_autostash(the_repository)); - - UNLEAK(err); - UNLEAK(sb); - return 0; -} diff --git a/third_party/git/builtin/config.c b/third_party/git/builtin/config.c deleted file mode 100644 index 963d65fd3fc1..000000000000 --- a/third_party/git/builtin/config.c +++ /dev/null @@ -1,919 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "color.h" -#include "parse-options.h" -#include "urlmatch.h" -#include "quote.h" -#include "worktree.h" - -static const char *const builtin_config_usage[] = { - N_("git config [<options>]"), - NULL -}; - -static char *key; -static regex_t *key_regexp; -static regex_t *regexp; -static int show_keys; -static int omit_values; -static int use_key_regexp; -static int do_all; -static int do_not_match; -static char delim = '='; -static char key_delim = ' '; -static char term = '\n'; - -static int use_global_config, use_system_config, use_local_config; -static int use_worktree_config; -static struct git_config_source given_config_source; -static int actions, type; -static char *default_value; -static int end_nul; -static int respect_includes_opt = -1; -static struct config_options config_options; -static int show_origin; -static int show_scope; - -#define ACTION_GET (1<<0) -#define ACTION_GET_ALL (1<<1) -#define ACTION_GET_REGEXP (1<<2) -#define ACTION_REPLACE_ALL (1<<3) -#define ACTION_ADD (1<<4) -#define ACTION_UNSET (1<<5) -#define ACTION_UNSET_ALL (1<<6) -#define ACTION_RENAME_SECTION (1<<7) -#define ACTION_REMOVE_SECTION (1<<8) -#define ACTION_LIST (1<<9) -#define ACTION_EDIT (1<<10) -#define ACTION_SET (1<<11) -#define ACTION_SET_ALL (1<<12) -#define ACTION_GET_COLOR (1<<13) -#define ACTION_GET_COLORBOOL (1<<14) -#define ACTION_GET_URLMATCH (1<<15) - -/* - * The actions "ACTION_LIST | ACTION_GET_*" which may produce more than - * one line of output and which should therefore be paged. - */ -#define PAGING_ACTIONS (ACTION_LIST | ACTION_GET_ALL | \ - ACTION_GET_REGEXP | ACTION_GET_URLMATCH) - -#define TYPE_BOOL 1 -#define TYPE_INT 2 -#define TYPE_BOOL_OR_INT 3 -#define TYPE_PATH 4 -#define TYPE_EXPIRY_DATE 5 -#define TYPE_COLOR 6 -#define TYPE_BOOL_OR_STR 7 - -#define OPT_CALLBACK_VALUE(s, l, v, h, i) \ - { OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \ - PARSE_OPT_NONEG, option_parse_type, (i) } - -static NORETURN void usage_builtin_config(void); - -static int option_parse_type(const struct option *opt, const char *arg, - int unset) -{ - int new_type, *to_type; - - if (unset) { - *((int *) opt->value) = 0; - return 0; - } - - /* - * To support '--<type>' style flags, begin with new_type equal to - * opt->defval. - */ - new_type = opt->defval; - if (!new_type) { - if (!strcmp(arg, "bool")) - new_type = TYPE_BOOL; - else if (!strcmp(arg, "int")) - new_type = TYPE_INT; - else if (!strcmp(arg, "bool-or-int")) - new_type = TYPE_BOOL_OR_INT; - else if (!strcmp(arg, "bool-or-str")) - new_type = TYPE_BOOL_OR_STR; - else if (!strcmp(arg, "path")) - new_type = TYPE_PATH; - else if (!strcmp(arg, "expiry-date")) - new_type = TYPE_EXPIRY_DATE; - else if (!strcmp(arg, "color")) - new_type = TYPE_COLOR; - else - die(_("unrecognized --type argument, %s"), arg); - } - - to_type = opt->value; - if (*to_type && *to_type != new_type) { - /* - * Complain when there is a new type not equal to the old type. - * This allows for combinations like '--int --type=int' and - * '--type=int --type=int', but disallows ones like '--type=bool - * --int' and '--type=bool - * --type=int'. - */ - error(_("only one type at a time")); - usage_builtin_config(); - } - *to_type = new_type; - - return 0; -} - -static struct option builtin_config_options[] = { - OPT_GROUP(N_("Config file location")), - OPT_BOOL(0, "global", &use_global_config, N_("use global config file")), - OPT_BOOL(0, "system", &use_system_config, N_("use system config file")), - OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")), - OPT_BOOL(0, "worktree", &use_worktree_config, N_("use per-worktree config file")), - OPT_STRING('f', "file", &given_config_source.file, N_("file"), N_("use given config file")), - OPT_STRING(0, "blob", &given_config_source.blob, N_("blob-id"), N_("read config from given blob object")), - OPT_GROUP(N_("Action")), - OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET), - OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL), - OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-regex]"), ACTION_GET_REGEXP), - OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH), - OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value_regex]"), ACTION_REPLACE_ALL), - OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD), - OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-regex]"), ACTION_UNSET), - OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-regex]"), ACTION_UNSET_ALL), - OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION), - OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION), - OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST), - OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT), - OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR), - OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL), - OPT_GROUP(N_("Type")), - OPT_CALLBACK('t', "type", &type, "", N_("value is given this type"), option_parse_type), - OPT_CALLBACK_VALUE(0, "bool", &type, N_("value is \"true\" or \"false\""), TYPE_BOOL), - OPT_CALLBACK_VALUE(0, "int", &type, N_("value is decimal number"), TYPE_INT), - OPT_CALLBACK_VALUE(0, "bool-or-int", &type, N_("value is --bool or --int"), TYPE_BOOL_OR_INT), - OPT_CALLBACK_VALUE(0, "bool-or-str", &type, N_("value is --bool or string"), TYPE_BOOL_OR_STR), - OPT_CALLBACK_VALUE(0, "path", &type, N_("value is a path (file or directory name)"), TYPE_PATH), - OPT_CALLBACK_VALUE(0, "expiry-date", &type, N_("value is an expiry date"), TYPE_EXPIRY_DATE), - OPT_GROUP(N_("Other")), - OPT_BOOL('z', "null", &end_nul, N_("terminate values with NUL byte")), - OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")), - OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")), - OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")), - OPT_BOOL(0, "show-scope", &show_scope, N_("show scope of config (worktree, local, global, system, command)")), - OPT_STRING(0, "default", &default_value, N_("value"), N_("with --get, use default value when missing entry")), - OPT_END(), -}; - -static NORETURN void usage_builtin_config(void) -{ - usage_with_options(builtin_config_usage, builtin_config_options); -} - -static void check_argc(int argc, int min, int max) -{ - if (argc >= min && argc <= max) - return; - if (min == max) - error(_("wrong number of arguments, should be %d"), min); - else - error(_("wrong number of arguments, should be from %d to %d"), - min, max); - usage_builtin_config(); -} - -static void show_config_origin(struct strbuf *buf) -{ - const char term = end_nul ? '\0' : '\t'; - - strbuf_addstr(buf, current_config_origin_type()); - strbuf_addch(buf, ':'); - if (end_nul) - strbuf_addstr(buf, current_config_name()); - else - quote_c_style(current_config_name(), buf, NULL, 0); - strbuf_addch(buf, term); -} - -static void show_config_scope(struct strbuf *buf) -{ - const char term = end_nul ? '\0' : '\t'; - const char *scope = config_scope_name(current_config_scope()); - - strbuf_addstr(buf, N_(scope)); - strbuf_addch(buf, term); -} - -static int show_all_config(const char *key_, const char *value_, void *cb) -{ - if (show_origin || show_scope) { - struct strbuf buf = STRBUF_INIT; - if (show_scope) - show_config_scope(&buf); - if (show_origin) - show_config_origin(&buf); - /* Use fwrite as "buf" can contain \0's if "end_null" is set. */ - fwrite(buf.buf, 1, buf.len, stdout); - strbuf_release(&buf); - } - if (!omit_values && value_) - printf("%s%c%s%c", key_, delim, value_, term); - else - printf("%s%c", key_, term); - return 0; -} - -struct strbuf_list { - struct strbuf *items; - int nr; - int alloc; -}; - -static int format_config(struct strbuf *buf, const char *key_, const char *value_) -{ - if (show_scope) - show_config_scope(buf); - if (show_origin) - show_config_origin(buf); - if (show_keys) - strbuf_addstr(buf, key_); - if (!omit_values) { - if (show_keys) - strbuf_addch(buf, key_delim); - - if (type == TYPE_INT) - strbuf_addf(buf, "%"PRId64, - git_config_int64(key_, value_ ? value_ : "")); - else if (type == TYPE_BOOL) - strbuf_addstr(buf, git_config_bool(key_, value_) ? - "true" : "false"); - else if (type == TYPE_BOOL_OR_INT) { - int is_bool, v; - v = git_config_bool_or_int(key_, value_, &is_bool); - if (is_bool) - strbuf_addstr(buf, v ? "true" : "false"); - else - strbuf_addf(buf, "%d", v); - } else if (type == TYPE_BOOL_OR_STR) { - int v = git_parse_maybe_bool(value_); - if (v < 0) - strbuf_addstr(buf, value_); - else - strbuf_addstr(buf, v ? "true" : "false"); - } else if (type == TYPE_PATH) { - const char *v; - if (git_config_pathname(&v, key_, value_) < 0) - return -1; - strbuf_addstr(buf, v); - free((char *)v); - } else if (type == TYPE_EXPIRY_DATE) { - timestamp_t t; - if (git_config_expiry_date(&t, key_, value_) < 0) - return -1; - strbuf_addf(buf, "%"PRItime, t); - } else if (type == TYPE_COLOR) { - char v[COLOR_MAXLEN]; - if (git_config_color(v, key_, value_) < 0) - return -1; - strbuf_addstr(buf, v); - } else if (value_) { - strbuf_addstr(buf, value_); - } else { - /* Just show the key name; back out delimiter */ - if (show_keys) - strbuf_setlen(buf, buf->len - 1); - } - } - strbuf_addch(buf, term); - return 0; -} - -static int collect_config(const char *key_, const char *value_, void *cb) -{ - struct strbuf_list *values = cb; - - if (!use_key_regexp && strcmp(key_, key)) - return 0; - if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0)) - return 0; - if (regexp != NULL && - (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0))) - return 0; - - ALLOC_GROW(values->items, values->nr + 1, values->alloc); - strbuf_init(&values->items[values->nr], 0); - - return format_config(&values->items[values->nr++], key_, value_); -} - -static int get_value(const char *key_, const char *regex_) -{ - int ret = CONFIG_GENERIC_ERROR; - struct strbuf_list values = {NULL}; - int i; - - if (use_key_regexp) { - char *tl; - - /* - * NEEDSWORK: this naive pattern lowercasing obviously does not - * work for more complex patterns like "^[^.]*Foo.*bar". - * Perhaps we should deprecate this altogether someday. - */ - - key = xstrdup(key_); - for (tl = key + strlen(key) - 1; - tl >= key && *tl != '.'; - tl--) - *tl = tolower(*tl); - for (tl = key; *tl && *tl != '.'; tl++) - *tl = tolower(*tl); - - key_regexp = (regex_t*)xmalloc(sizeof(regex_t)); - if (regcomp(key_regexp, key, REG_EXTENDED)) { - error(_("invalid key pattern: %s"), key_); - FREE_AND_NULL(key_regexp); - ret = CONFIG_INVALID_PATTERN; - goto free_strings; - } - } else { - if (git_config_parse_key(key_, &key, NULL)) { - ret = CONFIG_INVALID_KEY; - goto free_strings; - } - } - - if (regex_) { - if (regex_[0] == '!') { - do_not_match = 1; - regex_++; - } - - regexp = (regex_t*)xmalloc(sizeof(regex_t)); - if (regcomp(regexp, regex_, REG_EXTENDED)) { - error(_("invalid pattern: %s"), regex_); - FREE_AND_NULL(regexp); - ret = CONFIG_INVALID_PATTERN; - goto free_strings; - } - } - - config_with_options(collect_config, &values, - &given_config_source, &config_options); - - if (!values.nr && default_value) { - struct strbuf *item; - ALLOC_GROW(values.items, values.nr + 1, values.alloc); - item = &values.items[values.nr++]; - strbuf_init(item, 0); - if (format_config(item, key_, default_value) < 0) - die(_("failed to format default config value: %s"), - default_value); - } - - ret = !values.nr; - - for (i = 0; i < values.nr; i++) { - struct strbuf *buf = values.items + i; - if (do_all || i == values.nr - 1) - fwrite(buf->buf, 1, buf->len, stdout); - strbuf_release(buf); - } - free(values.items); - -free_strings: - free(key); - if (key_regexp) { - regfree(key_regexp); - free(key_regexp); - } - if (regexp) { - regfree(regexp); - free(regexp); - } - - return ret; -} - -static char *normalize_value(const char *key, const char *value) -{ - if (!value) - return NULL; - - if (type == 0 || type == TYPE_PATH || type == TYPE_EXPIRY_DATE) - /* - * We don't do normalization for TYPE_PATH here: If - * the path is like ~/foobar/, we prefer to store - * "~/foobar/" in the config file, and to expand the ~ - * when retrieving the value. - * Also don't do normalization for expiry dates. - */ - return xstrdup(value); - if (type == TYPE_INT) - return xstrfmt("%"PRId64, git_config_int64(key, value)); - if (type == TYPE_BOOL) - return xstrdup(git_config_bool(key, value) ? "true" : "false"); - if (type == TYPE_BOOL_OR_INT) { - int is_bool, v; - v = git_config_bool_or_int(key, value, &is_bool); - if (!is_bool) - return xstrfmt("%d", v); - else - return xstrdup(v ? "true" : "false"); - } - if (type == TYPE_BOOL_OR_STR) { - int v = git_parse_maybe_bool(value); - if (v < 0) - return xstrdup(value); - else - return xstrdup(v ? "true" : "false"); - } - if (type == TYPE_COLOR) { - char v[COLOR_MAXLEN]; - if (git_config_color(v, key, value)) - die(_("cannot parse color '%s'"), value); - - /* - * The contents of `v` now contain an ANSI escape - * sequence, not suitable for including within a - * configuration file. Treat the above as a - * "sanity-check", and return the given value, which we - * know is representable as valid color code. - */ - return xstrdup(value); - } - - BUG("cannot normalize type %d", type); -} - -static int get_color_found; -static const char *get_color_slot; -static const char *get_colorbool_slot; -static char parsed_color[COLOR_MAXLEN]; - -static int git_get_color_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, get_color_slot)) { - if (!value) - config_error_nonbool(var); - if (color_parse(value, parsed_color) < 0) - return -1; - get_color_found = 1; - } - return 0; -} - -static void get_color(const char *var, const char *def_color) -{ - get_color_slot = var; - get_color_found = 0; - parsed_color[0] = '\0'; - config_with_options(git_get_color_config, NULL, - &given_config_source, &config_options); - - if (!get_color_found && def_color) { - if (color_parse(def_color, parsed_color) < 0) - die(_("unable to parse default color value")); - } - - fputs(parsed_color, stdout); -} - -static int get_colorbool_found; -static int get_diff_color_found; -static int get_color_ui_found; -static int git_get_colorbool_config(const char *var, const char *value, - void *cb) -{ - if (!strcmp(var, get_colorbool_slot)) - get_colorbool_found = git_config_colorbool(var, value); - else if (!strcmp(var, "diff.color")) - get_diff_color_found = git_config_colorbool(var, value); - else if (!strcmp(var, "color.ui")) - get_color_ui_found = git_config_colorbool(var, value); - return 0; -} - -static int get_colorbool(const char *var, int print) -{ - get_colorbool_slot = var; - get_colorbool_found = -1; - get_diff_color_found = -1; - get_color_ui_found = -1; - config_with_options(git_get_colorbool_config, NULL, - &given_config_source, &config_options); - - if (get_colorbool_found < 0) { - if (!strcmp(get_colorbool_slot, "color.diff")) - get_colorbool_found = get_diff_color_found; - if (get_colorbool_found < 0) - get_colorbool_found = get_color_ui_found; - } - - if (get_colorbool_found < 0) - /* default value if none found in config */ - get_colorbool_found = GIT_COLOR_AUTO; - - get_colorbool_found = want_color(get_colorbool_found); - - if (print) { - printf("%s\n", get_colorbool_found ? "true" : "false"); - return 0; - } else - return get_colorbool_found ? 0 : 1; -} - -static void check_write(void) -{ - if (!given_config_source.file && !startup_info->have_repository) - die(_("not in a git directory")); - - if (given_config_source.use_stdin) - die(_("writing to stdin is not supported")); - - if (given_config_source.blob) - die(_("writing config blobs is not supported")); -} - -struct urlmatch_current_candidate_value { - char value_is_null; - struct strbuf value; -}; - -static int urlmatch_collect_fn(const char *var, const char *value, void *cb) -{ - struct string_list *values = cb; - struct string_list_item *item = string_list_insert(values, var); - struct urlmatch_current_candidate_value *matched = item->util; - - if (!matched) { - matched = xmalloc(sizeof(*matched)); - strbuf_init(&matched->value, 0); - item->util = matched; - } else { - strbuf_reset(&matched->value); - } - - if (value) { - strbuf_addstr(&matched->value, value); - matched->value_is_null = 0; - } else { - matched->value_is_null = 1; - } - return 0; -} - -static int get_urlmatch(const char *var, const char *url) -{ - int ret; - char *section_tail; - struct string_list_item *item; - struct urlmatch_config config = { STRING_LIST_INIT_DUP }; - struct string_list values = STRING_LIST_INIT_DUP; - - config.collect_fn = urlmatch_collect_fn; - config.cascade_fn = NULL; - config.cb = &values; - - if (!url_normalize(url, &config.url)) - die("%s", config.url.err); - - config.section = xstrdup_tolower(var); - section_tail = strchr(config.section, '.'); - if (section_tail) { - *section_tail = '\0'; - config.key = section_tail + 1; - show_keys = 0; - } else { - config.key = NULL; - show_keys = 1; - } - - config_with_options(urlmatch_config_entry, &config, - &given_config_source, &config_options); - - ret = !values.nr; - - for_each_string_list_item(item, &values) { - struct urlmatch_current_candidate_value *matched = item->util; - struct strbuf buf = STRBUF_INIT; - - format_config(&buf, item->string, - matched->value_is_null ? NULL : matched->value.buf); - fwrite(buf.buf, 1, buf.len, stdout); - strbuf_release(&buf); - - strbuf_release(&matched->value); - } - string_list_clear(&config.vars, 1); - string_list_clear(&values, 1); - free(config.url.url); - - free((void *)config.section); - return ret; -} - -static char *default_user_config(void) -{ - struct strbuf buf = STRBUF_INIT; - strbuf_addf(&buf, - _("# This is Git's per-user configuration file.\n" - "[user]\n" - "# Please adapt and uncomment the following lines:\n" - "# name = %s\n" - "# email = %s\n"), - ident_default_name(), - ident_default_email()); - return strbuf_detach(&buf, NULL); -} - -int cmd_config(int argc, const char **argv, const char *prefix) -{ - int nongit = !startup_info->have_repository; - char *value; - - given_config_source.file = xstrdup_or_null(getenv(CONFIG_ENVIRONMENT)); - - argc = parse_options(argc, argv, prefix, builtin_config_options, - builtin_config_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - - if (use_global_config + use_system_config + use_local_config + - use_worktree_config + - !!given_config_source.file + !!given_config_source.blob > 1) { - error(_("only one config file at a time")); - usage_builtin_config(); - } - - if (nongit) { - if (use_local_config) - die(_("--local can only be used inside a git repository")); - if (given_config_source.blob) - die(_("--blob can only be used inside a git repository")); - if (use_worktree_config) - die(_("--worktree can only be used inside a git repository")); - - } - - if (given_config_source.file && - !strcmp(given_config_source.file, "-")) { - given_config_source.file = NULL; - given_config_source.use_stdin = 1; - given_config_source.scope = CONFIG_SCOPE_COMMAND; - } - - if (use_global_config) { - char *user_config = expand_user_path("~/.gitconfig", 0); - char *xdg_config = xdg_config_home("config"); - - if (!user_config) - /* - * It is unknown if HOME/.gitconfig exists, so - * we do not know if we should write to XDG - * location; error out even if XDG_CONFIG_HOME - * is set and points at a sane location. - */ - die(_("$HOME not set")); - - given_config_source.scope = CONFIG_SCOPE_GLOBAL; - - if (access_or_warn(user_config, R_OK, 0) && - xdg_config && !access_or_warn(xdg_config, R_OK, 0)) { - given_config_source.file = xdg_config; - free(user_config); - } else { - given_config_source.file = user_config; - free(xdg_config); - } - } - else if (use_system_config) { - given_config_source.file = git_etc_gitconfig(); - given_config_source.scope = CONFIG_SCOPE_SYSTEM; - } else if (use_local_config) { - given_config_source.file = git_pathdup("config"); - given_config_source.scope = CONFIG_SCOPE_LOCAL; - } else if (use_worktree_config) { - struct worktree **worktrees = get_worktrees(); - if (repository_format_worktree_config) - given_config_source.file = git_pathdup("config.worktree"); - else if (worktrees[0] && worktrees[1]) - die(_("--worktree cannot be used with multiple " - "working trees unless the config\n" - "extension worktreeConfig is enabled. " - "Please read \"CONFIGURATION FILE\"\n" - "section in \"git help worktree\" for details")); - else - given_config_source.file = git_pathdup("config"); - given_config_source.scope = CONFIG_SCOPE_LOCAL; - free_worktrees(worktrees); - } else if (given_config_source.file) { - if (!is_absolute_path(given_config_source.file) && prefix) - given_config_source.file = - prefix_filename(prefix, given_config_source.file); - given_config_source.scope = CONFIG_SCOPE_COMMAND; - } else if (given_config_source.blob) { - given_config_source.scope = CONFIG_SCOPE_COMMAND; - } - - - if (respect_includes_opt == -1) - config_options.respect_includes = !given_config_source.file; - else - config_options.respect_includes = respect_includes_opt; - if (!nongit) { - config_options.commondir = get_git_common_dir(); - config_options.git_dir = get_git_dir(); - } - - if (end_nul) { - term = '\0'; - delim = '\n'; - key_delim = '\n'; - } - - if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) { - error(_("--get-color and variable type are incoherent")); - usage_builtin_config(); - } - - if (HAS_MULTI_BITS(actions)) { - error(_("only one action at a time")); - usage_builtin_config(); - } - if (actions == 0) - switch (argc) { - case 1: actions = ACTION_GET; break; - case 2: actions = ACTION_SET; break; - case 3: actions = ACTION_SET_ALL; break; - default: - usage_builtin_config(); - } - if (omit_values && - !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) { - error(_("--name-only is only applicable to --list or --get-regexp")); - usage_builtin_config(); - } - - if (show_origin && !(actions & - (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) { - error(_("--show-origin is only applicable to --get, --get-all, " - "--get-regexp, and --list")); - usage_builtin_config(); - } - - if (default_value && !(actions & ACTION_GET)) { - error(_("--default is only applicable to --get")); - usage_builtin_config(); - } - - if (actions & PAGING_ACTIONS) - setup_auto_pager("config", 1); - - if (actions == ACTION_LIST) { - check_argc(argc, 0, 0); - if (config_with_options(show_all_config, NULL, - &given_config_source, - &config_options) < 0) { - if (given_config_source.file) - die_errno(_("unable to read config file '%s'"), - given_config_source.file); - else - die(_("error processing config file(s)")); - } - } - else if (actions == ACTION_EDIT) { - char *config_file; - - check_argc(argc, 0, 0); - if (!given_config_source.file && nongit) - die(_("not in a git directory")); - if (given_config_source.use_stdin) - die(_("editing stdin is not supported")); - if (given_config_source.blob) - die(_("editing blobs is not supported")); - git_config(git_default_config, NULL); - config_file = given_config_source.file ? - xstrdup(given_config_source.file) : - git_pathdup("config"); - if (use_global_config) { - int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666); - if (fd >= 0) { - char *content = default_user_config(); - write_str_in_full(fd, content); - free(content); - close(fd); - } - else if (errno != EEXIST) - die_errno(_("cannot create configuration file %s"), config_file); - } - launch_editor(config_file, NULL, NULL); - free(config_file); - } - else if (actions == ACTION_SET) { - int ret; - check_write(); - check_argc(argc, 2, 2); - value = normalize_value(argv[0], argv[1]); - UNLEAK(value); - ret = git_config_set_in_file_gently(given_config_source.file, argv[0], value); - if (ret == CONFIG_NOTHING_SET) - error(_("cannot overwrite multiple values with a single value\n" - " Use a regexp, --add or --replace-all to change %s."), argv[0]); - return ret; - } - else if (actions == ACTION_SET_ALL) { - check_write(); - check_argc(argc, 2, 3); - value = normalize_value(argv[0], argv[1]); - UNLEAK(value); - return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], value, argv[2], 0); - } - else if (actions == ACTION_ADD) { - check_write(); - check_argc(argc, 2, 2); - value = normalize_value(argv[0], argv[1]); - UNLEAK(value); - return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], value, - CONFIG_REGEX_NONE, 0); - } - else if (actions == ACTION_REPLACE_ALL) { - check_write(); - check_argc(argc, 2, 3); - value = normalize_value(argv[0], argv[1]); - UNLEAK(value); - return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], value, argv[2], 1); - } - else if (actions == ACTION_GET) { - check_argc(argc, 1, 2); - return get_value(argv[0], argv[1]); - } - else if (actions == ACTION_GET_ALL) { - do_all = 1; - check_argc(argc, 1, 2); - return get_value(argv[0], argv[1]); - } - else if (actions == ACTION_GET_REGEXP) { - show_keys = 1; - use_key_regexp = 1; - do_all = 1; - check_argc(argc, 1, 2); - return get_value(argv[0], argv[1]); - } - else if (actions == ACTION_GET_URLMATCH) { - check_argc(argc, 2, 2); - return get_urlmatch(argv[0], argv[1]); - } - else if (actions == ACTION_UNSET) { - check_write(); - check_argc(argc, 1, 2); - if (argc == 2) - return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], NULL, argv[1], 0); - else - return git_config_set_in_file_gently(given_config_source.file, - argv[0], NULL); - } - else if (actions == ACTION_UNSET_ALL) { - check_write(); - check_argc(argc, 1, 2); - return git_config_set_multivar_in_file_gently(given_config_source.file, - argv[0], NULL, argv[1], 1); - } - else if (actions == ACTION_RENAME_SECTION) { - int ret; - check_write(); - check_argc(argc, 2, 2); - ret = git_config_rename_section_in_file(given_config_source.file, - argv[0], argv[1]); - if (ret < 0) - return ret; - if (ret == 0) - die(_("no such section: %s"), argv[0]); - } - else if (actions == ACTION_REMOVE_SECTION) { - int ret; - check_write(); - check_argc(argc, 1, 1); - ret = git_config_rename_section_in_file(given_config_source.file, - argv[0], NULL); - if (ret < 0) - return ret; - if (ret == 0) - die(_("no such section: %s"), argv[0]); - } - else if (actions == ACTION_GET_COLOR) { - check_argc(argc, 1, 2); - get_color(argv[0], argv[1]); - } - else if (actions == ACTION_GET_COLORBOOL) { - check_argc(argc, 1, 2); - if (argc == 2) - color_stdout_is_tty = git_config_bool("command line", argv[1]); - return get_colorbool(argv[0], argc == 2); - } - - return 0; -} diff --git a/third_party/git/builtin/count-objects.c b/third_party/git/builtin/count-objects.c deleted file mode 100644 index 3fae474f6f1f..000000000000 --- a/third_party/git/builtin/count-objects.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Builtin "git count-objects". - * - * Copyright (c) 2006 Junio C Hamano - */ - -#include "cache.h" -#include "config.h" -#include "dir.h" -#include "repository.h" -#include "builtin.h" -#include "parse-options.h" -#include "quote.h" -#include "packfile.h" -#include "object-store.h" - -static unsigned long garbage; -static off_t size_garbage; -static int verbose; -static unsigned long loose, packed, packed_loose; -static off_t loose_size; - -static const char *bits_to_msg(unsigned seen_bits) -{ - switch (seen_bits) { - case 0: - return "no corresponding .idx or .pack"; - case PACKDIR_FILE_GARBAGE: - return "garbage found"; - case PACKDIR_FILE_PACK: - return "no corresponding .idx"; - case PACKDIR_FILE_IDX: - return "no corresponding .pack"; - case PACKDIR_FILE_PACK|PACKDIR_FILE_IDX: - default: - return NULL; - } -} - -static void real_report_garbage(unsigned seen_bits, const char *path) -{ - struct stat st; - const char *desc = bits_to_msg(seen_bits); - - if (!desc) - return; - - if (!stat(path, &st)) - size_garbage += st.st_size; - warning("%s: %s", desc, path); - garbage++; -} - -static void loose_garbage(const char *path) -{ - if (verbose) - report_garbage(PACKDIR_FILE_GARBAGE, path); -} - -static int count_loose(const struct object_id *oid, const char *path, void *data) -{ - struct stat st; - - if (lstat(path, &st) || !S_ISREG(st.st_mode)) - loose_garbage(path); - else { - loose_size += on_disk_bytes(st); - loose++; - if (verbose && has_object_pack(oid)) - packed_loose++; - } - return 0; -} - -static int count_cruft(const char *basename, const char *path, void *data) -{ - loose_garbage(path); - return 0; -} - -static int print_alternate(struct object_directory *odb, void *data) -{ - printf("alternate: "); - quote_c_style(odb->path, NULL, stdout, 0); - putchar('\n'); - return 0; -} - -static char const * const count_objects_usage[] = { - N_("git count-objects [-v] [-H | --human-readable]"), - NULL -}; - -int cmd_count_objects(int argc, const char **argv, const char *prefix) -{ - int human_readable = 0; - struct option opts[] = { - OPT__VERBOSE(&verbose, N_("be verbose")), - OPT_BOOL('H', "human-readable", &human_readable, - N_("print sizes in human readable format")), - OPT_END(), - }; - - git_config(git_default_config, NULL); - - argc = parse_options(argc, argv, prefix, opts, count_objects_usage, 0); - /* we do not take arguments other than flags for now */ - if (argc) - usage_with_options(count_objects_usage, opts); - if (verbose) { - report_garbage = real_report_garbage; - report_linked_checkout_garbage(); - } - - for_each_loose_file_in_objdir(get_object_directory(), - count_loose, count_cruft, NULL, NULL); - - if (verbose) { - struct packed_git *p; - unsigned long num_pack = 0; - off_t size_pack = 0; - struct strbuf loose_buf = STRBUF_INIT; - struct strbuf pack_buf = STRBUF_INIT; - struct strbuf garbage_buf = STRBUF_INIT; - - for (p = get_all_packs(the_repository); p; p = p->next) { - if (!p->pack_local) - continue; - if (open_pack_index(p)) - continue; - packed += p->num_objects; - size_pack += p->pack_size + p->index_size; - num_pack++; - } - - if (human_readable) { - strbuf_humanise_bytes(&loose_buf, loose_size); - strbuf_humanise_bytes(&pack_buf, size_pack); - strbuf_humanise_bytes(&garbage_buf, size_garbage); - } else { - strbuf_addf(&loose_buf, "%lu", - (unsigned long)(loose_size / 1024)); - strbuf_addf(&pack_buf, "%lu", - (unsigned long)(size_pack / 1024)); - strbuf_addf(&garbage_buf, "%lu", - (unsigned long)(size_garbage / 1024)); - } - - printf("count: %lu\n", loose); - printf("size: %s\n", loose_buf.buf); - printf("in-pack: %lu\n", packed); - printf("packs: %lu\n", num_pack); - printf("size-pack: %s\n", pack_buf.buf); - printf("prune-packable: %lu\n", packed_loose); - printf("garbage: %lu\n", garbage); - printf("size-garbage: %s\n", garbage_buf.buf); - foreach_alt_odb(print_alternate, NULL); - strbuf_release(&loose_buf); - strbuf_release(&pack_buf); - strbuf_release(&garbage_buf); - } else { - struct strbuf buf = STRBUF_INIT; - if (human_readable) - strbuf_humanise_bytes(&buf, loose_size); - else - strbuf_addf(&buf, "%lu kilobytes", - (unsigned long)(loose_size / 1024)); - printf("%lu objects, %s\n", loose, buf.buf); - strbuf_release(&buf); - } - return 0; -} diff --git a/third_party/git/builtin/credential-cache--daemon.c b/third_party/git/builtin/credential-cache--daemon.c deleted file mode 100644 index c61f123a3b81..000000000000 --- a/third_party/git/builtin/credential-cache--daemon.c +++ /dev/null @@ -1,318 +0,0 @@ -#include "builtin.h" -#include "parse-options.h" - -#ifndef NO_UNIX_SOCKETS - -#include "config.h" -#include "tempfile.h" -#include "credential.h" -#include "unix-socket.h" - -struct credential_cache_entry { - struct credential item; - timestamp_t expiration; -}; -static struct credential_cache_entry *entries; -static int entries_nr; -static int entries_alloc; - -static void cache_credential(struct credential *c, int timeout) -{ - struct credential_cache_entry *e; - - ALLOC_GROW(entries, entries_nr + 1, entries_alloc); - e = &entries[entries_nr++]; - - /* take ownership of pointers */ - memcpy(&e->item, c, sizeof(*c)); - memset(c, 0, sizeof(*c)); - e->expiration = time(NULL) + timeout; -} - -static struct credential_cache_entry *lookup_credential(const struct credential *c) -{ - int i; - for (i = 0; i < entries_nr; i++) { - struct credential *e = &entries[i].item; - if (credential_match(c, e)) - return &entries[i]; - } - return NULL; -} - -static void remove_credential(const struct credential *c) -{ - struct credential_cache_entry *e; - - e = lookup_credential(c); - if (e) - e->expiration = 0; -} - -static timestamp_t check_expirations(void) -{ - static timestamp_t wait_for_entry_until; - int i = 0; - timestamp_t now = time(NULL); - timestamp_t next = TIME_MAX; - - /* - * Initially give the client 30 seconds to actually contact us - * and store a credential before we decide there's no point in - * keeping the daemon around. - */ - if (!wait_for_entry_until) - wait_for_entry_until = now + 30; - - while (i < entries_nr) { - if (entries[i].expiration <= now) { - entries_nr--; - credential_clear(&entries[i].item); - if (i != entries_nr) - memcpy(&entries[i], &entries[entries_nr], sizeof(*entries)); - /* - * Stick around 30 seconds in case a new credential - * shows up (e.g., because we just removed a failed - * one, and we will soon get the correct one). - */ - wait_for_entry_until = now + 30; - } - else { - if (entries[i].expiration < next) - next = entries[i].expiration; - i++; - } - } - - if (!entries_nr) { - if (wait_for_entry_until <= now) - return 0; - next = wait_for_entry_until; - } - - return next - now; -} - -static int read_request(FILE *fh, struct credential *c, - struct strbuf *action, int *timeout) -{ - static struct strbuf item = STRBUF_INIT; - const char *p; - - strbuf_getline_lf(&item, fh); - if (!skip_prefix(item.buf, "action=", &p)) - return error("client sent bogus action line: %s", item.buf); - strbuf_addstr(action, p); - - strbuf_getline_lf(&item, fh); - if (!skip_prefix(item.buf, "timeout=", &p)) - return error("client sent bogus timeout line: %s", item.buf); - *timeout = atoi(p); - - if (credential_read(c, fh) < 0) - return -1; - return 0; -} - -static void serve_one_client(FILE *in, FILE *out) -{ - struct credential c = CREDENTIAL_INIT; - struct strbuf action = STRBUF_INIT; - int timeout = -1; - - if (read_request(in, &c, &action, &timeout) < 0) - /* ignore error */ ; - else if (!strcmp(action.buf, "get")) { - struct credential_cache_entry *e = lookup_credential(&c); - if (e) { - fprintf(out, "username=%s\n", e->item.username); - fprintf(out, "password=%s\n", e->item.password); - } - } - else if (!strcmp(action.buf, "exit")) { - /* - * It's important that we clean up our socket first, and then - * signal the client only once we have finished the cleanup. - * Calling exit() directly does this, because we clean up in - * our atexit() handler, and then signal the client when our - * process actually ends, which closes the socket and gives - * them EOF. - */ - exit(0); - } - else if (!strcmp(action.buf, "erase")) - remove_credential(&c); - else if (!strcmp(action.buf, "store")) { - if (timeout < 0) - warning("cache client didn't specify a timeout"); - else if (!c.username || !c.password) - warning("cache client gave us a partial credential"); - else { - remove_credential(&c); - cache_credential(&c, timeout); - } - } - else - warning("cache client sent unknown action: %s", action.buf); - - credential_clear(&c); - strbuf_release(&action); -} - -static int serve_cache_loop(int fd) -{ - struct pollfd pfd; - timestamp_t wakeup; - - wakeup = check_expirations(); - if (!wakeup) - return 0; - - pfd.fd = fd; - pfd.events = POLLIN; - if (poll(&pfd, 1, 1000 * wakeup) < 0) { - if (errno != EINTR) - die_errno("poll failed"); - return 1; - } - - if (pfd.revents & POLLIN) { - int client, client2; - FILE *in, *out; - - client = accept(fd, NULL, NULL); - if (client < 0) { - warning_errno("accept failed"); - return 1; - } - client2 = dup(client); - if (client2 < 0) { - warning_errno("dup failed"); - close(client); - return 1; - } - - in = xfdopen(client, "r"); - out = xfdopen(client2, "w"); - serve_one_client(in, out); - fclose(in); - fclose(out); - } - return 1; -} - -static void serve_cache(const char *socket_path, int debug) -{ - int fd; - - fd = unix_stream_listen(socket_path); - if (fd < 0) - die_errno("unable to bind to '%s'", socket_path); - - printf("ok\n"); - fclose(stdout); - if (!debug) { - if (!freopen("/dev/null", "w", stderr)) - die_errno("unable to point stderr to /dev/null"); - } - - while (serve_cache_loop(fd)) - ; /* nothing */ - - close(fd); -} - -static const char permissions_advice[] = N_( -"The permissions on your socket directory are too loose; other\n" -"users may be able to read your cached credentials. Consider running:\n" -"\n" -" chmod 0700 %s"); -static void init_socket_directory(const char *path) -{ - struct stat st; - char *path_copy = xstrdup(path); - char *dir = dirname(path_copy); - - if (!stat(dir, &st)) { - if (st.st_mode & 077) - die(_(permissions_advice), dir); - } else { - /* - * We must be sure to create the directory with the correct mode, - * not just chmod it after the fact; otherwise, there is a race - * condition in which somebody can chdir to it, sleep, then try to open - * our protected socket. - */ - if (safe_create_leading_directories_const(dir) < 0) - die_errno("unable to create directories for '%s'", dir); - if (mkdir(dir, 0700) < 0) - die_errno("unable to mkdir '%s'", dir); - } - - if (chdir(dir)) - /* - * We don't actually care what our cwd is; we chdir here just to - * be a friendly daemon and avoid tying up our original cwd. - * If this fails, it's OK to just continue without that benefit. - */ - ; - - free(path_copy); -} - -int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix) -{ - struct tempfile *socket_file; - const char *socket_path; - int ignore_sighup = 0; - static const char *usage[] = { - "git-credential-cache--daemon [opts] <socket_path>", - NULL - }; - int debug = 0; - const struct option options[] = { - OPT_BOOL(0, "debug", &debug, - N_("print debugging messages to stderr")), - OPT_END() - }; - - git_config_get_bool("credentialcache.ignoresighup", &ignore_sighup); - - argc = parse_options(argc, argv, prefix, options, usage, 0); - socket_path = argv[0]; - - if (!socket_path) - usage_with_options(usage, options); - - if (!is_absolute_path(socket_path)) - die("socket directory must be an absolute path"); - - init_socket_directory(socket_path); - socket_file = register_tempfile(socket_path); - - if (ignore_sighup) - signal(SIGHUP, SIG_IGN); - - serve_cache(socket_path, debug); - delete_tempfile(&socket_file); - - return 0; -} - -#else - -int cmd_credential_cache_daemon(int argc, const char **argv, const char *prefix) -{ - const char * const usage[] = { - "git credential-cache--daemon [options] <action>", - "", - "credential-cache--daemon is disabled in this build of Git", - NULL - }; - struct option options[] = { OPT_END() }; - - argc = parse_options(argc, argv, prefix, options, usage, 0); - die(_("credential-cache--daemon unavailable; no unix socket support")); -} - -#endif /* NO_UNIX_SOCKET */ diff --git a/third_party/git/builtin/credential-cache.c b/third_party/git/builtin/credential-cache.c deleted file mode 100644 index 9b3f70990597..000000000000 --- a/third_party/git/builtin/credential-cache.c +++ /dev/null @@ -1,157 +0,0 @@ -#include "builtin.h" -#include "parse-options.h" - -#ifndef NO_UNIX_SOCKETS - -#include "credential.h" -#include "string-list.h" -#include "unix-socket.h" -#include "run-command.h" - -#define FLAG_SPAWN 0x1 -#define FLAG_RELAY 0x2 - -static int send_request(const char *socket, const struct strbuf *out) -{ - int got_data = 0; - int fd = unix_stream_connect(socket); - - if (fd < 0) - return -1; - - if (write_in_full(fd, out->buf, out->len) < 0) - die_errno("unable to write to cache daemon"); - shutdown(fd, SHUT_WR); - - while (1) { - char in[1024]; - int r; - - r = read_in_full(fd, in, sizeof(in)); - if (r == 0 || (r < 0 && errno == ECONNRESET)) - break; - if (r < 0) - die_errno("read error from cache daemon"); - write_or_die(1, in, r); - got_data = 1; - } - close(fd); - return got_data; -} - -static void spawn_daemon(const char *socket) -{ - struct child_process daemon = CHILD_PROCESS_INIT; - char buf[128]; - int r; - - strvec_pushl(&daemon.args, - "credential-cache--daemon", socket, - NULL); - daemon.git_cmd = 1; - daemon.no_stdin = 1; - daemon.out = -1; - - if (start_command(&daemon)) - die_errno("unable to start cache daemon"); - r = read_in_full(daemon.out, buf, sizeof(buf)); - if (r < 0) - die_errno("unable to read result code from cache daemon"); - if (r != 3 || memcmp(buf, "ok\n", 3)) - die("cache daemon did not start: %.*s", r, buf); - close(daemon.out); -} - -static void do_cache(const char *socket, const char *action, int timeout, - int flags) -{ - struct strbuf buf = STRBUF_INIT; - - strbuf_addf(&buf, "action=%s\n", action); - strbuf_addf(&buf, "timeout=%d\n", timeout); - if (flags & FLAG_RELAY) { - if (strbuf_read(&buf, 0, 0) < 0) - die_errno("unable to relay credential"); - } - - if (send_request(socket, &buf) < 0) { - if (errno != ENOENT && errno != ECONNREFUSED) - die_errno("unable to connect to cache daemon"); - if (flags & FLAG_SPAWN) { - spawn_daemon(socket); - if (send_request(socket, &buf) < 0) - die_errno("unable to connect to cache daemon"); - } - } - strbuf_release(&buf); -} - -static char *get_socket_path(void) -{ - struct stat sb; - char *old_dir, *socket; - old_dir = expand_user_path("~/.git-credential-cache", 0); - if (old_dir && !stat(old_dir, &sb) && S_ISDIR(sb.st_mode)) - socket = xstrfmt("%s/socket", old_dir); - else - socket = xdg_cache_home("credential/socket"); - free(old_dir); - return socket; -} - -int cmd_credential_cache(int argc, const char **argv, const char *prefix) -{ - char *socket_path = NULL; - int timeout = 900; - const char *op; - const char * const usage[] = { - "git credential-cache [<options>] <action>", - NULL - }; - struct option options[] = { - OPT_INTEGER(0, "timeout", &timeout, - "number of seconds to cache credentials"), - OPT_STRING(0, "socket", &socket_path, "path", - "path of cache-daemon socket"), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, usage, 0); - if (!argc) - usage_with_options(usage, options); - op = argv[0]; - - if (!socket_path) - socket_path = get_socket_path(); - if (!socket_path) - die("unable to find a suitable socket path; use --socket"); - - if (!strcmp(op, "exit")) - do_cache(socket_path, op, timeout, 0); - else if (!strcmp(op, "get") || !strcmp(op, "erase")) - do_cache(socket_path, op, timeout, FLAG_RELAY); - else if (!strcmp(op, "store")) - do_cache(socket_path, op, timeout, FLAG_RELAY|FLAG_SPAWN); - else - ; /* ignore unknown operation */ - - return 0; -} - -#else - -int cmd_credential_cache(int argc, const char **argv, const char *prefix) -{ - const char * const usage[] = { - "git credential-cache [options] <action>", - "", - "credential-cache is disabled in this build of Git", - NULL - }; - struct option options[] = { OPT_END() }; - - argc = parse_options(argc, argv, prefix, options, usage, 0); - die(_("credential-cache unavailable; no unix socket support")); -} - -#endif /* NO_UNIX_SOCKETS */ diff --git a/third_party/git/builtin/credential-store.c b/third_party/git/builtin/credential-store.c deleted file mode 100644 index 5331ab151a6f..000000000000 --- a/third_party/git/builtin/credential-store.c +++ /dev/null @@ -1,195 +0,0 @@ -#include "builtin.h" -#include "lockfile.h" -#include "credential.h" -#include "string-list.h" -#include "parse-options.h" - -static struct lock_file credential_lock; - -static int parse_credential_file(const char *fn, - struct credential *c, - void (*match_cb)(struct credential *), - void (*other_cb)(struct strbuf *)) -{ - FILE *fh; - struct strbuf line = STRBUF_INIT; - struct credential entry = CREDENTIAL_INIT; - int found_credential = 0; - - fh = fopen(fn, "r"); - if (!fh) { - if (errno != ENOENT && errno != EACCES) - die_errno("unable to open %s", fn); - return found_credential; - } - - while (strbuf_getline_lf(&line, fh) != EOF) { - if (!credential_from_url_gently(&entry, line.buf, 1) && - entry.username && entry.password && - credential_match(c, &entry)) { - found_credential = 1; - if (match_cb) { - match_cb(&entry); - break; - } - } - else if (other_cb) - other_cb(&line); - } - - credential_clear(&entry); - strbuf_release(&line); - fclose(fh); - return found_credential; -} - -static void print_entry(struct credential *c) -{ - printf("username=%s\n", c->username); - printf("password=%s\n", c->password); -} - -static void print_line(struct strbuf *buf) -{ - strbuf_addch(buf, '\n'); - write_or_die(get_lock_file_fd(&credential_lock), buf->buf, buf->len); -} - -static void rewrite_credential_file(const char *fn, struct credential *c, - struct strbuf *extra) -{ - if (hold_lock_file_for_update(&credential_lock, fn, 0) < 0) - die_errno("unable to get credential storage lock"); - if (extra) - print_line(extra); - parse_credential_file(fn, c, NULL, print_line); - if (commit_lock_file(&credential_lock) < 0) - die_errno("unable to write credential store"); -} - -static void store_credential_file(const char *fn, struct credential *c) -{ - struct strbuf buf = STRBUF_INIT; - - strbuf_addf(&buf, "%s://", c->protocol); - strbuf_addstr_urlencode(&buf, c->username, is_rfc3986_unreserved); - strbuf_addch(&buf, ':'); - strbuf_addstr_urlencode(&buf, c->password, is_rfc3986_unreserved); - strbuf_addch(&buf, '@'); - if (c->host) - strbuf_addstr_urlencode(&buf, c->host, is_rfc3986_unreserved); - if (c->path) { - strbuf_addch(&buf, '/'); - strbuf_addstr_urlencode(&buf, c->path, - is_rfc3986_reserved_or_unreserved); - } - - rewrite_credential_file(fn, c, &buf); - strbuf_release(&buf); -} - -static void store_credential(const struct string_list *fns, struct credential *c) -{ - struct string_list_item *fn; - - /* - * Sanity check that what we are storing is actually sensible. - * In particular, we can't make a URL without a protocol field. - * Without either a host or pathname (depending on the scheme), - * we have no primary key. And without a username and password, - * we are not actually storing a credential. - */ - if (!c->protocol || !(c->host || c->path) || !c->username || !c->password) - return; - - for_each_string_list_item(fn, fns) - if (!access(fn->string, F_OK)) { - store_credential_file(fn->string, c); - return; - } - /* - * Write credential to the filename specified by fns->items[0], thus - * creating it - */ - if (fns->nr) - store_credential_file(fns->items[0].string, c); -} - -static void remove_credential(const struct string_list *fns, struct credential *c) -{ - struct string_list_item *fn; - - /* - * Sanity check that we actually have something to match - * against. The input we get is a restrictive pattern, - * so technically a blank credential means "erase everything". - * But it is too easy to accidentally send this, since it is equivalent - * to empty input. So explicitly disallow it, and require that the - * pattern have some actual content to match. - */ - if (!c->protocol && !c->host && !c->path && !c->username) - return; - for_each_string_list_item(fn, fns) - if (!access(fn->string, F_OK)) - rewrite_credential_file(fn->string, c, NULL); -} - -static void lookup_credential(const struct string_list *fns, struct credential *c) -{ - struct string_list_item *fn; - - for_each_string_list_item(fn, fns) - if (parse_credential_file(fn->string, c, print_entry, NULL)) - return; /* Found credential */ -} - -int cmd_credential_store(int argc, const char **argv, const char *prefix) -{ - const char * const usage[] = { - "git credential-store [<options>] <action>", - NULL - }; - const char *op; - struct credential c = CREDENTIAL_INIT; - struct string_list fns = STRING_LIST_INIT_DUP; - char *file = NULL; - struct option options[] = { - OPT_STRING(0, "file", &file, "path", - "fetch and store credentials in <path>"), - OPT_END() - }; - - umask(077); - - argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0); - if (argc != 1) - usage_with_options(usage, options); - op = argv[0]; - - if (file) { - string_list_append(&fns, file); - } else { - if ((file = expand_user_path("~/.git-credentials", 0))) - string_list_append_nodup(&fns, file); - file = xdg_config_home("credentials"); - if (file) - string_list_append_nodup(&fns, file); - } - if (!fns.nr) - die("unable to set up default path; use --file"); - - if (credential_read(&c, stdin) < 0) - die("unable to read credential"); - - if (!strcmp(op, "get")) - lookup_credential(&fns, &c); - else if (!strcmp(op, "erase")) - remove_credential(&fns, &c); - else if (!strcmp(op, "store")) - store_credential(&fns, &c); - else - ; /* Ignore unknown operation. */ - - string_list_clear(&fns, 0); - return 0; -} diff --git a/third_party/git/builtin/credential.c b/third_party/git/builtin/credential.c deleted file mode 100644 index 879acfbcda75..000000000000 --- a/third_party/git/builtin/credential.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "git-compat-util.h" -#include "credential.h" -#include "builtin.h" - -static const char usage_msg[] = - "git credential [fill|approve|reject]"; - -int cmd_credential(int argc, const char **argv, const char *prefix) -{ - const char *op; - struct credential c = CREDENTIAL_INIT; - - if (argc != 2 || !strcmp(argv[1], "-h")) - usage(usage_msg); - op = argv[1]; - - if (credential_read(&c, stdin) < 0) - die("unable to read credential from stdin"); - - if (!strcmp(op, "fill")) { - credential_fill(&c); - credential_write(&c, stdout); - } else if (!strcmp(op, "approve")) { - credential_approve(&c); - } else if (!strcmp(op, "reject")) { - credential_reject(&c); - } else { - usage(usage_msg); - } - return 0; -} diff --git a/third_party/git/builtin/describe.c b/third_party/git/builtin/describe.c deleted file mode 100644 index 7668591d575a..000000000000 --- a/third_party/git/builtin/describe.c +++ /dev/null @@ -1,681 +0,0 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "lockfile.h" -#include "commit.h" -#include "tag.h" -#include "blob.h" -#include "refs.h" -#include "builtin.h" -#include "exec-cmd.h" -#include "parse-options.h" -#include "revision.h" -#include "diff.h" -#include "hashmap.h" -#include "strvec.h" -#include "run-command.h" -#include "object-store.h" -#include "list-objects.h" -#include "commit-slab.h" - -#define MAX_TAGS (FLAG_BITS - 1) - -define_commit_slab(commit_names, struct commit_name *); - -static const char * const describe_usage[] = { - N_("git describe [<options>] [<commit-ish>...]"), - N_("git describe [<options>] --dirty"), - NULL -}; - -static int debug; /* Display lots of verbose info */ -static int all; /* Any valid ref can be used */ -static int tags; /* Allow lightweight tags */ -static int longformat; -static int first_parent; -static int abbrev = -1; /* unspecified */ -static int max_candidates = 10; -static struct hashmap names; -static int have_util; -static struct string_list patterns = STRING_LIST_INIT_NODUP; -static struct string_list exclude_patterns = STRING_LIST_INIT_NODUP; -static int always; -static const char *suffix, *dirty, *broken; -static struct commit_names commit_names; - -/* diff-index command arguments to check if working tree is dirty. */ -static const char *diff_index_args[] = { - "diff-index", "--quiet", "HEAD", "--", NULL -}; - -struct commit_name { - struct hashmap_entry entry; - struct object_id peeled; - struct tag *tag; - unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */ - unsigned name_checked:1; - unsigned misnamed:1; - struct object_id oid; - char *path; -}; - -static const char *prio_names[] = { - N_("head"), N_("lightweight"), N_("annotated"), -}; - -static int commit_name_neq(const void *unused_cmp_data, - const struct hashmap_entry *eptr, - const struct hashmap_entry *entry_or_key, - const void *peeled) -{ - const struct commit_name *cn1, *cn2; - - cn1 = container_of(eptr, const struct commit_name, entry); - cn2 = container_of(entry_or_key, const struct commit_name, entry); - - return !oideq(&cn1->peeled, peeled ? peeled : &cn2->peeled); -} - -static inline struct commit_name *find_commit_name(const struct object_id *peeled) -{ - return hashmap_get_entry_from_hash(&names, oidhash(peeled), peeled, - struct commit_name, entry); -} - -static int replace_name(struct commit_name *e, - int prio, - const struct object_id *oid, - struct tag **tag) -{ - if (!e || e->prio < prio) - return 1; - - if (e->prio == 2 && prio == 2) { - /* Multiple annotated tags point to the same commit. - * Select one to keep based upon their tagger date. - */ - struct tag *t; - - if (!e->tag) { - t = lookup_tag(the_repository, &e->oid); - if (!t || parse_tag(t)) - return 1; - e->tag = t; - } - - t = lookup_tag(the_repository, oid); - if (!t || parse_tag(t)) - return 0; - *tag = t; - - if (e->tag->date < t->date) - return 1; - } - - return 0; -} - -static void add_to_known_names(const char *path, - const struct object_id *peeled, - int prio, - const struct object_id *oid) -{ - struct commit_name *e = find_commit_name(peeled); - struct tag *tag = NULL; - if (replace_name(e, prio, oid, &tag)) { - if (!e) { - e = xmalloc(sizeof(struct commit_name)); - oidcpy(&e->peeled, peeled); - hashmap_entry_init(&e->entry, oidhash(peeled)); - hashmap_add(&names, &e->entry); - e->path = NULL; - } - e->tag = tag; - e->prio = prio; - e->name_checked = 0; - e->misnamed = 0; - oidcpy(&e->oid, oid); - free(e->path); - e->path = xstrdup(path); - } -} - -static int get_name(const char *path, const struct object_id *oid, int flag, void *cb_data) -{ - int is_tag = 0; - struct object_id peeled; - int is_annotated, prio; - const char *path_to_match = NULL; - - if (skip_prefix(path, "refs/tags/", &path_to_match)) { - is_tag = 1; - } else if (all) { - if ((exclude_patterns.nr || patterns.nr) && - !skip_prefix(path, "refs/heads/", &path_to_match) && - !skip_prefix(path, "refs/remotes/", &path_to_match)) { - /* Only accept reference of known type if there are match/exclude patterns */ - return 0; - } - } else { - /* Reject anything outside refs/tags/ unless --all */ - return 0; - } - - /* - * If we're given exclude patterns, first exclude any tag which match - * any of the exclude pattern. - */ - if (exclude_patterns.nr) { - struct string_list_item *item; - - for_each_string_list_item(item, &exclude_patterns) { - if (!wildmatch(item->string, path_to_match, 0)) - return 0; - } - } - - /* - * If we're given patterns, accept only tags which match at least one - * pattern. - */ - if (patterns.nr) { - int found = 0; - struct string_list_item *item; - - for_each_string_list_item(item, &patterns) { - if (!wildmatch(item->string, path_to_match, 0)) { - found = 1; - break; - } - } - - if (!found) - return 0; - } - - /* Is it annotated? */ - if (!peel_ref(path, &peeled)) { - is_annotated = !oideq(oid, &peeled); - } else { - oidcpy(&peeled, oid); - is_annotated = 0; - } - - /* - * By default, we only use annotated tags, but with --tags - * we fall back to lightweight ones (even without --tags, - * we still remember lightweight ones, only to give hints - * in an error message). --all allows any refs to be used. - */ - if (is_annotated) - prio = 2; - else if (is_tag) - prio = 1; - else - prio = 0; - - add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid); - return 0; -} - -struct possible_tag { - struct commit_name *name; - int depth; - int found_order; - unsigned flag_within; -}; - -static int compare_pt(const void *a_, const void *b_) -{ - struct possible_tag *a = (struct possible_tag *)a_; - struct possible_tag *b = (struct possible_tag *)b_; - if (a->depth != b->depth) - return a->depth - b->depth; - if (a->found_order != b->found_order) - return a->found_order - b->found_order; - return 0; -} - -static unsigned long finish_depth_computation( - struct commit_list **list, - struct possible_tag *best) -{ - unsigned long seen_commits = 0; - while (*list) { - struct commit *c = pop_commit(list); - struct commit_list *parents = c->parents; - seen_commits++; - if (c->object.flags & best->flag_within) { - struct commit_list *a = *list; - while (a) { - struct commit *i = a->item; - if (!(i->object.flags & best->flag_within)) - break; - a = a->next; - } - if (!a) - break; - } else - best->depth++; - while (parents) { - struct commit *p = parents->item; - parse_commit(p); - if (!(p->object.flags & SEEN)) - commit_list_insert_by_date(p, list); - p->object.flags |= c->object.flags; - parents = parents->next; - } - } - return seen_commits; -} - -static void append_name(struct commit_name *n, struct strbuf *dst) -{ - if (n->prio == 2 && !n->tag) { - n->tag = lookup_tag(the_repository, &n->oid); - if (!n->tag || parse_tag(n->tag)) - die(_("annotated tag %s not available"), n->path); - } - if (n->tag && !n->name_checked) { - if (strcmp(n->tag->tag, all ? n->path + 5 : n->path)) { - warning(_("tag '%s' is externally known as '%s'"), - n->path, n->tag->tag); - n->misnamed = 1; - } - n->name_checked = 1; - } - - if (n->tag) { - if (all) - strbuf_addstr(dst, "tags/"); - strbuf_addstr(dst, n->tag->tag); - } else { - strbuf_addstr(dst, n->path); - } -} - -static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst) -{ - strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid, abbrev)); -} - -static void describe_commit(struct object_id *oid, struct strbuf *dst) -{ - struct commit *cmit, *gave_up_on = NULL; - struct commit_list *list; - struct commit_name *n; - struct possible_tag all_matches[MAX_TAGS]; - unsigned int match_cnt = 0, annotated_cnt = 0, cur_match; - unsigned long seen_commits = 0; - unsigned int unannotated_cnt = 0; - - cmit = lookup_commit_reference(the_repository, oid); - - n = find_commit_name(&cmit->object.oid); - if (n && (tags || all || n->prio == 2)) { - /* - * Exact match to an existing ref. - */ - append_name(n, dst); - if (n->misnamed || longformat) - append_suffix(0, n->tag ? get_tagged_oid(n->tag) : oid, dst); - if (suffix) - strbuf_addstr(dst, suffix); - return; - } - - if (!max_candidates) - die(_("no tag exactly matches '%s'"), oid_to_hex(&cmit->object.oid)); - if (debug) - fprintf(stderr, _("No exact match on refs or tags, searching to describe\n")); - - if (!have_util) { - struct hashmap_iter iter; - struct commit *c; - struct commit_name *n; - - init_commit_names(&commit_names); - hashmap_for_each_entry(&names, &iter, n, - entry /* member name */) { - c = lookup_commit_reference_gently(the_repository, - &n->peeled, 1); - if (c) - *commit_names_at(&commit_names, c) = n; - } - have_util = 1; - } - - list = NULL; - cmit->object.flags = SEEN; - commit_list_insert(cmit, &list); - while (list) { - struct commit *c = pop_commit(&list); - struct commit_list *parents = c->parents; - struct commit_name **slot; - - seen_commits++; - slot = commit_names_peek(&commit_names, c); - n = slot ? *slot : NULL; - if (n) { - if (!tags && !all && n->prio < 2) { - unannotated_cnt++; - } else if (match_cnt < max_candidates) { - struct possible_tag *t = &all_matches[match_cnt++]; - t->name = n; - t->depth = seen_commits - 1; - t->flag_within = 1u << match_cnt; - t->found_order = match_cnt; - c->object.flags |= t->flag_within; - if (n->prio == 2) - annotated_cnt++; - } - else { - gave_up_on = c; - break; - } - } - for (cur_match = 0; cur_match < match_cnt; cur_match++) { - struct possible_tag *t = &all_matches[cur_match]; - if (!(c->object.flags & t->flag_within)) - t->depth++; - } - /* Stop if last remaining path already covered by best candidate(s) */ - if (annotated_cnt && !list) { - int best_depth = INT_MAX; - unsigned best_within = 0; - for (cur_match = 0; cur_match < match_cnt; cur_match++) { - struct possible_tag *t = &all_matches[cur_match]; - if (t->depth < best_depth) { - best_depth = t->depth; - best_within = t->flag_within; - } else if (t->depth == best_depth) { - best_within |= t->flag_within; - } - } - if ((c->object.flags & best_within) == best_within) { - if (debug) - fprintf(stderr, _("finished search at %s\n"), - oid_to_hex(&c->object.oid)); - break; - } - } - while (parents) { - struct commit *p = parents->item; - parse_commit(p); - if (!(p->object.flags & SEEN)) - commit_list_insert_by_date(p, &list); - p->object.flags |= c->object.flags; - parents = parents->next; - - if (first_parent) - break; - } - } - - if (!match_cnt) { - struct object_id *cmit_oid = &cmit->object.oid; - if (always) { - strbuf_add_unique_abbrev(dst, cmit_oid, abbrev); - if (suffix) - strbuf_addstr(dst, suffix); - return; - } - if (unannotated_cnt) - die(_("No annotated tags can describe '%s'.\n" - "However, there were unannotated tags: try --tags."), - oid_to_hex(cmit_oid)); - else - die(_("No tags can describe '%s'.\n" - "Try --always, or create some tags."), - oid_to_hex(cmit_oid)); - } - - QSORT(all_matches, match_cnt, compare_pt); - - if (gave_up_on) { - commit_list_insert_by_date(gave_up_on, &list); - seen_commits--; - } - seen_commits += finish_depth_computation(&list, &all_matches[0]); - free_commit_list(list); - - if (debug) { - static int label_width = -1; - if (label_width < 0) { - int i, w; - for (i = 0; i < ARRAY_SIZE(prio_names); i++) { - w = strlen(_(prio_names[i])); - if (label_width < w) - label_width = w; - } - } - for (cur_match = 0; cur_match < match_cnt; cur_match++) { - struct possible_tag *t = &all_matches[cur_match]; - fprintf(stderr, " %-*s %8d %s\n", - label_width, _(prio_names[t->name->prio]), - t->depth, t->name->path); - } - fprintf(stderr, _("traversed %lu commits\n"), seen_commits); - if (gave_up_on) { - fprintf(stderr, - _("more than %i tags found; listed %i most recent\n" - "gave up search at %s\n"), - max_candidates, max_candidates, - oid_to_hex(&gave_up_on->object.oid)); - } - } - - append_name(all_matches[0].name, dst); - if (all_matches[0].name->misnamed || abbrev) - append_suffix(all_matches[0].depth, &cmit->object.oid, dst); - if (suffix) - strbuf_addstr(dst, suffix); -} - -struct process_commit_data { - struct object_id current_commit; - struct object_id looking_for; - struct strbuf *dst; - struct rev_info *revs; -}; - -static void process_commit(struct commit *commit, void *data) -{ - struct process_commit_data *pcd = data; - pcd->current_commit = commit->object.oid; -} - -static void process_object(struct object *obj, const char *path, void *data) -{ - struct process_commit_data *pcd = data; - - if (oideq(&pcd->looking_for, &obj->oid) && !pcd->dst->len) { - reset_revision_walk(); - describe_commit(&pcd->current_commit, pcd->dst); - strbuf_addf(pcd->dst, ":%s", path); - free_commit_list(pcd->revs->commits); - pcd->revs->commits = NULL; - } -} - -static void describe_blob(struct object_id oid, struct strbuf *dst) -{ - struct rev_info revs; - struct strvec args = STRVEC_INIT; - struct process_commit_data pcd = { null_oid, oid, dst, &revs}; - - strvec_pushl(&args, "internal: The first arg is not parsed", - "--objects", "--in-commit-order", "--reverse", "HEAD", - NULL); - - repo_init_revisions(the_repository, &revs, NULL); - if (setup_revisions(args.nr, args.v, &revs, NULL) > 1) - BUG("setup_revisions could not handle all args?"); - - if (prepare_revision_walk(&revs)) - die("revision walk setup failed"); - - traverse_commit_list(&revs, process_commit, process_object, &pcd); - reset_revision_walk(); -} - -static void describe(const char *arg, int last_one) -{ - struct object_id oid; - struct commit *cmit; - struct strbuf sb = STRBUF_INIT; - - if (debug) - fprintf(stderr, _("describe %s\n"), arg); - - if (get_oid(arg, &oid)) - die(_("Not a valid object name %s"), arg); - cmit = lookup_commit_reference_gently(the_repository, &oid, 1); - - if (cmit) - describe_commit(&oid, &sb); - else if (oid_object_info(the_repository, &oid, NULL) == OBJ_BLOB) - describe_blob(oid, &sb); - else - die(_("%s is neither a commit nor blob"), arg); - - puts(sb.buf); - - if (!last_one) - clear_commit_marks(cmit, -1); - - strbuf_release(&sb); -} - -int cmd_describe(int argc, const char **argv, const char *prefix) -{ - int contains = 0; - struct option options[] = { - OPT_BOOL(0, "contains", &contains, N_("find the tag that comes after the commit")), - OPT_BOOL(0, "debug", &debug, N_("debug search strategy on stderr")), - OPT_BOOL(0, "all", &all, N_("use any ref")), - OPT_BOOL(0, "tags", &tags, N_("use any tag, even unannotated")), - OPT_BOOL(0, "long", &longformat, N_("always use long format")), - OPT_BOOL(0, "first-parent", &first_parent, N_("only follow first parent")), - 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_LIST(0, "match", &patterns, N_("pattern"), - N_("only consider tags matching <pattern>")), - OPT_STRING_LIST(0, "exclude", &exclude_patterns, N_("pattern"), - N_("do not consider tags matching <pattern>")), - OPT_BOOL(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"}, - {OPTION_STRING, 0, "broken", &broken, N_("mark"), - N_("append <mark> on broken working tree (default: \"-broken\")"), - PARSE_OPT_OPTARG, NULL, (intptr_t) "-broken"}, - 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) { - struct string_list_item *item; - struct strvec args; - - strvec_init(&args); - strvec_pushl(&args, "name-rev", - "--peel-tag", "--name-only", "--no-undefined", - NULL); - if (always) - strvec_push(&args, "--always"); - if (!all) { - strvec_push(&args, "--tags"); - for_each_string_list_item(item, &patterns) - strvec_pushf(&args, "--refs=refs/tags/%s", item->string); - for_each_string_list_item(item, &exclude_patterns) - strvec_pushf(&args, "--exclude=refs/tags/%s", item->string); - } - if (argc) - strvec_pushv(&args, argv); - else - strvec_push(&args, "HEAD"); - return cmd_name_rev(args.nr, args.v, prefix); - } - - hashmap_init(&names, commit_name_neq, NULL, 0); - for_each_rawref(get_name, NULL); - if (!hashmap_get_size(&names) && !always) - die(_("No names found, cannot describe anything.")); - - if (argc == 0) { - if (broken) { - struct child_process cp = CHILD_PROCESS_INIT; - strvec_pushv(&cp.args, diff_index_args); - cp.git_cmd = 1; - cp.no_stdin = 1; - cp.no_stdout = 1; - - if (!dirty) - dirty = "-dirty"; - - switch (run_command(&cp)) { - case 0: - suffix = NULL; - break; - case 1: - suffix = dirty; - break; - default: - /* diff-index aborted abnormally */ - suffix = broken; - } - } else if (dirty) { - struct lock_file index_lock = LOCK_INIT; - struct rev_info revs; - struct strvec args = STRVEC_INIT; - int fd, result; - - setup_work_tree(); - read_cache(); - refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, - NULL, NULL, NULL); - fd = hold_locked_index(&index_lock, 0); - if (0 <= fd) - repo_update_index_if_able(the_repository, &index_lock); - - repo_init_revisions(the_repository, &revs, prefix); - strvec_pushv(&args, diff_index_args); - if (setup_revisions(args.nr, args.v, &revs, NULL) != 1) - BUG("malformed internal diff-index command line"); - result = run_diff_index(&revs, 0); - - if (!diff_result_code(&revs.diffopt, result)) - suffix = NULL; - else - suffix = dirty; - } - describe("HEAD", 1); - } else if (dirty) { - die(_("--dirty is incompatible with commit-ishes")); - } else if (broken) { - die(_("--broken is incompatible with commit-ishes")); - } else { - while (argc-- > 0) - describe(*argv++, argc == 0); - } - return 0; -} diff --git a/third_party/git/builtin/diff-files.c b/third_party/git/builtin/diff-files.c deleted file mode 100644 index 1e352dd8f77c..000000000000 --- a/third_party/git/builtin/diff-files.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * GIT - The information manager from hell - * - * Copyright (C) Linus Torvalds, 2005 - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "diff.h" -#include "commit.h" -#include "revision.h" -#include "builtin.h" -#include "submodule.h" - -static const char diff_files_usage[] = -"git diff-files [-q] [-0 | -1 | -2 | -3 | -c | --cc] [<common-diff-options>] [<path>...]" -COMMON_DIFF_OPTIONS_HELP; - -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 */ - repo_init_revisions(the_repository, &rev, prefix); - rev.abbrev = 0; - - /* - * Consider "intent-to-add" files as new by default, unless - * explicitly specified in the command line or anywhere else. - */ - rev.diffopt.ita_invisible_in_index = 1; - - 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); -} diff --git a/third_party/git/builtin/diff-index.c b/third_party/git/builtin/diff-index.c deleted file mode 100644 index 93ec6424234c..000000000000 --- a/third_party/git/builtin/diff-index.c +++ /dev/null @@ -1,62 +0,0 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "diff.h" -#include "commit.h" -#include "revision.h" -#include "builtin.h" -#include "submodule.h" - -static const char diff_cache_usage[] = -"git diff-index [-m] [--cached] " -"[<common-diff-options>] <tree-ish> [<path>...]" -COMMON_DIFF_OPTIONS_HELP; - -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 */ - repo_init_revisions(the_repository, &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); -} diff --git a/third_party/git/builtin/diff-tree.c b/third_party/git/builtin/diff-tree.c deleted file mode 100644 index 802363d0a229..000000000000 --- a/third_party/git/builtin/diff-tree.c +++ /dev/null @@ -1,205 +0,0 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "diff.h" -#include "commit.h" -#include "log-tree.h" -#include "builtin.h" -#include "submodule.h" -#include "repository.h" - -static struct rev_info log_tree_opt; - -static int diff_tree_commit_oid(const struct object_id *oid) -{ - struct commit *commit = lookup_commit_reference(the_repository, oid); - if (!commit) - return -1; - return log_tree_commit(&log_tree_opt, commit); -} - -/* Diff one or more commits. */ -static int stdin_diff_commit(struct commit *commit, const char *p) -{ - struct object_id oid; - struct commit_list **pptr = NULL; - - /* Graft the fake parents locally to the commit */ - while (isspace(*p++) && !parse_oid_hex(p, &oid, &p)) { - struct commit *parent = lookup_commit(the_repository, &oid); - if (!pptr) { - /* Free the real parent list */ - free_commit_list(commit->parents); - commit->parents = NULL; - pptr = &(commit->parents); - } - if (parent) { - pptr = &commit_list_insert(parent, pptr)->next; - } - } - return log_tree_commit(&log_tree_opt, commit); -} - -/* Diff two trees. */ -static int stdin_diff_trees(struct tree *tree1, const char *p) -{ - struct object_id oid; - struct tree *tree2; - if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p) - return error("Need exactly two trees, separated by a space"); - tree2 = lookup_tree(the_repository, &oid); - if (!tree2 || parse_tree(tree2)) - return -1; - printf("%s %s\n", oid_to_hex(&tree1->object.oid), - oid_to_hex(&tree2->object.oid)); - diff_tree_oid(&tree1->object.oid, &tree2->object.oid, - "", &log_tree_opt.diffopt); - log_tree_diff_flush(&log_tree_opt); - return 0; -} - -static int diff_tree_stdin(char *line) -{ - int len = strlen(line); - struct object_id oid; - struct object *obj; - const char *p; - - if (!len || line[len-1] != '\n') - return -1; - line[len-1] = 0; - if (parse_oid_hex(line, &oid, &p)) - return -1; - obj = parse_object(the_repository, &oid); - if (!obj) - return -1; - if (obj->type == OBJ_COMMIT) - return stdin_diff_commit((struct commit *)obj, p); - if (obj->type == OBJ_TREE) - return stdin_diff_trees((struct tree *)obj, p); - error("Object %s is a %s, not a commit or tree", - oid_to_hex(&oid), type_name(obj->type)); - return -1; -} - -static const char diff_tree_usage[] = -"git diff-tree [--stdin] [-m] [-c | --cc] [-s] [-v] [--pretty] [-t] [-r] [--root] " -"[<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]\n" -" -r diff recursively\n" -" -c show combined diff for merge commits\n" -" --cc show combined diff for merge commits removing uninteresting hunks\n" -" --combined-all-paths\n" -" show name of file in all parents for combined diffs\n" -" --root include the initial commit as diff against /dev/null\n" -COMMON_DIFF_OPTIONS_HELP; - -static void diff_tree_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt) -{ - if (!rev->diffopt.output_format) { - if (rev->dense_combined_merges) - rev->diffopt.output_format = DIFF_FORMAT_PATCH; - else - rev->diffopt.output_format = DIFF_FORMAT_RAW; - } -} - -int cmd_diff_tree(int argc, const char **argv, const char *prefix) -{ - char line[1000]; - struct object *tree1, *tree2; - static struct rev_info *opt = &log_tree_opt; - struct setup_revision_opt s_r_opt; - struct userformat_want w; - int read_stdin = 0; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage(diff_tree_usage); - - git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ - repo_init_revisions(the_repository, opt, prefix); - if (read_cache() < 0) - die(_("index file corrupt")); - opt->abbrev = 0; - opt->diff = 1; - opt->disable_stdin = 1; - memset(&s_r_opt, 0, sizeof(s_r_opt)); - s_r_opt.tweak = diff_tree_tweak_rev; - - precompose_argv(argc, argv); - argc = setup_revisions(argc, argv, opt, &s_r_opt); - - memset(&w, 0, sizeof(w)); - userformat_find_requirements(NULL, &w); - - if (!opt->show_notes_given && w.notes) - opt->show_notes = 1; - if (opt->show_notes) - load_display_notes(&opt->notes_opt); - - while (--argc > 0) { - const char *arg = *++argv; - - if (!strcmp(arg, "--stdin")) { - read_stdin = 1; - continue; - } - usage(diff_tree_usage); - } - - /* - * NOTE! We expect "a..b" to expand to "^a b" but it is - * perfectly valid for revision range parser to yield "b ^a", - * which means the same thing. If we get the latter, i.e. the - * second one is marked UNINTERESTING, we recover the original - * order the user gave, i.e. "a..b", by swapping the trees. - */ - switch (opt->pending.nr) { - case 0: - if (!read_stdin) - usage(diff_tree_usage); - break; - case 1: - tree1 = opt->pending.objects[0].item; - diff_tree_commit_oid(&tree1->oid); - break; - case 2: - tree1 = opt->pending.objects[0].item; - tree2 = opt->pending.objects[1].item; - if (tree2->flags & UNINTERESTING) { - SWAP(tree2, tree1); - } - diff_tree_oid(&tree1->oid, &tree2->oid, "", &opt->diffopt); - log_tree_diff_flush(opt); - break; - } - - if (read_stdin) { - int saved_nrl = 0; - int saved_dcctc = 0; - - if (opt->diffopt.detect_rename) { - if (!the_index.cache) - repo_read_index(the_repository); - opt->diffopt.setup |= DIFF_SETUP_USE_SIZE_CACHE; - } - while (fgets(line, sizeof(line), stdin)) { - struct object_id oid; - - if (get_oid_hex(line, &oid)) { - fputs(line, stdout); - fflush(stdout); - } - else { - diff_tree_stdin(line); - if (saved_nrl < opt->diffopt.needed_rename_limit) - saved_nrl = opt->diffopt.needed_rename_limit; - if (opt->diffopt.degraded_cc_to_c) - saved_dcctc = 1; - } - } - opt->diffopt.degraded_cc_to_c = saved_dcctc; - opt->diffopt.needed_rename_limit = saved_nrl; - } - - return diff_result_code(&opt->diffopt, 0); -} diff --git a/third_party/git/builtin/diff.c b/third_party/git/builtin/diff.c deleted file mode 100644 index cd4083fed96e..000000000000 --- a/third_party/git/builtin/diff.c +++ /dev/null @@ -1,577 +0,0 @@ -/* - * Builtin "git diff" - * - * Copyright (c) 2006 Junio C Hamano - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "ewah/ewok.h" -#include "lockfile.h" -#include "color.h" -#include "commit.h" -#include "blob.h" -#include "tag.h" -#include "diff.h" -#include "diffcore.h" -#include "revision.h" -#include "log-tree.h" -#include "builtin.h" -#include "submodule.h" -#include "oid-array.h" - -#define DIFF_NO_INDEX_EXPLICIT 1 -#define DIFF_NO_INDEX_IMPLICIT 2 - -static const char builtin_diff_usage[] = -"git diff [<options>] [<commit>] [--] [<path>...]\n" -" or: git diff [<options>] --cached [<commit>] [--] [<path>...]\n" -" or: git diff [<options>] <commit> [<commit>...] <commit> [--] [<path>...]\n" -" or: git diff [<options>] <commit>...<commit>] [--] [<path>...]\n" -" or: git diff [<options>] <blob> <blob>]\n" -" or: git diff [<options>] --no-index [--] <path> <path>]\n" -COMMON_DIFF_OPTIONS_HELP; - -static const char *blob_path(struct object_array_entry *entry) -{ - return entry->path ? entry->path : entry->name; -} - -static void stuff_change(struct diff_options *opt, - unsigned old_mode, unsigned new_mode, - const struct object_id *old_oid, - const struct object_id *new_oid, - int old_oid_valid, - int new_oid_valid, - const char *old_path, - const char *new_path) -{ - struct diff_filespec *one, *two; - - if (!is_null_oid(old_oid) && !is_null_oid(new_oid) && - oideq(old_oid, new_oid) && (old_mode == new_mode)) - return; - - if (opt->flags.reverse_diff) { - SWAP(old_mode, new_mode); - SWAP(old_oid, new_oid); - SWAP(old_path, new_path); - } - - if (opt->prefix && - (strncmp(old_path, opt->prefix, opt->prefix_length) || - strncmp(new_path, opt->prefix, opt->prefix_length))) - return; - - one = alloc_filespec(old_path); - two = alloc_filespec(new_path); - fill_filespec(one, old_oid, old_oid_valid, old_mode); - fill_filespec(two, new_oid, new_oid_valid, new_mode); - - diff_queue(&diff_queued_diff, one, two); -} - -static int builtin_diff_b_f(struct rev_info *revs, - int argc, const char **argv, - struct object_array_entry **blob) -{ - /* Blob vs file in the working tree*/ - struct stat st; - const char *path; - - if (argc > 1) - usage(builtin_diff_usage); - - GUARD_PATHSPEC(&revs->prune_data, PATHSPEC_FROMTOP | PATHSPEC_LITERAL); - path = revs->prune_data.items[0].match; - - if (lstat(path, &st)) - die_errno(_("failed to stat '%s'"), path); - if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) - die(_("'%s': not a regular file or symlink"), path); - - diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/"); - - if (blob[0]->mode == S_IFINVALID) - blob[0]->mode = canon_mode(st.st_mode); - - stuff_change(&revs->diffopt, - blob[0]->mode, canon_mode(st.st_mode), - &blob[0]->item->oid, &null_oid, - 1, 0, - blob[0]->path ? blob[0]->path : path, - path); - diffcore_std(&revs->diffopt); - diff_flush(&revs->diffopt); - return 0; -} - -static int builtin_diff_blobs(struct rev_info *revs, - int argc, const char **argv, - struct object_array_entry **blob) -{ - const unsigned mode = canon_mode(S_IFREG | 0644); - - if (argc > 1) - usage(builtin_diff_usage); - - if (blob[0]->mode == S_IFINVALID) - blob[0]->mode = mode; - - if (blob[1]->mode == S_IFINVALID) - blob[1]->mode = mode; - - stuff_change(&revs->diffopt, - blob[0]->mode, blob[1]->mode, - &blob[0]->item->oid, &blob[1]->item->oid, - 1, 1, - blob_path(blob[0]), blob_path(blob[1])); - diffcore_std(&revs->diffopt); - diff_flush(&revs->diffopt); - 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--; - } - /* - * 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 (!cached) { - setup_work_tree(); - if (read_cache_preload(&revs->diffopt.pathspec) < 0) { - perror("read_cache_preload"); - return -1; - } - } else if (read_cache() < 0) { - perror("read_cache"); - return -1; - } - return run_diff_index(revs, cached); -} - -static int builtin_diff_tree(struct rev_info *revs, - int argc, const char **argv, - struct object_array_entry *ent0, - struct object_array_entry *ent1) -{ - const struct object_id *(oid[2]); - int swap = 0; - - if (argc > 1) - usage(builtin_diff_usage); - - /* - * We saw two trees, ent0 and ent1. If ent1 is uninteresting, - * swap them. - */ - if (ent1->item->flags & UNINTERESTING) - swap = 1; - oid[swap] = &ent0->item->oid; - oid[1 - swap] = &ent1->item->oid; - diff_tree_oid(oid[0], oid[1], "", &revs->diffopt); - log_tree_diff_flush(revs); - return 0; -} - -static int builtin_diff_combined(struct rev_info *revs, - int argc, const char **argv, - struct object_array_entry *ent, - int ents) -{ - struct oid_array parents = OID_ARRAY_INIT; - int i; - - if (argc > 1) - usage(builtin_diff_usage); - - if (!revs->dense_combined_merges && !revs->combine_merges) - revs->dense_combined_merges = revs->combine_merges = 1; - for (i = 1; i < ents; i++) - oid_array_append(&parents, &ent[i].item->oid); - diff_tree_combined(&ent[0].item->oid, &parents, revs); - oid_array_clear(&parents); - return 0; -} - -static void refresh_index_quietly(void) -{ - struct lock_file lock_file = LOCK_INIT; - int fd; - - fd = hold_locked_index(&lock_file, 0); - if (fd < 0) - return; - discard_cache(); - read_cache(); - refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED); - repo_update_index_if_able(the_repository, &lock_file); -} - -static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv) -{ - 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.pathspec) < 0) { - perror("read_cache_preload"); - return -1; - } - return run_diff_files(revs, options); -} - -struct symdiff { - struct bitmap *skip; - int warn; - const char *base, *left, *right; -}; - -/* - * Check for symmetric-difference arguments, and if present, arrange - * everything we need to know to handle them correctly. As a bonus, - * weed out all bogus range-based revision specifications, e.g., - * "git diff A..B C..D" or "git diff A..B C" get rejected. - * - * For an actual symmetric diff, *symdiff is set this way: - * - * - its skip is non-NULL and marks *all* rev->pending.objects[i] - * indices that the caller should ignore (extra merge bases, of - * which there might be many, and A in A...B). Note that the - * chosen merge base and right side are NOT marked. - * - warn is set if there are multiple merge bases. - * - base, left, and right point to the names to use in a - * warning about multiple merge bases. - * - * If there is no symmetric diff argument, sym->skip is NULL and - * sym->warn is cleared. The remaining fields are not set. - */ -static void symdiff_prepare(struct rev_info *rev, struct symdiff *sym) -{ - int i, is_symdiff = 0, basecount = 0, othercount = 0; - int lpos = -1, rpos = -1, basepos = -1; - struct bitmap *map = NULL; - - /* - * Use the whence fields to find merge bases and left and - * right parts of symmetric difference, so that we do not - * depend on the order that revisions are parsed. If there - * are any revs that aren't from these sources, we have a - * "git diff C A...B" or "git diff A...B C" case. Or we - * could even get "git diff A...B C...E", for instance. - * - * If we don't have just one merge base, we pick one - * at random. - * - * NB: REV_CMD_LEFT, REV_CMD_RIGHT are also used for A..B, - * so we must check for SYMMETRIC_LEFT too. The two arrays - * rev->pending.objects and rev->cmdline.rev are parallel. - */ - for (i = 0; i < rev->cmdline.nr; i++) { - struct object *obj = rev->pending.objects[i].item; - switch (rev->cmdline.rev[i].whence) { - case REV_CMD_MERGE_BASE: - if (basepos < 0) - basepos = i; - basecount++; - break; /* do mark all bases */ - case REV_CMD_LEFT: - if (lpos >= 0) - usage(builtin_diff_usage); - lpos = i; - if (obj->flags & SYMMETRIC_LEFT) { - is_symdiff = 1; - break; /* do mark A */ - } - continue; - case REV_CMD_RIGHT: - if (rpos >= 0) - usage(builtin_diff_usage); - rpos = i; - continue; /* don't mark B */ - case REV_CMD_PARENTS_ONLY: - case REV_CMD_REF: - case REV_CMD_REV: - othercount++; - continue; - } - if (map == NULL) - map = bitmap_new(); - bitmap_set(map, i); - } - - /* - * Forbid any additional revs for both A...B and A..B. - */ - if (lpos >= 0 && othercount > 0) - usage(builtin_diff_usage); - - if (!is_symdiff) { - bitmap_free(map); - sym->warn = 0; - sym->skip = NULL; - return; - } - - sym->left = rev->pending.objects[lpos].name; - sym->right = rev->pending.objects[rpos].name; - if (basecount == 0) - die(_("%s...%s: no merge base"), sym->left, sym->right); - sym->base = rev->pending.objects[basepos].name; - bitmap_unset(map, basepos); /* unmark the base we want */ - sym->warn = basecount > 1; - sym->skip = map; -} - -int cmd_diff(int argc, const char **argv, const char *prefix) -{ - int i; - struct rev_info rev; - struct object_array ent = OBJECT_ARRAY_INIT; - int blobs = 0, paths = 0; - struct object_array_entry *blob[2]; - int nongit = 0, no_index = 0; - int result = 0; - struct symdiff sdiff; - - /* - * We could get N tree-ish in the rev.pending_objects list. - * Also there could be M blobs there, and P pathspecs. --cached may - * also be present. - * - * N=0, M=0: - * cache vs files (diff-files) - * - * N=0, M=0, --cached: - * HEAD vs cache (diff-index --cached) - * - * N=0, M=2: - * compare two random blobs. P must be zero. - * - * N=0, M=1, P=1: - * compare a blob with a working tree file. - * - * N=1, M=0: - * tree vs files (diff-index) - * - * N=1, M=0, --cached: - * tree vs cache (diff-index --cached) - * - * N=2, M=0: - * tree vs tree (diff-tree) - * - * N=0, M=0, P=2: - * compare two filesystem entities (aka --no-index). - * - * Other cases are errors. - */ - - /* Were we asked to do --no-index explicitly? */ - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "--")) { - i++; - break; - } - if (!strcmp(argv[i], "--no-index")) - no_index = DIFF_NO_INDEX_EXPLICIT; - if (argv[i][0] != '-') - break; - } - - prefix = setup_git_directory_gently(&nongit); - - if (!no_index) { - /* - * Treat git diff with at least one path outside of the - * repo the same as if the command would have been executed - * outside of a git repository. In this case it behaves - * the same way as "git diff --no-index <a> <b>", which acts - * as a colourful "diff" replacement. - */ - if (nongit || ((argc == i + 2) && - (!path_inside_repo(prefix, argv[i]) || - !path_inside_repo(prefix, argv[i + 1])))) - no_index = DIFF_NO_INDEX_IMPLICIT; - } - - init_diff_ui_defaults(); - git_config(git_diff_ui_config, NULL); - precompose_argv(argc, argv); - - repo_init_revisions(the_repository, &rev, prefix); - - /* Set up defaults that will apply to both no-index and regular diffs. */ - rev.diffopt.stat_width = -1; - rev.diffopt.stat_graph_width = -1; - rev.diffopt.flags.allow_external = 1; - rev.diffopt.flags.allow_textconv = 1; - - /* If this is a no-index diff, just run it and exit there. */ - if (no_index) - exit(diff_no_index(&rev, no_index == DIFF_NO_INDEX_IMPLICIT, - argc, argv)); - - - /* - * Otherwise, we are doing the usual "git" diff; set up any - * further defaults that apply to regular diffs. - */ - rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; - - /* - * Default to intent-to-add entries invisible in the - * index. This makes them show up as new files in diff-files - * and not at all in diff-cached. - */ - rev.diffopt.ita_invisible_in_index = 1; - - if (nongit) - die(_("Not a git repository")); - argc = setup_revisions(argc, argv, &rev, NULL); - if (!rev.diffopt.output_format) { - rev.diffopt.output_format = DIFF_FORMAT_PATCH; - diff_setup_done(&rev.diffopt); - } - - rev.diffopt.flags.recursive = 1; - - setup_diff_pager(&rev.diffopt); - - /* - * Do we have --cached and not have a pending object, then - * default to HEAD by hand. Eek. - */ - if (!rev.pending.nr) { - int i; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!strcmp(arg, "--")) - break; - else if (!strcmp(arg, "--cached") || - !strcmp(arg, "--staged")) { - add_head_to_pending(&rev); - if (!rev.pending.nr) { - struct tree *tree; - tree = lookup_tree(the_repository, - the_repository->hash_algo->empty_tree); - add_pending_object(&rev, &tree->object, "HEAD"); - } - break; - } - } - } - - symdiff_prepare(&rev, &sdiff); - for (i = 0; i < rev.pending.nr; i++) { - struct object_array_entry *entry = &rev.pending.objects[i]; - struct object *obj = entry->item; - const char *name = entry->name; - int flags = (obj->flags & UNINTERESTING); - if (!obj->parsed) - obj = parse_object(the_repository, &obj->oid); - obj = deref_tag(the_repository, obj, NULL, 0); - if (!obj) - die(_("invalid object '%s' given."), name); - if (obj->type == OBJ_COMMIT) - obj = &get_commit_tree(((struct commit *)obj))->object; - - if (obj->type == OBJ_TREE) { - if (sdiff.skip && bitmap_get(sdiff.skip, i)) - continue; - obj->flags |= flags; - add_object_array(obj, name, &ent); - } else if (obj->type == OBJ_BLOB) { - if (2 <= blobs) - die(_("more than two blobs given: '%s'"), name); - blob[blobs] = entry; - blobs++; - - } else { - die(_("unhandled object '%s' given."), name); - } - } - if (rev.prune_data.nr) - paths += rev.prune_data.nr; - - /* - * Now, do the arguments look reasonable? - */ - if (!ent.nr) { - switch (blobs) { - case 0: - result = builtin_diff_files(&rev, argc, argv); - break; - case 1: - if (paths != 1) - usage(builtin_diff_usage); - result = builtin_diff_b_f(&rev, argc, argv, blob); - break; - case 2: - if (paths) - usage(builtin_diff_usage); - result = builtin_diff_blobs(&rev, argc, argv, blob); - break; - default: - usage(builtin_diff_usage); - } - } - else if (blobs) - usage(builtin_diff_usage); - else if (ent.nr == 1) - result = builtin_diff_index(&rev, argc, argv); - else if (ent.nr == 2) { - if (sdiff.warn) - warning(_("%s...%s: multiple merge bases, using %s"), - sdiff.left, sdiff.right, sdiff.base); - result = builtin_diff_tree(&rev, argc, argv, - &ent.objects[0], &ent.objects[1]); - } else - result = builtin_diff_combined(&rev, argc, argv, - ent.objects, ent.nr); - result = diff_result_code(&rev.diffopt, result); - if (1 < rev.diffopt.skip_stat_unmatch) - refresh_index_quietly(); - UNLEAK(rev); - UNLEAK(ent); - UNLEAK(blob); - return result; -} diff --git a/third_party/git/builtin/difftool.c b/third_party/git/builtin/difftool.c deleted file mode 100644 index 7ac432b88193..000000000000 --- a/third_party/git/builtin/difftool.c +++ /dev/null @@ -1,772 +0,0 @@ -/* - * "git difftool" builtin command - * - * This is a wrapper around the GIT_EXTERNAL_DIFF-compatible - * git-difftool--helper script. - * - * This script exports GIT_EXTERNAL_DIFF and GIT_PAGER for use by git. - * The GIT_DIFF* variables are exported for use by git-difftool--helper. - * - * Any arguments that are unknown to this script are forwarded to 'git diff'. - * - * Copyright (C) 2016 Johannes Schindelin - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "builtin.h" -#include "run-command.h" -#include "exec-cmd.h" -#include "parse-options.h" -#include "strvec.h" -#include "strbuf.h" -#include "lockfile.h" -#include "object-store.h" -#include "dir.h" - -static int trust_exit_code; - -static const char *const builtin_difftool_usage[] = { - N_("git difftool [<options>] [<commit> [<commit>]] [--] [<path>...]"), - NULL -}; - -static int difftool_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "difftool.trustexitcode")) { - trust_exit_code = git_config_bool(var, value); - return 0; - } - - return git_default_config(var, value, cb); -} - -static int print_tool_help(void) -{ - const char *argv[] = { "mergetool", "--tool-help=diff", NULL }; - return run_command_v_opt(argv, RUN_GIT_CMD); -} - -static int parse_index_info(char *p, int *mode1, int *mode2, - struct object_id *oid1, struct object_id *oid2, - char *status) -{ - if (*p != ':') - return error("expected ':', got '%c'", *p); - *mode1 = (int)strtol(p + 1, &p, 8); - if (*p != ' ') - return error("expected ' ', got '%c'", *p); - *mode2 = (int)strtol(p + 1, &p, 8); - if (*p != ' ') - return error("expected ' ', got '%c'", *p); - if (parse_oid_hex(++p, oid1, (const char **)&p)) - return error("expected object ID, got '%s'", p); - if (*p != ' ') - return error("expected ' ', got '%c'", *p); - if (parse_oid_hex(++p, oid2, (const char **)&p)) - return error("expected object ID, got '%s'", p); - if (*p != ' ') - return error("expected ' ', got '%c'", *p); - *status = *++p; - if (!*status) - return error("missing status"); - if (p[1] && !isdigit(p[1])) - return error("unexpected trailer: '%s'", p + 1); - return 0; -} - -/* - * Remove any trailing slash from $workdir - * before starting to avoid double slashes in symlink targets. - */ -static void add_path(struct strbuf *buf, size_t base_len, const char *path) -{ - strbuf_setlen(buf, base_len); - if (buf->len && buf->buf[buf->len - 1] != '/') - strbuf_addch(buf, '/'); - strbuf_addstr(buf, path); -} - -/* - * Determine whether we can simply reuse the file in the worktree. - */ -static int use_wt_file(const char *workdir, const char *name, - struct object_id *oid) -{ - struct strbuf buf = STRBUF_INIT; - struct stat st; - int use = 0; - - strbuf_addstr(&buf, workdir); - add_path(&buf, buf.len, name); - - if (!lstat(buf.buf, &st) && !S_ISLNK(st.st_mode)) { - struct object_id wt_oid; - int fd = open(buf.buf, O_RDONLY); - - if (fd >= 0 && - !index_fd(&the_index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) { - if (is_null_oid(oid)) { - oidcpy(oid, &wt_oid); - use = 1; - } else if (oideq(oid, &wt_oid)) - use = 1; - } - } - - strbuf_release(&buf); - - return use; -} - -struct working_tree_entry { - struct hashmap_entry entry; - char path[FLEX_ARRAY]; -}; - -static int working_tree_entry_cmp(const void *unused_cmp_data, - const struct hashmap_entry *eptr, - const struct hashmap_entry *entry_or_key, - const void *unused_keydata) -{ - const struct working_tree_entry *a, *b; - - a = container_of(eptr, const struct working_tree_entry, entry); - b = container_of(entry_or_key, const struct working_tree_entry, entry); - - return strcmp(a->path, b->path); -} - -/* - * The `left` and `right` entries hold paths for the symlinks hashmap, - * and a SHA-1 surrounded by brief text for submodules. - */ -struct pair_entry { - struct hashmap_entry entry; - char left[PATH_MAX], right[PATH_MAX]; - const char path[FLEX_ARRAY]; -}; - -static int pair_cmp(const void *unused_cmp_data, - const struct hashmap_entry *eptr, - const struct hashmap_entry *entry_or_key, - const void *unused_keydata) -{ - const struct pair_entry *a, *b; - - a = container_of(eptr, const struct pair_entry, entry); - b = container_of(entry_or_key, const struct pair_entry, entry); - - return strcmp(a->path, b->path); -} - -static void add_left_or_right(struct hashmap *map, const char *path, - const char *content, int is_right) -{ - struct pair_entry *e, *existing; - - FLEX_ALLOC_STR(e, path, path); - hashmap_entry_init(&e->entry, strhash(path)); - existing = hashmap_get_entry(map, e, entry, NULL); - if (existing) { - free(e); - e = existing; - } else { - e->left[0] = e->right[0] = '\0'; - hashmap_add(map, &e->entry); - } - strlcpy(is_right ? e->right : e->left, content, PATH_MAX); -} - -struct path_entry { - struct hashmap_entry entry; - char path[FLEX_ARRAY]; -}; - -static int path_entry_cmp(const void *unused_cmp_data, - const struct hashmap_entry *eptr, - const struct hashmap_entry *entry_or_key, - const void *key) -{ - const struct path_entry *a, *b; - - a = container_of(eptr, const struct path_entry, entry); - b = container_of(entry_or_key, const struct path_entry, entry); - - return strcmp(a->path, key ? key : b->path); -} - -static void changed_files(struct hashmap *result, const char *index_path, - const char *workdir) -{ - struct child_process update_index = CHILD_PROCESS_INIT; - struct child_process diff_files = CHILD_PROCESS_INIT; - struct strbuf index_env = STRBUF_INIT, buf = STRBUF_INIT; - const char *git_dir = absolute_path(get_git_dir()), *env[] = { - NULL, NULL - }; - FILE *fp; - - strbuf_addf(&index_env, "GIT_INDEX_FILE=%s", index_path); - env[0] = index_env.buf; - - strvec_pushl(&update_index.args, - "--git-dir", git_dir, "--work-tree", workdir, - "update-index", "--really-refresh", "-q", - "--unmerged", NULL); - update_index.no_stdin = 1; - update_index.no_stdout = 1; - update_index.no_stderr = 1; - update_index.git_cmd = 1; - update_index.use_shell = 0; - update_index.clean_on_exit = 1; - update_index.dir = workdir; - update_index.env = env; - /* Ignore any errors of update-index */ - run_command(&update_index); - - strvec_pushl(&diff_files.args, - "--git-dir", git_dir, "--work-tree", workdir, - "diff-files", "--name-only", "-z", NULL); - diff_files.no_stdin = 1; - diff_files.git_cmd = 1; - diff_files.use_shell = 0; - diff_files.clean_on_exit = 1; - diff_files.out = -1; - diff_files.dir = workdir; - diff_files.env = env; - if (start_command(&diff_files)) - die("could not obtain raw diff"); - fp = xfdopen(diff_files.out, "r"); - while (!strbuf_getline_nul(&buf, fp)) { - struct path_entry *entry; - FLEX_ALLOC_STR(entry, path, buf.buf); - hashmap_entry_init(&entry->entry, strhash(buf.buf)); - hashmap_add(result, &entry->entry); - } - fclose(fp); - if (finish_command(&diff_files)) - die("diff-files did not exit properly"); - strbuf_release(&index_env); - strbuf_release(&buf); -} - -static NORETURN void exit_cleanup(const char *tmpdir, int exit_code) -{ - struct strbuf buf = STRBUF_INIT; - strbuf_addstr(&buf, tmpdir); - remove_dir_recursively(&buf, 0); - if (exit_code) - warning(_("failed: %d"), exit_code); - exit(exit_code); -} - -static int ensure_leading_directories(char *path) -{ - switch (safe_create_leading_directories(path)) { - case SCLD_OK: - case SCLD_EXISTS: - return 0; - default: - return error(_("could not create leading directories " - "of '%s'"), path); - } -} - -/* - * Unconditional writing of a plain regular file is what - * "git difftool --dir-diff" wants to do for symlinks. We are preparing two - * temporary directories to be fed to a Git-unaware tool that knows how to - * show a diff of two directories (e.g. "diff -r A B"). - * - * Because the tool is Git-unaware, if a symbolic link appears in either of - * these temporary directories, it will try to dereference and show the - * difference of the target of the symbolic link, which is not what we want, - * as the goal of the dir-diff mode is to produce an output that is logically - * equivalent to what "git diff" produces. - * - * Most importantly, we want to get textual comparison of the result of the - * readlink(2). get_symlink() provides that---it returns the contents of - * the symlink that gets written to a regular file to force the external tool - * to compare the readlink(2) result as text, even on a filesystem that is - * capable of doing a symbolic link. - */ -static char *get_symlink(const struct object_id *oid, const char *path) -{ - char *data; - if (is_null_oid(oid)) { - /* The symlink is unknown to Git so read from the filesystem */ - struct strbuf link = STRBUF_INIT; - if (has_symlinks) { - if (strbuf_readlink(&link, path, strlen(path))) - die(_("could not read symlink %s"), path); - } else if (strbuf_read_file(&link, path, 128)) - die(_("could not read symlink file %s"), path); - - data = strbuf_detach(&link, NULL); - } else { - enum object_type type; - unsigned long size; - data = read_object_file(oid, &type, &size); - if (!data) - die(_("could not read object %s for symlink %s"), - oid_to_hex(oid), path); - } - - return data; -} - -static int checkout_path(unsigned mode, struct object_id *oid, - const char *path, const struct checkout *state) -{ - struct cache_entry *ce; - int ret; - - ce = make_transient_cache_entry(mode, oid, path, 0); - ret = checkout_entry(ce, state, NULL, NULL); - - discard_cache_entry(ce); - return ret; -} - -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; - char *lbase_dir, *rbase_dir; - size_t ldir_len, rdir_len, wtdir_len; - 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; - 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 = lbase_dir = xstrdup(ldir.buf); - lstate.base_dir_len = ldir.len; - lstate.force = 1; - memset(&rstate, 0, sizeof(rstate)); - rstate.base_dir = rbase_dir = xstrdup(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, working_tree_entry_cmp, NULL, 0); - hashmap_init(&submodules, pair_cmp, NULL, 0); - hashmap_init(&symlinks2, pair_cmp, NULL, 0); - - child.no_stdin = 1; - child.git_cmd = 1; - child.use_shell = 0; - child.clean_on_exit = 1; - child.dir = prefix; - child.out = -1; - strvec_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z", - NULL); - for (i = 0; i < argc; i++) - strvec_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; - - 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; - - i++; - if (status != 'C' && status != 'R') { - dst_path = src_path; - } else { - if (strbuf_getline_nul(&rpath, fp)) - break; - dst_path = rpath.buf; - } - - 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 (oideq(&loid, &roid)) - strbuf_addstr(&buf, "-dirty"); - add_left_or_right(&submodules, dst_path, buf.buf, 1); - continue; - } - - if (S_ISLNK(lmode)) { - char *content = get_symlink(&loid, src_path); - add_left_or_right(&symlinks2, src_path, content, 0); - free(content); - } - - if (S_ISLNK(rmode)) { - char *content = get_symlink(&roid, dst_path); - add_left_or_right(&symlinks2, dst_path, content, 1); - free(content); - } - - if (lmode && status != 'C') { - if (checkout_path(lmode, &loid, src_path, &lstate)) { - ret = error("could not write '%s'", src_path); - goto finish; - } - } - - if (rmode && !S_ISLNK(rmode)) { - struct working_tree_entry *entry; - - /* Avoid duplicate working_tree entries */ - FLEX_ALLOC_STR(entry, path, dst_path); - hashmap_entry_init(&entry->entry, strhash(dst_path)); - if (hashmap_get(&working_tree_dups, &entry->entry, - NULL)) { - free(entry); - continue; - } - hashmap_add(&working_tree_dups, &entry->entry); - - if (!use_wt_file(workdir, dst_path, &roid)) { - if (checkout_path(rmode, &roid, dst_path, - &rstate)) { - ret = error("could not write '%s'", - dst_path); - goto finish; - } - } 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(&wtindex, rmode, &roid, - dst_path, 0, 0); - - add_index_entry(&wtindex, ce2, - ADD_CACHE_JUST_APPEND); - - add_path(&rdir, rdir_len, dst_path); - if (ensure_leading_directories(rdir.buf)) { - ret = error("could not create " - "directory for '%s'", - dst_path); - goto finish; - } - add_path(&wtdir, wtdir_len, 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; - } - } - } - } - } - - fclose(fp); - fp = NULL; - if (finish_command(&child)) { - ret = error("error occurred running diff --raw"); - goto finish; - } - - if (!i) - goto finish; - - /* - * 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_for_each_entry(&submodules, &iter, entry, - entry /* member name */) { - 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_for_each_entry(&symlinks2, &iter, entry, - entry /* member name */) { - 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, path_entry_cmp, NULL, wtindex.cache_nr); - hashmap_init(&tmp_modified, path_entry_cmp, NULL, 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) { - struct lock_file lock = LOCK_INIT; - 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); - 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("%s", ""); - 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: - if (fp) - fclose(fp); - - free(lbase_dir); - free(rbase_dir); - strbuf_release(&ldir); - strbuf_release(&rdir); - strbuf_release(&wtdir); - strbuf_release(&buf); - - return ret; -} - -static int run_file_diff(int prompt, const char *prefix, - int argc, const char **argv) -{ - struct strvec args = STRVEC_INIT; - const char *env[] = { - "GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL, - NULL - }; - int ret = 0, i; - - if (prompt > 0) - env[2] = "GIT_DIFFTOOL_PROMPT=true"; - else if (!prompt) - env[2] = "GIT_DIFFTOOL_NO_PROMPT=true"; - - - strvec_push(&args, "diff"); - for (i = 0; i < argc; i++) - strvec_push(&args, argv[i]); - ret = run_command_v_opt_cd_env(args.v, RUN_GIT_CMD, prefix, env); - exit(ret); -} - -int cmd_difftool(int argc, const char **argv, const char *prefix) -{ - int use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0, - tool_help = 0, no_index = 0; - static char *difftool_cmd = NULL, *extcmd = NULL; - struct option builtin_difftool_options[] = { - OPT_BOOL('g', "gui", &use_gui_tool, - N_("use `diff.guitool` instead of `diff.tool`")), - OPT_BOOL('d', "dir-diff", &dir_diff, - N_("perform a full-directory diff")), - OPT_SET_INT_F('y', "no-prompt", &prompt, - N_("do not prompt before launching a diff tool"), - 0, PARSE_OPT_NONEG), - OPT_SET_INT_F(0, "prompt", &prompt, NULL, - 1, PARSE_OPT_NONEG | PARSE_OPT_HIDDEN), - OPT_BOOL(0, "symlinks", &symlinks, - N_("use symlinks in dir-diff mode")), - OPT_STRING('t', "tool", &difftool_cmd, N_("tool"), - N_("use the specified diff tool")), - OPT_BOOL(0, "tool-help", &tool_help, - N_("print a list of diff tools that may be used with " - "`--tool`")), - OPT_BOOL(0, "trust-exit-code", &trust_exit_code, - N_("make 'git-difftool' exit when an invoked diff " - "tool returns a non - zero exit code")), - OPT_STRING('x', "extcmd", &extcmd, N_("command"), - N_("specify a custom command for viewing diffs")), - OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")), - OPT_END() - }; - - git_config(difftool_config, NULL); - symlinks = has_symlinks; - - argc = parse_options(argc, argv, prefix, builtin_difftool_options, - builtin_difftool_usage, PARSE_OPT_KEEP_UNKNOWN | - PARSE_OPT_KEEP_DASHDASH); - - if (tool_help) - return print_tool_help(); - - if (!no_index && !startup_info->have_repository) - die(_("difftool requires worktree or --no-index")); - - if (!no_index){ - setup_work_tree(); - setenv(GIT_DIR_ENVIRONMENT, absolute_path(get_git_dir()), 1); - setenv(GIT_WORK_TREE_ENVIRONMENT, absolute_path(get_git_work_tree()), 1); - } else if (dir_diff) - die(_("--dir-diff is incompatible with --no-index")); - - if (use_gui_tool + !!difftool_cmd + !!extcmd > 1) - die(_("--gui, --tool and --extcmd are mutually exclusive")); - - if (use_gui_tool) - setenv("GIT_MERGETOOL_GUI", "true", 1); - else if (difftool_cmd) { - if (*difftool_cmd) - setenv("GIT_DIFF_TOOL", difftool_cmd, 1); - else - die(_("no <tool> given for --tool=<tool>")); - } - - if (extcmd) { - if (*extcmd) - setenv("GIT_DIFFTOOL_EXTCMD", extcmd, 1); - else - die(_("no <cmd> given for --extcmd=<cmd>")); - } - - setenv("GIT_DIFFTOOL_TRUST_EXIT_CODE", - trust_exit_code ? "true" : "false", 1); - - /* - * In directory diff mode, 'git-difftool--helper' is called once - * to compare the a / b directories. In file diff mode, 'git diff' - * will invoke a separate instance of 'git-difftool--helper' for - * each file that changed. - */ - if (dir_diff) - return run_dir_diff(extcmd, symlinks, prefix, argc, argv); - return run_file_diff(prompt, prefix, argc, argv); -} diff --git a/third_party/git/builtin/env--helper.c b/third_party/git/builtin/env--helper.c deleted file mode 100644 index 27349098b074..000000000000 --- a/third_party/git/builtin/env--helper.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "builtin.h" -#include "config.h" -#include "parse-options.h" - -static char const * const env__helper_usage[] = { - N_("git env--helper --type=[bool|ulong] <options> <env-var>"), - NULL -}; - -enum cmdmode { - ENV_HELPER_TYPE_BOOL = 1, - ENV_HELPER_TYPE_ULONG -}; - -static int option_parse_type(const struct option *opt, const char *arg, - int unset) -{ - enum cmdmode *cmdmode = opt->value; - - BUG_ON_OPT_NEG(unset); - - if (!strcmp(arg, "bool")) - *cmdmode = ENV_HELPER_TYPE_BOOL; - else if (!strcmp(arg, "ulong")) - *cmdmode = ENV_HELPER_TYPE_ULONG; - else - die(_("unrecognized --type argument, %s"), arg); - - return 0; -} - -int cmd_env__helper(int argc, const char **argv, const char *prefix) -{ - int exit_code = 0; - const char *env_variable = NULL; - const char *env_default = NULL; - int ret; - int ret_int, default_int; - unsigned long ret_ulong, default_ulong; - enum cmdmode cmdmode = 0; - struct option opts[] = { - OPT_CALLBACK_F(0, "type", &cmdmode, N_("type"), - N_("value is given this type"), PARSE_OPT_NONEG, - option_parse_type), - OPT_STRING(0, "default", &env_default, N_("value"), - N_("default for git_env_*(...) to fall back on")), - OPT_BOOL(0, "exit-code", &exit_code, - N_("be quiet only use git_env_*() value as exit code")), - OPT_END(), - }; - - argc = parse_options(argc, argv, prefix, opts, env__helper_usage, - PARSE_OPT_KEEP_UNKNOWN); - if (env_default && !*env_default) - usage_with_options(env__helper_usage, opts); - if (!cmdmode) - usage_with_options(env__helper_usage, opts); - if (argc != 1) - usage_with_options(env__helper_usage, opts); - env_variable = argv[0]; - - switch (cmdmode) { - case ENV_HELPER_TYPE_BOOL: - if (env_default) { - default_int = git_parse_maybe_bool(env_default); - if (default_int == -1) { - error(_("option `--default' expects a boolean value with `--type=bool`, not `%s`"), - env_default); - usage_with_options(env__helper_usage, opts); - } - } else { - default_int = 0; - } - ret_int = git_env_bool(env_variable, default_int); - if (!exit_code) - puts(ret_int ? "true" : "false"); - ret = ret_int; - break; - case ENV_HELPER_TYPE_ULONG: - if (env_default) { - if (!git_parse_ulong(env_default, &default_ulong)) { - error(_("option `--default' expects an unsigned long value with `--type=ulong`, not `%s`"), - env_default); - usage_with_options(env__helper_usage, opts); - } - } else { - default_ulong = 0; - } - ret_ulong = git_env_ulong(env_variable, default_ulong); - if (!exit_code) - printf("%lu\n", ret_ulong); - ret = ret_ulong; - break; - default: - BUG("unknown <type> value"); - break; - } - - return !ret; -} diff --git a/third_party/git/builtin/fast-export.c b/third_party/git/builtin/fast-export.c deleted file mode 100644 index d2e33f500521..000000000000 --- a/third_party/git/builtin/fast-export.c +++ /dev/null @@ -1,1312 +0,0 @@ -/* - * "git fast-export" builtin command - * - * Copyright (C) 2007 Johannes E. Schindelin - */ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "refs.h" -#include "refspec.h" -#include "object-store.h" -#include "commit.h" -#include "object.h" -#include "tag.h" -#include "diff.h" -#include "diffcore.h" -#include "log-tree.h" -#include "revision.h" -#include "decorate.h" -#include "string-list.h" -#include "utf8.h" -#include "parse-options.h" -#include "quote.h" -#include "remote.h" -#include "blob.h" -#include "commit-slab.h" - -static const char *fast_export_usage[] = { - N_("git fast-export [rev-list-opts]"), - NULL -}; - -static int progress; -static enum { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT; -static enum { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT; -static enum { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT; -static int fake_missing_tagger; -static int use_done_feature; -static int no_data; -static int full_tree; -static int reference_excluded_commits; -static int show_original_ids; -static int mark_tags; -static struct string_list extra_refs = STRING_LIST_INIT_NODUP; -static struct string_list tag_refs = STRING_LIST_INIT_NODUP; -static struct refspec refspecs = REFSPEC_INIT_FETCH; -static int anonymize; -static struct hashmap anonymized_seeds; -static struct revision_sources revision_sources; - -static int parse_opt_signed_tag_mode(const struct option *opt, - const char *arg, int unset) -{ - if (unset || !strcmp(arg, "abort")) - signed_tag_mode = SIGNED_TAG_ABORT; - else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore")) - signed_tag_mode = VERBATIM; - else if (!strcmp(arg, "warn")) - signed_tag_mode = WARN; - else if (!strcmp(arg, "warn-strip")) - signed_tag_mode = WARN_STRIP; - else if (!strcmp(arg, "strip")) - signed_tag_mode = STRIP; - else - return error("Unknown signed-tags mode: %s", arg); - return 0; -} - -static int parse_opt_tag_of_filtered_mode(const struct option *opt, - const char *arg, int unset) -{ - if (unset || !strcmp(arg, "abort")) - tag_of_filtered_mode = TAG_FILTERING_ABORT; - else if (!strcmp(arg, "drop")) - tag_of_filtered_mode = DROP; - else if (!strcmp(arg, "rewrite")) - tag_of_filtered_mode = REWRITE; - else - return error("Unknown tag-of-filtered mode: %s", arg); - return 0; -} - -static int parse_opt_reencode_mode(const struct option *opt, - const char *arg, int unset) -{ - if (unset) { - reencode_mode = REENCODE_ABORT; - return 0; - } - - switch (git_parse_maybe_bool(arg)) { - case 0: - reencode_mode = REENCODE_NO; - break; - case 1: - reencode_mode = REENCODE_YES; - break; - default: - if (!strcasecmp(arg, "abort")) - reencode_mode = REENCODE_ABORT; - else - return error("Unknown reencoding mode: %s", arg); - } - - return 0; -} - -static struct decoration idnums; -static uint32_t last_idnum; - -static int has_unshown_parent(struct commit *commit) -{ - struct commit_list *parent; - - for (parent = commit->parents; parent; parent = parent->next) - if (!(parent->item->object.flags & SHOWN) && - !(parent->item->object.flags & UNINTERESTING)) - return 1; - return 0; -} - -struct anonymized_entry { - struct hashmap_entry hash; - const char *anon; - const char orig[FLEX_ARRAY]; -}; - -struct anonymized_entry_key { - struct hashmap_entry hash; - const char *orig; - size_t orig_len; -}; - -static int anonymized_entry_cmp(const void *unused_cmp_data, - const struct hashmap_entry *eptr, - const struct hashmap_entry *entry_or_key, - const void *keydata) -{ - const struct anonymized_entry *a, *b; - - a = container_of(eptr, const struct anonymized_entry, hash); - if (keydata) { - const struct anonymized_entry_key *key = keydata; - int equal = !strncmp(a->orig, key->orig, key->orig_len) && - !a->orig[key->orig_len]; - return !equal; - } - - b = container_of(entry_or_key, const struct anonymized_entry, hash); - return strcmp(a->orig, b->orig); -} - -/* - * Basically keep a cache of X->Y so that we can repeatedly replace - * the same anonymized string with another. The actual generation - * is farmed out to the generate function. - */ -static const char *anonymize_str(struct hashmap *map, - char *(*generate)(void *), - const char *orig, size_t len, - void *data) -{ - struct anonymized_entry_key key; - struct anonymized_entry *ret; - - if (!map->cmpfn) - hashmap_init(map, anonymized_entry_cmp, NULL, 0); - - hashmap_entry_init(&key.hash, memhash(orig, len)); - key.orig = orig; - key.orig_len = len; - - /* First check if it's a token the user configured manually... */ - if (anonymized_seeds.cmpfn) - ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key); - else - ret = NULL; - - /* ...otherwise check if we've already seen it in this context... */ - if (!ret) - ret = hashmap_get_entry(map, &key, hash, &key); - - /* ...and finally generate a new mapping if necessary */ - if (!ret) { - FLEX_ALLOC_MEM(ret, orig, orig, len); - hashmap_entry_init(&ret->hash, key.hash.hash); - ret->anon = generate(data); - hashmap_put(map, &ret->hash); - } - - return ret->anon; -} - -/* - * We anonymize each component of a path individually, - * so that paths a/b and a/c will share a common root. - * The paths are cached via anonymize_mem so that repeated - * lookups for "a" will yield the same value. - */ -static void anonymize_path(struct strbuf *out, const char *path, - struct hashmap *map, - char *(*generate)(void *)) -{ - while (*path) { - const char *end_of_component = strchrnul(path, '/'); - size_t len = end_of_component - path; - const char *c = anonymize_str(map, generate, path, len, NULL); - strbuf_addstr(out, c); - path = end_of_component; - if (*path) - strbuf_addch(out, *path++); - } -} - -static inline void *mark_to_ptr(uint32_t mark) -{ - return (void *)(uintptr_t)mark; -} - -static inline uint32_t ptr_to_mark(void * mark) -{ - return (uint32_t)(uintptr_t)mark; -} - -static inline void mark_object(struct object *object, uint32_t mark) -{ - add_decoration(&idnums, object, mark_to_ptr(mark)); -} - -static inline void mark_next_object(struct object *object) -{ - mark_object(object, ++last_idnum); -} - -static int get_object_mark(struct object *object) -{ - void *decoration = lookup_decoration(&idnums, object); - if (!decoration) - return 0; - return ptr_to_mark(decoration); -} - -static struct commit *rewrite_commit(struct commit *p) -{ - for (;;) { - if (p->parents && p->parents->next) - break; - if (p->object.flags & UNINTERESTING) - break; - if (!(p->object.flags & TREESAME)) - break; - if (!p->parents) - return NULL; - p = p->parents->item; - } - return p; -} - -static void show_progress(void) -{ - static int counter = 0; - if (!progress) - return; - if ((++counter % progress) == 0) - printf("progress %d objects\n", counter); -} - -/* - * Ideally we would want some transformation of the blob data here - * that is unreversible, but would still be the same size and have - * the same data relationship to other blobs (so that we get the same - * delta and packing behavior as the original). But the first and last - * requirements there are probably mutually exclusive, so let's take - * the easy way out for now, and just generate arbitrary content. - * - * There's no need to cache this result with anonymize_mem, since - * we already handle blob content caching with marks. - */ -static char *anonymize_blob(unsigned long *size) -{ - static int counter; - struct strbuf out = STRBUF_INIT; - strbuf_addf(&out, "anonymous blob %d", counter++); - *size = out.len; - return strbuf_detach(&out, NULL); -} - -static void export_blob(const struct object_id *oid) -{ - unsigned long size; - enum object_type type; - char *buf; - struct object *object; - int eaten; - - if (no_data) - return; - - if (is_null_oid(oid)) - return; - - object = lookup_object(the_repository, oid); - if (object && object->flags & SHOWN) - return; - - if (anonymize) { - buf = anonymize_blob(&size); - object = (struct object *)lookup_blob(the_repository, oid); - eaten = 0; - } else { - buf = read_object_file(oid, &type, &size); - if (!buf) - die("could not read blob %s", oid_to_hex(oid)); - if (check_object_signature(the_repository, oid, buf, size, - type_name(type)) < 0) - die("oid mismatch in blob %s", oid_to_hex(oid)); - object = parse_object_buffer(the_repository, oid, type, - size, buf, &eaten); - } - - if (!object) - die("Could not read blob %s", oid_to_hex(oid)); - - mark_next_object(object); - - printf("blob\nmark :%"PRIu32"\n", last_idnum); - if (show_original_ids) - printf("original-oid %s\n", oid_to_hex(oid)); - printf("data %"PRIuMAX"\n", (uintmax_t)size); - if (size && fwrite(buf, size, 1, stdout) != 1) - die_errno("could not write blob '%s'", oid_to_hex(oid)); - printf("\n"); - - show_progress(); - - object->flags |= SHOWN; - if (!eaten) - free(buf); -} - -static int depth_first(const void *a_, const void *b_) -{ - const struct diff_filepair *a = *((const struct diff_filepair **)a_); - const struct diff_filepair *b = *((const struct diff_filepair **)b_); - const char *name_a, *name_b; - int len_a, len_b, len; - int cmp; - - name_a = a->one ? a->one->path : a->two->path; - name_b = b->one ? b->one->path : b->two->path; - - len_a = strlen(name_a); - len_b = strlen(name_b); - len = (len_a < len_b) ? len_a : len_b; - - /* strcmp will sort 'd' before 'd/e', we want 'd/e' before 'd' */ - cmp = memcmp(name_a, name_b, len); - if (cmp) - return cmp; - cmp = len_b - len_a; - if (cmp) - return cmp; - /* - * Move 'R'ename entries last so that all references of the file - * appear in the output before it is renamed (e.g., when a file - * was copied and renamed in the same commit). - */ - return (a->status == 'R') - (b->status == 'R'); -} - -static void print_path_1(const char *path) -{ - int need_quote = quote_c_style(path, NULL, NULL, 0); - if (need_quote) - quote_c_style(path, NULL, stdout, 0); - else if (strchr(path, ' ')) - printf("\"%s\"", path); - else - printf("%s", path); -} - -static char *anonymize_path_component(void *data) -{ - static int counter; - struct strbuf out = STRBUF_INIT; - strbuf_addf(&out, "path%d", counter++); - return strbuf_detach(&out, NULL); -} - -static void print_path(const char *path) -{ - if (!anonymize) - print_path_1(path); - else { - static struct hashmap paths; - static struct strbuf anon = STRBUF_INIT; - - anonymize_path(&anon, path, &paths, anonymize_path_component); - print_path_1(anon.buf); - strbuf_reset(&anon); - } -} - -static char *generate_fake_oid(void *data) -{ - static uint32_t counter = 1; /* avoid null oid */ - const unsigned hashsz = the_hash_algo->rawsz; - struct object_id oid; - char *hex = xmallocz(GIT_MAX_HEXSZ); - - oidclr(&oid); - put_be32(oid.hash + hashsz - 4, counter++); - return oid_to_hex_r(hex, &oid); -} - -static const char *anonymize_oid(const char *oid_hex) -{ - static struct hashmap objs; - size_t len = strlen(oid_hex); - return anonymize_str(&objs, generate_fake_oid, oid_hex, len, NULL); -} - -static void show_filemodify(struct diff_queue_struct *q, - struct diff_options *options, void *data) -{ - int i; - struct string_list *changed = data; - - /* - * Handle files below a directory first, in case they are all deleted - * and the directory changes to a file or symlink. - */ - QSORT(q->queue, q->nr, depth_first); - - for (i = 0; i < q->nr; i++) { - struct diff_filespec *ospec = q->queue[i]->one; - struct diff_filespec *spec = q->queue[i]->two; - - switch (q->queue[i]->status) { - case DIFF_STATUS_DELETED: - printf("D "); - print_path(spec->path); - string_list_insert(changed, spec->path); - putchar('\n'); - break; - - case DIFF_STATUS_COPIED: - case DIFF_STATUS_RENAMED: - /* - * If a change in the file corresponding to ospec->path - * has been observed, we cannot trust its contents - * because the diff is calculated based on the prior - * contents, not the current contents. So, declare a - * copy or rename only if there was no change observed. - */ - if (!string_list_has_string(changed, ospec->path)) { - printf("%c ", q->queue[i]->status); - print_path(ospec->path); - putchar(' '); - print_path(spec->path); - string_list_insert(changed, spec->path); - putchar('\n'); - - if (oideq(&ospec->oid, &spec->oid) && - ospec->mode == spec->mode) - break; - } - /* fallthrough */ - - case DIFF_STATUS_TYPE_CHANGED: - case DIFF_STATUS_MODIFIED: - case DIFF_STATUS_ADDED: - /* - * Links refer to objects in another repositories; - * output the SHA-1 verbatim. - */ - if (no_data || S_ISGITLINK(spec->mode)) - printf("M %06o %s ", spec->mode, - anonymize ? - anonymize_oid(oid_to_hex(&spec->oid)) : - oid_to_hex(&spec->oid)); - else { - struct object *object = lookup_object(the_repository, - &spec->oid); - printf("M %06o :%d ", spec->mode, - get_object_mark(object)); - } - print_path(spec->path); - string_list_insert(changed, spec->path); - putchar('\n'); - break; - - default: - die("Unexpected comparison status '%c' for %s, %s", - q->queue[i]->status, - ospec->path ? ospec->path : "none", - spec->path ? spec->path : "none"); - } - } -} - -static const char *find_encoding(const char *begin, const char *end) -{ - const char *needle = "\nencoding "; - char *bol, *eol; - - bol = memmem(begin, end ? end - begin : strlen(begin), - needle, strlen(needle)); - if (!bol) - return NULL; - bol += strlen(needle); - eol = strchrnul(bol, '\n'); - *eol = '\0'; - return bol; -} - -static char *anonymize_ref_component(void *data) -{ - static int counter; - struct strbuf out = STRBUF_INIT; - strbuf_addf(&out, "ref%d", counter++); - return strbuf_detach(&out, NULL); -} - -static const char *anonymize_refname(const char *refname) -{ - /* - * If any of these prefixes is found, we will leave it intact - * so that tags remain tags and so forth. - */ - static const char *prefixes[] = { - "refs/heads/", - "refs/tags/", - "refs/remotes/", - "refs/" - }; - static struct hashmap refs; - static struct strbuf anon = STRBUF_INIT; - int i; - - strbuf_reset(&anon); - for (i = 0; i < ARRAY_SIZE(prefixes); i++) { - if (skip_prefix(refname, prefixes[i], &refname)) { - strbuf_addstr(&anon, prefixes[i]); - break; - } - } - - anonymize_path(&anon, refname, &refs, anonymize_ref_component); - return anon.buf; -} - -/* - * We do not even bother to cache commit messages, as they are unlikely - * to be repeated verbatim, and it is not that interesting when they are. - */ -static char *anonymize_commit_message(const char *old) -{ - static int counter; - return xstrfmt("subject %d\n\nbody\n", counter++); -} - -static char *anonymize_ident(void *data) -{ - static int counter; - struct strbuf out = STRBUF_INIT; - strbuf_addf(&out, "User %d <user%d@example.com>", counter, counter); - counter++; - return strbuf_detach(&out, NULL); -} - -/* - * Our strategy here is to anonymize the names and email addresses, - * but keep timestamps intact, as they influence things like traversal - * order (and by themselves should not be too revealing). - */ -static void anonymize_ident_line(const char **beg, const char **end) -{ - static struct hashmap idents; - static struct strbuf buffers[] = { STRBUF_INIT, STRBUF_INIT }; - static unsigned which_buffer; - - struct strbuf *out; - struct ident_split split; - const char *end_of_header; - - out = &buffers[which_buffer++]; - which_buffer %= ARRAY_SIZE(buffers); - strbuf_reset(out); - - /* skip "committer", "author", "tagger", etc */ - end_of_header = strchr(*beg, ' '); - if (!end_of_header) - BUG("malformed line fed to anonymize_ident_line: %.*s", - (int)(*end - *beg), *beg); - end_of_header++; - strbuf_add(out, *beg, end_of_header - *beg); - - if (!split_ident_line(&split, end_of_header, *end - end_of_header) && - split.date_begin) { - const char *ident; - size_t len; - - len = split.mail_end - split.name_begin; - ident = anonymize_str(&idents, anonymize_ident, - split.name_begin, len, NULL); - strbuf_addstr(out, ident); - strbuf_addch(out, ' '); - strbuf_add(out, split.date_begin, split.tz_end - split.date_begin); - } else { - strbuf_addstr(out, "Malformed Ident <malformed@example.com> 0 -0000"); - } - - *beg = out->buf; - *end = out->buf + out->len; -} - -static void handle_commit(struct commit *commit, struct rev_info *rev, - struct string_list *paths_of_changed_objects) -{ - int saved_output_format = rev->diffopt.output_format; - const char *commit_buffer; - const char *author, *author_end, *committer, *committer_end; - const char *encoding, *message; - char *reencoded = NULL; - struct commit_list *p; - const char *refname; - int i; - - rev->diffopt.output_format = DIFF_FORMAT_CALLBACK; - - parse_commit_or_die(commit); - commit_buffer = get_commit_buffer(commit, NULL); - author = strstr(commit_buffer, "\nauthor "); - if (!author) - die("could not find author in commit %s", - oid_to_hex(&commit->object.oid)); - author++; - author_end = strchrnul(author, '\n'); - committer = strstr(author_end, "\ncommitter "); - if (!committer) - die("could not find committer in commit %s", - oid_to_hex(&commit->object.oid)); - committer++; - committer_end = strchrnul(committer, '\n'); - message = strstr(committer_end, "\n\n"); - encoding = find_encoding(committer_end, message); - if (message) - message += 2; - - if (commit->parents && - (get_object_mark(&commit->parents->item->object) != 0 || - reference_excluded_commits) && - !full_tree) { - parse_commit_or_die(commit->parents->item); - diff_tree_oid(get_commit_tree_oid(commit->parents->item), - get_commit_tree_oid(commit), "", &rev->diffopt); - } - else - diff_root_tree_oid(get_commit_tree_oid(commit), - "", &rev->diffopt); - - /* Export the referenced blobs, and remember the marks. */ - for (i = 0; i < diff_queued_diff.nr; i++) - if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode)) - export_blob(&diff_queued_diff.queue[i]->two->oid); - - refname = *revision_sources_at(&revision_sources, commit); - /* - * FIXME: string_list_remove() below for each ref is overall - * O(N^2). Compared to a history walk and diffing trees, this is - * just lost in the noise in practice. However, theoretically a - * repo may have enough refs for this to become slow. - */ - string_list_remove(&extra_refs, refname, 0); - if (anonymize) { - refname = anonymize_refname(refname); - anonymize_ident_line(&committer, &committer_end); - anonymize_ident_line(&author, &author_end); - } - - mark_next_object(&commit->object); - if (anonymize) { - reencoded = anonymize_commit_message(message); - } else if (encoding) { - switch(reencode_mode) { - case REENCODE_YES: - reencoded = reencode_string(message, "UTF-8", encoding); - break; - case REENCODE_NO: - break; - case REENCODE_ABORT: - die("Encountered commit-specific encoding %s in commit " - "%s; use --reencode=[yes|no] to handle it", - encoding, oid_to_hex(&commit->object.oid)); - } - } - if (!commit->parents) - printf("reset %s\n", refname); - printf("commit %s\nmark :%"PRIu32"\n", refname, last_idnum); - if (show_original_ids) - printf("original-oid %s\n", oid_to_hex(&commit->object.oid)); - printf("%.*s\n%.*s\n", - (int)(author_end - author), author, - (int)(committer_end - committer), committer); - if (!reencoded && encoding) - printf("encoding %s\n", encoding); - printf("data %u\n%s", - (unsigned)(reencoded - ? strlen(reencoded) : message - ? strlen(message) : 0), - reencoded ? reencoded : message ? message : ""); - free(reencoded); - unuse_commit_buffer(commit, commit_buffer); - - for (i = 0, p = commit->parents; p; p = p->next) { - struct object *obj = &p->item->object; - int mark = get_object_mark(obj); - - if (!mark && !reference_excluded_commits) - continue; - if (i == 0) - printf("from "); - else - printf("merge "); - if (mark) - printf(":%d\n", mark); - else - printf("%s\n", - anonymize ? - anonymize_oid(oid_to_hex(&obj->oid)) : - oid_to_hex(&obj->oid)); - i++; - } - - if (full_tree) - printf("deleteall\n"); - log_tree_diff_flush(rev); - string_list_clear(paths_of_changed_objects, 0); - rev->diffopt.output_format = saved_output_format; - - printf("\n"); - - show_progress(); -} - -static char *anonymize_tag(void *data) -{ - static int counter; - struct strbuf out = STRBUF_INIT; - strbuf_addf(&out, "tag message %d", counter++); - return strbuf_detach(&out, NULL); -} - -static void handle_tail(struct object_array *commits, struct rev_info *revs, - struct string_list *paths_of_changed_objects) -{ - struct commit *commit; - while (commits->nr) { - commit = (struct commit *)object_array_pop(commits); - if (has_unshown_parent(commit)) { - /* Queue again, to be handled later */ - add_object_array(&commit->object, NULL, commits); - return; - } - handle_commit(commit, revs, paths_of_changed_objects); - } -} - -static void handle_tag(const char *name, struct tag *tag) -{ - unsigned long size; - enum object_type type; - char *buf; - const char *tagger, *tagger_end, *message; - size_t message_size = 0; - struct object *tagged; - int tagged_mark; - struct commit *p; - - /* Trees have no identifier in fast-export output, thus we have no way - * to output tags of trees, tags of tags of trees, etc. Simply omit - * such tags. - */ - tagged = tag->tagged; - while (tagged->type == OBJ_TAG) { - tagged = ((struct tag *)tagged)->tagged; - } - if (tagged->type == OBJ_TREE) { - warning("Omitting tag %s,\nsince tags of trees (or tags of tags of trees, etc.) are not supported.", - oid_to_hex(&tag->object.oid)); - return; - } - - buf = read_object_file(&tag->object.oid, &type, &size); - if (!buf) - die("could not read tag %s", oid_to_hex(&tag->object.oid)); - message = memmem(buf, size, "\n\n", 2); - if (message) { - message += 2; - message_size = strlen(message); - } - tagger = memmem(buf, message ? message - buf : size, "\ntagger ", 8); - if (!tagger) { - if (fake_missing_tagger) - tagger = "tagger Unspecified Tagger " - "<unspecified-tagger> 0 +0000"; - else - tagger = ""; - tagger_end = tagger + strlen(tagger); - } else { - tagger++; - tagger_end = strchrnul(tagger, '\n'); - if (anonymize) - anonymize_ident_line(&tagger, &tagger_end); - } - - if (anonymize) { - name = anonymize_refname(name); - if (message) { - static struct hashmap tags; - message = anonymize_str(&tags, anonymize_tag, - message, message_size, NULL); - } - } - - /* handle signed tags */ - if (message) { - const char *signature = strstr(message, - "\n-----BEGIN PGP SIGNATURE-----\n"); - if (signature) - switch(signed_tag_mode) { - case SIGNED_TAG_ABORT: - die("encountered signed tag %s; use " - "--signed-tags=<mode> to handle it", - oid_to_hex(&tag->object.oid)); - case WARN: - warning("exporting signed tag %s", - oid_to_hex(&tag->object.oid)); - /* fallthru */ - case VERBATIM: - break; - case WARN_STRIP: - warning("stripping signature from tag %s", - oid_to_hex(&tag->object.oid)); - /* fallthru */ - case STRIP: - message_size = signature + 1 - message; - break; - } - } - - /* handle tag->tagged having been filtered out due to paths specified */ - tagged = tag->tagged; - tagged_mark = get_object_mark(tagged); - if (!tagged_mark) { - switch(tag_of_filtered_mode) { - case TAG_FILTERING_ABORT: - die("tag %s tags unexported object; use " - "--tag-of-filtered-object=<mode> to handle it", - oid_to_hex(&tag->object.oid)); - case DROP: - /* Ignore this tag altogether */ - free(buf); - return; - case REWRITE: - if (tagged->type == OBJ_TAG && !mark_tags) { - die(_("Error: Cannot export nested tags unless --mark-tags is specified.")); - } else if (tagged->type == OBJ_COMMIT) { - p = rewrite_commit((struct commit *)tagged); - if (!p) { - printf("reset %s\nfrom %s\n\n", - name, oid_to_hex(&null_oid)); - free(buf); - return; - } - tagged_mark = get_object_mark(&p->object); - } else { - /* tagged->type is either OBJ_BLOB or OBJ_TAG */ - tagged_mark = get_object_mark(tagged); - } - } - } - - if (tagged->type == OBJ_TAG) { - printf("reset %s\nfrom %s\n\n", - name, oid_to_hex(&null_oid)); - } - skip_prefix(name, "refs/tags/", &name); - printf("tag %s\n", name); - if (mark_tags) { - mark_next_object(&tag->object); - printf("mark :%"PRIu32"\n", last_idnum); - } - if (tagged_mark) - printf("from :%d\n", tagged_mark); - else - printf("from %s\n", oid_to_hex(&tagged->oid)); - - if (show_original_ids) - printf("original-oid %s\n", oid_to_hex(&tag->object.oid)); - printf("%.*s%sdata %d\n%.*s\n", - (int)(tagger_end - tagger), tagger, - tagger == tagger_end ? "" : "\n", - (int)message_size, (int)message_size, message ? message : ""); - free(buf); -} - -static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name) -{ - switch (e->item->type) { - case OBJ_COMMIT: - return (struct commit *)e->item; - case OBJ_TAG: { - struct tag *tag = (struct tag *)e->item; - - /* handle nested tags */ - while (tag && tag->object.type == OBJ_TAG) { - parse_object(the_repository, &tag->object.oid); - string_list_append(&tag_refs, full_name)->util = tag; - tag = (struct tag *)tag->tagged; - } - if (!tag) - die("Tag %s points nowhere?", e->name); - return (struct commit *)tag; - break; - } - default: - return NULL; - } -} - -static void get_tags_and_duplicates(struct rev_cmdline_info *info) -{ - int i; - - for (i = 0; i < info->nr; i++) { - struct rev_cmdline_entry *e = info->rev + i; - struct object_id oid; - struct commit *commit; - char *full_name; - - if (e->flags & UNINTERESTING) - continue; - - if (dwim_ref(e->name, strlen(e->name), &oid, &full_name, 0) != 1) - continue; - - if (refspecs.nr) { - char *private; - private = apply_refspecs(&refspecs, full_name); - if (private) { - free(full_name); - full_name = private; - } - } - - commit = get_commit(e, full_name); - if (!commit) { - warning("%s: Unexpected object of type %s, skipping.", - e->name, - type_name(e->item->type)); - continue; - } - - switch(commit->object.type) { - case OBJ_COMMIT: - break; - case OBJ_BLOB: - export_blob(&commit->object.oid); - continue; - default: /* OBJ_TAG (nested tags) is already handled */ - warning("Tag points to object of unexpected type %s, skipping.", - type_name(commit->object.type)); - continue; - } - - /* - * Make sure this ref gets properly updated eventually, whether - * through a commit or manually at the end. - */ - if (e->item->type != OBJ_TAG) - string_list_append(&extra_refs, full_name)->util = commit; - - if (!*revision_sources_at(&revision_sources, commit)) - *revision_sources_at(&revision_sources, commit) = full_name; - } - - string_list_sort(&extra_refs); - string_list_remove_duplicates(&extra_refs, 0); -} - -static void handle_tags_and_duplicates(struct string_list *extras) -{ - struct commit *commit; - int i; - - for (i = extras->nr - 1; i >= 0; i--) { - const char *name = extras->items[i].string; - struct object *object = extras->items[i].util; - int mark; - - switch (object->type) { - case OBJ_TAG: - handle_tag(name, (struct tag *)object); - break; - case OBJ_COMMIT: - if (anonymize) - name = anonymize_refname(name); - /* create refs pointing to already seen commits */ - commit = rewrite_commit((struct commit *)object); - if (!commit) { - /* - * Neither this object nor any of its - * ancestors touch any relevant paths, so - * it has been filtered to nothing. Delete - * it. - */ - printf("reset %s\nfrom %s\n\n", - name, oid_to_hex(&null_oid)); - continue; - } - - mark = get_object_mark(&commit->object); - if (!mark) { - /* - * Getting here means we have a commit which - * was excluded by a negative refspec (e.g. - * fast-export ^HEAD HEAD). If we are - * referencing excluded commits, set the ref - * to the exact commit. Otherwise, the user - * wants the branch exported but every commit - * in its history to be deleted, which basically - * just means deletion of the ref. - */ - if (!reference_excluded_commits) { - /* delete the ref */ - printf("reset %s\nfrom %s\n\n", - name, oid_to_hex(&null_oid)); - continue; - } - /* set ref to commit using oid, not mark */ - printf("reset %s\nfrom %s\n\n", name, - oid_to_hex(&commit->object.oid)); - continue; - } - - printf("reset %s\nfrom :%d\n\n", name, mark - ); - show_progress(); - break; - } - } -} - -static void export_marks(char *file) -{ - unsigned int i; - uint32_t mark; - struct decoration_entry *deco = idnums.entries; - FILE *f; - int e = 0; - - f = fopen_for_writing(file); - if (!f) - die_errno("Unable to open marks file %s for writing.", file); - - for (i = 0; i < idnums.size; i++) { - if (deco->base && deco->base->type == 1) { - mark = ptr_to_mark(deco->decoration); - if (fprintf(f, ":%"PRIu32" %s\n", mark, - oid_to_hex(&deco->base->oid)) < 0) { - e = 1; - break; - } - } - deco++; - } - - e |= ferror(f); - e |= fclose(f); - if (e) - error("Unable to write marks file %s.", file); -} - -static void import_marks(char *input_file, int check_exists) -{ - char line[512]; - FILE *f; - struct stat sb; - - if (check_exists && stat(input_file, &sb)) - return; - - f = xfopen(input_file, "r"); - while (fgets(line, sizeof(line), f)) { - uint32_t mark; - char *line_end, *mark_end; - struct object_id oid; - struct object *object; - struct commit *commit; - enum object_type type; - - line_end = strchr(line, '\n'); - if (line[0] != ':' || !line_end) - die("corrupt mark line: %s", line); - *line_end = '\0'; - - mark = strtoumax(line + 1, &mark_end, 10); - if (!mark || mark_end == line + 1 - || *mark_end != ' ' || get_oid_hex(mark_end + 1, &oid)) - die("corrupt mark line: %s", line); - - if (last_idnum < mark) - last_idnum = mark; - - type = oid_object_info(the_repository, &oid, NULL); - if (type < 0) - die("object not found: %s", oid_to_hex(&oid)); - - if (type != OBJ_COMMIT) - /* only commits */ - continue; - - commit = lookup_commit(the_repository, &oid); - if (!commit) - die("not a commit? can't happen: %s", oid_to_hex(&oid)); - - object = &commit->object; - - if (object->flags & SHOWN) - error("Object %s already has a mark", oid_to_hex(&oid)); - - mark_object(object, mark); - - object->flags |= SHOWN; - } - fclose(f); -} - -static void handle_deletes(void) -{ - int i; - for (i = 0; i < refspecs.nr; i++) { - struct refspec_item *refspec = &refspecs.items[i]; - if (*refspec->src) - continue; - - printf("reset %s\nfrom %s\n\n", - refspec->dst, oid_to_hex(&null_oid)); - } -} - -static char *anonymize_seed(void *data) -{ - return xstrdup(data); -} - -static int parse_opt_anonymize_map(const struct option *opt, - const char *arg, int unset) -{ - struct hashmap *map = opt->value; - const char *delim, *value; - size_t keylen; - - BUG_ON_OPT_NEG(unset); - - delim = strchr(arg, ':'); - if (delim) { - keylen = delim - arg; - value = delim + 1; - } else { - keylen = strlen(arg); - value = arg; - } - - if (!keylen || !*value) - return error(_("--anonymize-map token cannot be empty")); - - anonymize_str(map, anonymize_seed, arg, keylen, (void *)value); - - return 0; -} - -int cmd_fast_export(int argc, const char **argv, const char *prefix) -{ - struct rev_info revs; - struct object_array commits = OBJECT_ARRAY_INIT; - struct commit *commit; - char *export_filename = NULL, - *import_filename = NULL, - *import_filename_if_exists = NULL; - uint32_t lastimportid; - struct string_list refspecs_list = STRING_LIST_INIT_NODUP; - struct string_list paths_of_changed_objects = STRING_LIST_INIT_DUP; - struct option options[] = { - OPT_INTEGER(0, "progress", &progress, - N_("show progress after <n> objects")), - OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, N_("mode"), - N_("select handling of signed tags"), - parse_opt_signed_tag_mode), - OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, N_("mode"), - N_("select handling of tags that tag filtered objects"), - parse_opt_tag_of_filtered_mode), - OPT_CALLBACK(0, "reencode", &reencode_mode, N_("mode"), - N_("select handling of commit messages in an alternate encoding"), - parse_opt_reencode_mode), - OPT_STRING(0, "export-marks", &export_filename, N_("file"), - N_("Dump marks to this file")), - OPT_STRING(0, "import-marks", &import_filename, N_("file"), - N_("Import marks from this file")), - OPT_STRING(0, "import-marks-if-exists", - &import_filename_if_exists, - N_("file"), - N_("Import marks from this file if it exists")), - OPT_BOOL(0, "fake-missing-tagger", &fake_missing_tagger, - N_("Fake a tagger when tags lack one")), - OPT_BOOL(0, "full-tree", &full_tree, - N_("Output full tree for each commit")), - OPT_BOOL(0, "use-done-feature", &use_done_feature, - N_("Use the done feature to terminate the stream")), - OPT_BOOL(0, "no-data", &no_data, N_("Skip output of blob data")), - OPT_STRING_LIST(0, "refspec", &refspecs_list, N_("refspec"), - N_("Apply refspec to exported refs")), - OPT_BOOL(0, "anonymize", &anonymize, N_("anonymize output")), - OPT_CALLBACK_F(0, "anonymize-map", &anonymized_seeds, N_("from:to"), - N_("convert <from> to <to> in anonymized output"), - PARSE_OPT_NONEG, parse_opt_anonymize_map), - OPT_BOOL(0, "reference-excluded-parents", - &reference_excluded_commits, N_("Reference parents which are not in fast-export stream by object id")), - OPT_BOOL(0, "show-original-ids", &show_original_ids, - N_("Show original object ids of blobs/commits")), - OPT_BOOL(0, "mark-tags", &mark_tags, - N_("Label tags with mark ids")), - - OPT_END() - }; - - if (argc == 1) - usage_with_options (fast_export_usage, options); - - /* we handle encodings */ - git_config(git_default_config, NULL); - - repo_init_revisions(the_repository, &revs, prefix); - init_revision_sources(&revision_sources); - revs.topo_order = 1; - revs.sources = &revision_sources; - revs.rewrite_parents = 1; - argc = parse_options(argc, argv, prefix, options, fast_export_usage, - PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN); - argc = setup_revisions(argc, argv, &revs, NULL); - if (argc > 1) - usage_with_options (fast_export_usage, options); - - if (anonymized_seeds.cmpfn && !anonymize) - die(_("--anonymize-map without --anonymize does not make sense")); - - if (refspecs_list.nr) { - int i; - - for (i = 0; i < refspecs_list.nr; i++) - refspec_append(&refspecs, refspecs_list.items[i].string); - - string_list_clear(&refspecs_list, 1); - } - - if (use_done_feature) - printf("feature done\n"); - - if (import_filename && import_filename_if_exists) - die(_("Cannot pass both --import-marks and --import-marks-if-exists")); - if (import_filename) - import_marks(import_filename, 0); - else if (import_filename_if_exists) - import_marks(import_filename_if_exists, 1); - lastimportid = last_idnum; - - if (import_filename && revs.prune_data.nr) - full_tree = 1; - - get_tags_and_duplicates(&revs.cmdline); - - if (prepare_revision_walk(&revs)) - die("revision walk setup failed"); - revs.diffopt.format_callback = show_filemodify; - revs.diffopt.format_callback_data = &paths_of_changed_objects; - revs.diffopt.flags.recursive = 1; - while ((commit = get_revision(&revs))) { - if (has_unshown_parent(commit)) { - add_object_array(&commit->object, NULL, &commits); - } - else { - handle_commit(commit, &revs, &paths_of_changed_objects); - handle_tail(&commits, &revs, &paths_of_changed_objects); - } - } - - handle_tags_and_duplicates(&extra_refs); - handle_tags_and_duplicates(&tag_refs); - handle_deletes(); - - if (export_filename && lastimportid != last_idnum) - export_marks(export_filename); - - if (use_done_feature) - printf("done\n"); - - refspec_clear(&refspecs); - - return 0; -} diff --git a/third_party/git/builtin/fast-import.c b/third_party/git/builtin/fast-import.c deleted file mode 100644 index 1bf50a73dc35..000000000000 --- a/third_party/git/builtin/fast-import.c +++ /dev/null @@ -1,3634 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "repository.h" -#include "config.h" -#include "lockfile.h" -#include "object.h" -#include "blob.h" -#include "tree.h" -#include "commit.h" -#include "delta.h" -#include "pack.h" -#include "refs.h" -#include "csum-file.h" -#include "quote.h" -#include "dir.h" -#include "run-command.h" -#include "packfile.h" -#include "object-store.h" -#include "mem-pool.h" -#include "commit-reach.h" -#include "khash.h" - -#define PACK_ID_BITS 16 -#define MAX_PACK_ID ((1<<PACK_ID_BITS)-1) -#define DEPTH_BITS 13 -#define MAX_DEPTH ((1<<DEPTH_BITS)-1) - -/* - * We abuse the setuid bit on directories to mean "do not delta". - */ -#define NO_DELTA S_ISUID - -/* - * The amount of additional space required in order to write an object into the - * current pack. This is the hash lengths at the end of the pack, plus the - * length of one object ID. - */ -#define PACK_SIZE_THRESHOLD (the_hash_algo->rawsz * 3) - -struct object_entry { - struct pack_idx_entry idx; - struct hashmap_entry ent; - uint32_t type : TYPE_BITS, - pack_id : PACK_ID_BITS, - depth : DEPTH_BITS; -}; - -static int object_entry_hashcmp(const void *map_data, - const struct hashmap_entry *eptr, - const struct hashmap_entry *entry_or_key, - const void *keydata) -{ - const struct object_id *oid = keydata; - const struct object_entry *e1, *e2; - - e1 = container_of(eptr, const struct object_entry, ent); - if (oid) - return oidcmp(&e1->idx.oid, oid); - - e2 = container_of(entry_or_key, const struct object_entry, ent); - return oidcmp(&e1->idx.oid, &e2->idx.oid); -} - -struct object_entry_pool { - struct object_entry_pool *next_pool; - struct object_entry *next_free; - struct object_entry *end; - struct object_entry entries[FLEX_ARRAY]; /* more */ -}; - -struct mark_set { - union { - struct object_id *oids[1024]; - struct object_entry *marked[1024]; - struct mark_set *sets[1024]; - } data; - unsigned int shift; -}; - -struct last_object { - struct strbuf data; - off_t offset; - unsigned int depth; - unsigned no_swap : 1; -}; - -struct atom_str { - struct atom_str *next_atom; - unsigned short str_len; - char str_dat[FLEX_ARRAY]; /* more */ -}; - -struct tree_content; -struct tree_entry { - struct tree_content *tree; - struct atom_str *name; - struct tree_entry_ms { - uint16_t mode; - struct object_id oid; - } versions[2]; -}; - -struct tree_content { - unsigned int entry_capacity; /* must match avail_tree_content */ - unsigned int entry_count; - unsigned int delta_depth; - struct tree_entry *entries[FLEX_ARRAY]; /* more */ -}; - -struct avail_tree_content { - unsigned int entry_capacity; /* must match tree_content */ - struct avail_tree_content *next_avail; -}; - -struct branch { - struct branch *table_next_branch; - struct branch *active_next_branch; - const char *name; - struct tree_entry branch_tree; - uintmax_t last_commit; - uintmax_t num_notes; - unsigned active : 1; - unsigned delete : 1; - unsigned pack_id : PACK_ID_BITS; - struct object_id oid; -}; - -struct tag { - struct tag *next_tag; - const char *name; - unsigned int pack_id; - struct object_id oid; -}; - -struct hash_list { - struct hash_list *next; - struct object_id oid; -}; - -typedef enum { - WHENSPEC_RAW = 1, - WHENSPEC_RAW_PERMISSIVE, - WHENSPEC_RFC2822, - WHENSPEC_NOW -} whenspec_type; - -struct recent_command { - struct recent_command *prev; - struct recent_command *next; - char *buf; -}; - -typedef void (*mark_set_inserter_t)(struct mark_set *s, struct object_id *oid, uintmax_t mark); -typedef void (*each_mark_fn_t)(uintmax_t mark, void *obj, void *cbp); - -/* Configured limits on output */ -static unsigned long max_depth = 50; -static off_t max_packsize; -static int unpack_limit = 100; -static int force_update; - -/* Stats and misc. counters */ -static uintmax_t alloc_count; -static uintmax_t marks_set_count; -static uintmax_t object_count_by_type[1 << TYPE_BITS]; -static uintmax_t duplicate_count_by_type[1 << TYPE_BITS]; -static uintmax_t delta_count_by_type[1 << TYPE_BITS]; -static uintmax_t delta_count_attempts_by_type[1 << TYPE_BITS]; -static unsigned long object_count; -static unsigned long branch_count; -static unsigned long branch_load_count; -static int failure; -static FILE *pack_edges; -static unsigned int show_stats = 1; -static int global_argc; -static const char **global_argv; - -/* Memory pools */ -static struct mem_pool fi_mem_pool = {NULL, 2*1024*1024 - - sizeof(struct mp_block), 0 }; - -/* Atom management */ -static unsigned int atom_table_sz = 4451; -static unsigned int atom_cnt; -static struct atom_str **atom_table; - -/* The .pack file being generated */ -static struct pack_idx_option pack_idx_opts; -static unsigned int pack_id; -static struct hashfile *pack_file; -static struct packed_git *pack_data; -static struct packed_git **all_packs; -static off_t pack_size; - -/* Table of objects we've written. */ -static unsigned int object_entry_alloc = 5000; -static struct object_entry_pool *blocks; -static struct hashmap object_table; -static struct mark_set *marks; -static const char *export_marks_file; -static const char *import_marks_file; -static int import_marks_file_from_stream; -static int import_marks_file_ignore_missing; -static int import_marks_file_done; -static int relative_marks_paths; - -/* Our last blob */ -static struct last_object last_blob = { STRBUF_INIT, 0, 0, 0 }; - -/* Tree management */ -static unsigned int tree_entry_alloc = 1000; -static void *avail_tree_entry; -static unsigned int avail_tree_table_sz = 100; -static struct avail_tree_content **avail_tree_table; -static size_t tree_entry_allocd; -static struct strbuf old_tree = STRBUF_INIT; -static struct strbuf new_tree = STRBUF_INIT; - -/* Branch data */ -static unsigned long max_active_branches = 5; -static unsigned long cur_active_branches; -static unsigned long branch_table_sz = 1039; -static struct branch **branch_table; -static struct branch *active_branches; - -/* Tag data */ -static struct tag *first_tag; -static struct tag *last_tag; - -/* Input stream parsing */ -static whenspec_type whenspec = WHENSPEC_RAW; -static struct strbuf command_buf = STRBUF_INIT; -static int unread_command_buf; -static struct recent_command cmd_hist = {&cmd_hist, &cmd_hist, NULL}; -static struct recent_command *cmd_tail = &cmd_hist; -static struct recent_command *rc_free; -static unsigned int cmd_save = 100; -static uintmax_t next_mark; -static struct strbuf new_data = STRBUF_INIT; -static int seen_data_command; -static int require_explicit_termination; -static int allow_unsafe_features; - -/* Signal handling */ -static volatile sig_atomic_t checkpoint_requested; - -/* Submodule marks */ -static struct string_list sub_marks_from = STRING_LIST_INIT_DUP; -static struct string_list sub_marks_to = STRING_LIST_INIT_DUP; -static kh_oid_map_t *sub_oid_map; - -/* Where to write output of cat-blob commands */ -static int cat_blob_fd = STDOUT_FILENO; - -static void parse_argv(void); -static void parse_get_mark(const char *p); -static void parse_cat_blob(const char *p); -static void parse_ls(const char *p, struct branch *b); - -static void for_each_mark(struct mark_set *m, uintmax_t base, each_mark_fn_t callback, void *p) -{ - uintmax_t k; - if (m->shift) { - for (k = 0; k < 1024; k++) { - if (m->data.sets[k]) - for_each_mark(m->data.sets[k], base + (k << m->shift), callback, p); - } - } else { - for (k = 0; k < 1024; k++) { - if (m->data.marked[k]) - callback(base + k, m->data.marked[k], p); - } - } -} - -static void dump_marks_fn(uintmax_t mark, void *object, void *cbp) { - struct object_entry *e = object; - FILE *f = cbp; - - fprintf(f, ":%" PRIuMAX " %s\n", mark, oid_to_hex(&e->idx.oid)); -} - -static void write_branch_report(FILE *rpt, struct branch *b) -{ - fprintf(rpt, "%s:\n", b->name); - - fprintf(rpt, " status :"); - if (b->active) - fputs(" active", rpt); - if (b->branch_tree.tree) - fputs(" loaded", rpt); - if (is_null_oid(&b->branch_tree.versions[1].oid)) - fputs(" dirty", rpt); - fputc('\n', rpt); - - fprintf(rpt, " tip commit : %s\n", oid_to_hex(&b->oid)); - fprintf(rpt, " old tree : %s\n", - oid_to_hex(&b->branch_tree.versions[0].oid)); - fprintf(rpt, " cur tree : %s\n", - oid_to_hex(&b->branch_tree.versions[1].oid)); - fprintf(rpt, " commit clock: %" PRIuMAX "\n", b->last_commit); - - fputs(" last pack : ", rpt); - if (b->pack_id < MAX_PACK_ID) - fprintf(rpt, "%u", b->pack_id); - fputc('\n', rpt); - - fputc('\n', rpt); -} - -static void write_crash_report(const char *err) -{ - char *loc = git_pathdup("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid()); - FILE *rpt = fopen(loc, "w"); - struct branch *b; - unsigned long lu; - struct recent_command *rc; - - if (!rpt) { - error_errno("can't write crash report %s", loc); - free(loc); - return; - } - - fprintf(stderr, "fast-import: dumping crash report to %s\n", loc); - - fprintf(rpt, "fast-import crash report:\n"); - fprintf(rpt, " fast-import process: %"PRIuMAX"\n", (uintmax_t) getpid()); - fprintf(rpt, " parent process : %"PRIuMAX"\n", (uintmax_t) getppid()); - fprintf(rpt, " at %s\n", show_date(time(NULL), 0, DATE_MODE(ISO8601))); - fputc('\n', rpt); - - fputs("fatal: ", rpt); - fputs(err, rpt); - fputc('\n', rpt); - - fputc('\n', rpt); - fputs("Most Recent Commands Before Crash\n", rpt); - fputs("---------------------------------\n", rpt); - for (rc = cmd_hist.next; rc != &cmd_hist; rc = rc->next) { - if (rc->next == &cmd_hist) - fputs("* ", rpt); - else - fputs(" ", rpt); - fputs(rc->buf, rpt); - fputc('\n', rpt); - } - - fputc('\n', rpt); - fputs("Active Branch LRU\n", rpt); - fputs("-----------------\n", rpt); - fprintf(rpt, " active_branches = %lu cur, %lu max\n", - cur_active_branches, - max_active_branches); - fputc('\n', rpt); - fputs(" pos clock name\n", rpt); - fputs(" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", rpt); - for (b = active_branches, lu = 0; b; b = b->active_next_branch) - fprintf(rpt, " %2lu) %6" PRIuMAX" %s\n", - ++lu, b->last_commit, b->name); - - fputc('\n', rpt); - fputs("Inactive Branches\n", rpt); - fputs("-----------------\n", rpt); - for (lu = 0; lu < branch_table_sz; lu++) { - for (b = branch_table[lu]; b; b = b->table_next_branch) - write_branch_report(rpt, b); - } - - if (first_tag) { - struct tag *tg; - fputc('\n', rpt); - fputs("Annotated Tags\n", rpt); - fputs("--------------\n", rpt); - for (tg = first_tag; tg; tg = tg->next_tag) { - fputs(oid_to_hex(&tg->oid), rpt); - fputc(' ', rpt); - fputs(tg->name, rpt); - fputc('\n', rpt); - } - } - - fputc('\n', rpt); - fputs("Marks\n", rpt); - fputs("-----\n", rpt); - if (export_marks_file) - fprintf(rpt, " exported to %s\n", export_marks_file); - else - for_each_mark(marks, 0, dump_marks_fn, rpt); - - fputc('\n', rpt); - fputs("-------------------\n", rpt); - fputs("END OF CRASH REPORT\n", rpt); - fclose(rpt); - free(loc); -} - -static void end_packfile(void); -static void unkeep_all_packs(void); -static void dump_marks(void); - -static NORETURN void die_nicely(const char *err, va_list params) -{ - static int zombie; - char message[2 * PATH_MAX]; - - vsnprintf(message, sizeof(message), err, params); - fputs("fatal: ", stderr); - fputs(message, stderr); - fputc('\n', stderr); - - if (!zombie) { - zombie = 1; - write_crash_report(message); - end_packfile(); - unkeep_all_packs(); - dump_marks(); - } - exit(128); -} - -#ifndef SIGUSR1 /* Windows, for example */ - -static void set_checkpoint_signal(void) -{ -} - -#else - -static void checkpoint_signal(int signo) -{ - checkpoint_requested = 1; -} - -static void set_checkpoint_signal(void) -{ - struct sigaction sa; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = checkpoint_signal; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - sigaction(SIGUSR1, &sa, NULL); -} - -#endif - -static void alloc_objects(unsigned int cnt) -{ - struct object_entry_pool *b; - - b = xmalloc(sizeof(struct object_entry_pool) - + cnt * sizeof(struct object_entry)); - b->next_pool = blocks; - b->next_free = b->entries; - b->end = b->entries + cnt; - blocks = b; - alloc_count += cnt; -} - -static struct object_entry *new_object(struct object_id *oid) -{ - struct object_entry *e; - - if (blocks->next_free == blocks->end) - alloc_objects(object_entry_alloc); - - e = blocks->next_free++; - oidcpy(&e->idx.oid, oid); - return e; -} - -static struct object_entry *find_object(struct object_id *oid) -{ - return hashmap_get_entry_from_hash(&object_table, oidhash(oid), oid, - struct object_entry, ent); -} - -static struct object_entry *insert_object(struct object_id *oid) -{ - struct object_entry *e; - unsigned int hash = oidhash(oid); - - e = hashmap_get_entry_from_hash(&object_table, hash, oid, - struct object_entry, ent); - if (!e) { - e = new_object(oid); - e->idx.offset = 0; - hashmap_entry_init(&e->ent, hash); - hashmap_add(&object_table, &e->ent); - } - - return e; -} - -static void invalidate_pack_id(unsigned int id) -{ - unsigned long lu; - struct tag *t; - struct hashmap_iter iter; - struct object_entry *e; - - hashmap_for_each_entry(&object_table, &iter, e, ent) { - if (e->pack_id == id) - e->pack_id = MAX_PACK_ID; - } - - for (lu = 0; lu < branch_table_sz; lu++) { - struct branch *b; - - for (b = branch_table[lu]; b; b = b->table_next_branch) - if (b->pack_id == id) - b->pack_id = MAX_PACK_ID; - } - - for (t = first_tag; t; t = t->next_tag) - if (t->pack_id == id) - t->pack_id = MAX_PACK_ID; -} - -static unsigned int hc_str(const char *s, size_t len) -{ - unsigned int r = 0; - while (len-- > 0) - r = r * 31 + *s++; - return r; -} - -static void insert_mark(struct mark_set *s, uintmax_t idnum, struct object_entry *oe) -{ - while ((idnum >> s->shift) >= 1024) { - s = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set)); - s->shift = marks->shift + 10; - s->data.sets[0] = marks; - marks = s; - } - while (s->shift) { - uintmax_t i = idnum >> s->shift; - idnum -= i << s->shift; - if (!s->data.sets[i]) { - s->data.sets[i] = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set)); - s->data.sets[i]->shift = s->shift - 10; - } - s = s->data.sets[i]; - } - if (!s->data.marked[idnum]) - marks_set_count++; - s->data.marked[idnum] = oe; -} - -static void *find_mark(struct mark_set *s, uintmax_t idnum) -{ - uintmax_t orig_idnum = idnum; - struct object_entry *oe = NULL; - if ((idnum >> s->shift) < 1024) { - while (s && s->shift) { - uintmax_t i = idnum >> s->shift; - idnum -= i << s->shift; - s = s->data.sets[i]; - } - if (s) - oe = s->data.marked[idnum]; - } - if (!oe) - die("mark :%" PRIuMAX " not declared", orig_idnum); - return oe; -} - -static struct atom_str *to_atom(const char *s, unsigned short len) -{ - unsigned int hc = hc_str(s, len) % atom_table_sz; - struct atom_str *c; - - for (c = atom_table[hc]; c; c = c->next_atom) - if (c->str_len == len && !strncmp(s, c->str_dat, len)) - return c; - - c = mem_pool_alloc(&fi_mem_pool, sizeof(struct atom_str) + len + 1); - c->str_len = len; - memcpy(c->str_dat, s, len); - c->str_dat[len] = 0; - c->next_atom = atom_table[hc]; - atom_table[hc] = c; - atom_cnt++; - return c; -} - -static struct branch *lookup_branch(const char *name) -{ - unsigned int hc = hc_str(name, strlen(name)) % branch_table_sz; - struct branch *b; - - for (b = branch_table[hc]; b; b = b->table_next_branch) - if (!strcmp(name, b->name)) - return b; - return NULL; -} - -static struct branch *new_branch(const char *name) -{ - unsigned int hc = hc_str(name, strlen(name)) % branch_table_sz; - struct branch *b = lookup_branch(name); - - if (b) - die("Invalid attempt to create duplicate branch: %s", name); - if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL)) - die("Branch name doesn't conform to GIT standards: %s", name); - - b = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct branch)); - b->name = mem_pool_strdup(&fi_mem_pool, name); - b->table_next_branch = branch_table[hc]; - b->branch_tree.versions[0].mode = S_IFDIR; - b->branch_tree.versions[1].mode = S_IFDIR; - b->num_notes = 0; - b->active = 0; - b->pack_id = MAX_PACK_ID; - branch_table[hc] = b; - branch_count++; - return b; -} - -static unsigned int hc_entries(unsigned int cnt) -{ - cnt = cnt & 7 ? (cnt / 8) + 1 : cnt / 8; - return cnt < avail_tree_table_sz ? cnt : avail_tree_table_sz - 1; -} - -static struct tree_content *new_tree_content(unsigned int cnt) -{ - struct avail_tree_content *f, *l = NULL; - struct tree_content *t; - unsigned int hc = hc_entries(cnt); - - for (f = avail_tree_table[hc]; f; l = f, f = f->next_avail) - if (f->entry_capacity >= cnt) - break; - - if (f) { - if (l) - l->next_avail = f->next_avail; - else - avail_tree_table[hc] = f->next_avail; - } else { - cnt = cnt & 7 ? ((cnt / 8) + 1) * 8 : cnt; - f = mem_pool_alloc(&fi_mem_pool, sizeof(*t) + sizeof(t->entries[0]) * cnt); - f->entry_capacity = cnt; - } - - t = (struct tree_content*)f; - t->entry_count = 0; - t->delta_depth = 0; - return t; -} - -static void release_tree_entry(struct tree_entry *e); -static void release_tree_content(struct tree_content *t) -{ - struct avail_tree_content *f = (struct avail_tree_content*)t; - unsigned int hc = hc_entries(f->entry_capacity); - f->next_avail = avail_tree_table[hc]; - avail_tree_table[hc] = f; -} - -static void release_tree_content_recursive(struct tree_content *t) -{ - unsigned int i; - for (i = 0; i < t->entry_count; i++) - release_tree_entry(t->entries[i]); - release_tree_content(t); -} - -static struct tree_content *grow_tree_content( - struct tree_content *t, - int amt) -{ - struct tree_content *r = new_tree_content(t->entry_count + amt); - r->entry_count = t->entry_count; - r->delta_depth = t->delta_depth; - COPY_ARRAY(r->entries, t->entries, t->entry_count); - release_tree_content(t); - return r; -} - -static struct tree_entry *new_tree_entry(void) -{ - struct tree_entry *e; - - if (!avail_tree_entry) { - unsigned int n = tree_entry_alloc; - tree_entry_allocd += n * sizeof(struct tree_entry); - ALLOC_ARRAY(e, n); - avail_tree_entry = e; - while (n-- > 1) { - *((void**)e) = e + 1; - e++; - } - *((void**)e) = NULL; - } - - e = avail_tree_entry; - avail_tree_entry = *((void**)e); - return e; -} - -static void release_tree_entry(struct tree_entry *e) -{ - if (e->tree) - release_tree_content_recursive(e->tree); - *((void**)e) = avail_tree_entry; - avail_tree_entry = e; -} - -static struct tree_content *dup_tree_content(struct tree_content *s) -{ - struct tree_content *d; - struct tree_entry *a, *b; - unsigned int i; - - if (!s) - return NULL; - d = new_tree_content(s->entry_count); - for (i = 0; i < s->entry_count; i++) { - a = s->entries[i]; - b = new_tree_entry(); - memcpy(b, a, sizeof(*a)); - if (a->tree && is_null_oid(&b->versions[1].oid)) - b->tree = dup_tree_content(a->tree); - else - b->tree = NULL; - d->entries[i] = b; - } - d->entry_count = s->entry_count; - d->delta_depth = s->delta_depth; - - return d; -} - -static void start_packfile(void) -{ - struct strbuf tmp_file = STRBUF_INIT; - struct packed_git *p; - int pack_fd; - - pack_fd = odb_mkstemp(&tmp_file, "pack/tmp_pack_XXXXXX"); - FLEX_ALLOC_STR(p, pack_name, tmp_file.buf); - strbuf_release(&tmp_file); - - p->pack_fd = pack_fd; - p->do_not_close = 1; - pack_file = hashfd(pack_fd, p->pack_name); - - pack_data = p; - pack_size = write_pack_header(pack_file, 0); - object_count = 0; - - REALLOC_ARRAY(all_packs, pack_id + 1); - all_packs[pack_id] = p; -} - -static const char *create_index(void) -{ - const char *tmpfile; - struct pack_idx_entry **idx, **c, **last; - struct object_entry *e; - struct object_entry_pool *o; - - /* Build the table of object IDs. */ - ALLOC_ARRAY(idx, object_count); - c = idx; - for (o = blocks; o; o = o->next_pool) - for (e = o->next_free; e-- != o->entries;) - if (pack_id == e->pack_id) - *c++ = &e->idx; - last = idx + object_count; - if (c != last) - die("internal consistency error creating the index"); - - tmpfile = write_idx_file(NULL, idx, object_count, &pack_idx_opts, - pack_data->hash); - free(idx); - return tmpfile; -} - -static char *keep_pack(const char *curr_index_name) -{ - static const char *keep_msg = "fast-import"; - struct strbuf name = STRBUF_INIT; - int keep_fd; - - odb_pack_name(&name, pack_data->hash, "keep"); - keep_fd = odb_pack_keep(name.buf); - if (keep_fd < 0) - die_errno("cannot create keep file"); - write_or_die(keep_fd, keep_msg, strlen(keep_msg)); - if (close(keep_fd)) - die_errno("failed to write keep file"); - - odb_pack_name(&name, pack_data->hash, "pack"); - if (finalize_object_file(pack_data->pack_name, name.buf)) - die("cannot store pack file"); - - odb_pack_name(&name, pack_data->hash, "idx"); - if (finalize_object_file(curr_index_name, name.buf)) - die("cannot store index file"); - free((void *)curr_index_name); - return strbuf_detach(&name, NULL); -} - -static void unkeep_all_packs(void) -{ - struct strbuf name = STRBUF_INIT; - int k; - - for (k = 0; k < pack_id; k++) { - struct packed_git *p = all_packs[k]; - odb_pack_name(&name, p->hash, "keep"); - unlink_or_warn(name.buf); - } - strbuf_release(&name); -} - -static int loosen_small_pack(const struct packed_git *p) -{ - struct child_process unpack = CHILD_PROCESS_INIT; - - if (lseek(p->pack_fd, 0, SEEK_SET) < 0) - die_errno("Failed seeking to start of '%s'", p->pack_name); - - unpack.in = p->pack_fd; - unpack.git_cmd = 1; - unpack.stdout_to_stderr = 1; - strvec_push(&unpack.args, "unpack-objects"); - if (!show_stats) - strvec_push(&unpack.args, "-q"); - - return run_command(&unpack); -} - -static void end_packfile(void) -{ - static int running; - - if (running || !pack_data) - return; - - running = 1; - clear_delta_base_cache(); - if (object_count) { - struct packed_git *new_p; - struct object_id cur_pack_oid; - char *idx_name; - int i; - struct branch *b; - struct tag *t; - - close_pack_windows(pack_data); - finalize_hashfile(pack_file, cur_pack_oid.hash, 0); - fixup_pack_header_footer(pack_data->pack_fd, pack_data->hash, - pack_data->pack_name, object_count, - cur_pack_oid.hash, pack_size); - - if (object_count <= unpack_limit) { - if (!loosen_small_pack(pack_data)) { - invalidate_pack_id(pack_id); - goto discard_pack; - } - } - - close(pack_data->pack_fd); - idx_name = keep_pack(create_index()); - - /* Register the packfile with core git's machinery. */ - new_p = add_packed_git(idx_name, strlen(idx_name), 1); - if (!new_p) - die("core git rejected index %s", idx_name); - all_packs[pack_id] = new_p; - install_packed_git(the_repository, new_p); - free(idx_name); - - /* Print the boundary */ - if (pack_edges) { - fprintf(pack_edges, "%s:", new_p->pack_name); - for (i = 0; i < branch_table_sz; i++) { - for (b = branch_table[i]; b; b = b->table_next_branch) { - if (b->pack_id == pack_id) - fprintf(pack_edges, " %s", - oid_to_hex(&b->oid)); - } - } - for (t = first_tag; t; t = t->next_tag) { - if (t->pack_id == pack_id) - fprintf(pack_edges, " %s", - oid_to_hex(&t->oid)); - } - fputc('\n', pack_edges); - fflush(pack_edges); - } - - pack_id++; - } - else { -discard_pack: - close(pack_data->pack_fd); - unlink_or_warn(pack_data->pack_name); - } - FREE_AND_NULL(pack_data); - running = 0; - - /* We can't carry a delta across packfiles. */ - strbuf_release(&last_blob.data); - last_blob.offset = 0; - last_blob.depth = 0; -} - -static void cycle_packfile(void) -{ - end_packfile(); - start_packfile(); -} - -static int store_object( - enum object_type type, - struct strbuf *dat, - struct last_object *last, - struct object_id *oidout, - uintmax_t mark) -{ - void *out, *delta; - struct object_entry *e; - unsigned char hdr[96]; - struct object_id oid; - unsigned long hdrlen, deltalen; - git_hash_ctx c; - git_zstream s; - - hdrlen = xsnprintf((char *)hdr, sizeof(hdr), "%s %lu", - type_name(type), (unsigned long)dat->len) + 1; - the_hash_algo->init_fn(&c); - the_hash_algo->update_fn(&c, hdr, hdrlen); - the_hash_algo->update_fn(&c, dat->buf, dat->len); - the_hash_algo->final_fn(oid.hash, &c); - if (oidout) - oidcpy(oidout, &oid); - - e = insert_object(&oid); - if (mark) - insert_mark(marks, mark, e); - if (e->idx.offset) { - duplicate_count_by_type[type]++; - return 1; - } else if (find_sha1_pack(oid.hash, - get_all_packs(the_repository))) { - e->type = type; - e->pack_id = MAX_PACK_ID; - e->idx.offset = 1; /* just not zero! */ - duplicate_count_by_type[type]++; - return 1; - } - - if (last && last->data.len && last->data.buf && last->depth < max_depth - && dat->len > the_hash_algo->rawsz) { - - delta_count_attempts_by_type[type]++; - delta = diff_delta(last->data.buf, last->data.len, - dat->buf, dat->len, - &deltalen, dat->len - the_hash_algo->rawsz); - } else - delta = NULL; - - git_deflate_init(&s, pack_compression_level); - if (delta) { - s.next_in = delta; - s.avail_in = deltalen; - } else { - s.next_in = (void *)dat->buf; - s.avail_in = dat->len; - } - s.avail_out = git_deflate_bound(&s, s.avail_in); - s.next_out = out = xmalloc(s.avail_out); - while (git_deflate(&s, Z_FINISH) == Z_OK) - ; /* nothing */ - git_deflate_end(&s); - - /* Determine if we should auto-checkpoint. */ - if ((max_packsize - && (pack_size + PACK_SIZE_THRESHOLD + s.total_out) > max_packsize) - || (pack_size + PACK_SIZE_THRESHOLD + s.total_out) < pack_size) { - - /* This new object needs to *not* have the current pack_id. */ - e->pack_id = pack_id + 1; - cycle_packfile(); - - /* We cannot carry a delta into the new pack. */ - if (delta) { - FREE_AND_NULL(delta); - - git_deflate_init(&s, pack_compression_level); - s.next_in = (void *)dat->buf; - s.avail_in = dat->len; - s.avail_out = git_deflate_bound(&s, s.avail_in); - s.next_out = out = xrealloc(out, s.avail_out); - while (git_deflate(&s, Z_FINISH) == Z_OK) - ; /* nothing */ - git_deflate_end(&s); - } - } - - e->type = type; - e->pack_id = pack_id; - e->idx.offset = pack_size; - object_count++; - object_count_by_type[type]++; - - crc32_begin(pack_file); - - if (delta) { - off_t ofs = e->idx.offset - last->offset; - unsigned pos = sizeof(hdr) - 1; - - delta_count_by_type[type]++; - e->depth = last->depth + 1; - - hdrlen = encode_in_pack_object_header(hdr, sizeof(hdr), - OBJ_OFS_DELTA, deltalen); - hashwrite(pack_file, hdr, hdrlen); - pack_size += hdrlen; - - hdr[pos] = ofs & 127; - while (ofs >>= 7) - hdr[--pos] = 128 | (--ofs & 127); - hashwrite(pack_file, hdr + pos, sizeof(hdr) - pos); - pack_size += sizeof(hdr) - pos; - } else { - e->depth = 0; - hdrlen = encode_in_pack_object_header(hdr, sizeof(hdr), - type, dat->len); - hashwrite(pack_file, hdr, hdrlen); - pack_size += hdrlen; - } - - hashwrite(pack_file, out, s.total_out); - pack_size += s.total_out; - - e->idx.crc32 = crc32_end(pack_file); - - free(out); - free(delta); - if (last) { - if (last->no_swap) { - last->data = *dat; - } else { - strbuf_swap(&last->data, dat); - } - last->offset = e->idx.offset; - last->depth = e->depth; - } - return 0; -} - -static void truncate_pack(struct hashfile_checkpoint *checkpoint) -{ - if (hashfile_truncate(pack_file, checkpoint)) - die_errno("cannot truncate pack to skip duplicate"); - pack_size = checkpoint->offset; -} - -static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark) -{ - size_t in_sz = 64 * 1024, out_sz = 64 * 1024; - unsigned char *in_buf = xmalloc(in_sz); - unsigned char *out_buf = xmalloc(out_sz); - struct object_entry *e; - struct object_id oid; - unsigned long hdrlen; - off_t offset; - git_hash_ctx c; - git_zstream s; - struct hashfile_checkpoint checkpoint; - int status = Z_OK; - - /* Determine if we should auto-checkpoint. */ - if ((max_packsize - && (pack_size + PACK_SIZE_THRESHOLD + len) > max_packsize) - || (pack_size + PACK_SIZE_THRESHOLD + len) < pack_size) - cycle_packfile(); - - hashfile_checkpoint(pack_file, &checkpoint); - offset = checkpoint.offset; - - hdrlen = xsnprintf((char *)out_buf, out_sz, "blob %" PRIuMAX, len) + 1; - - the_hash_algo->init_fn(&c); - the_hash_algo->update_fn(&c, out_buf, hdrlen); - - crc32_begin(pack_file); - - git_deflate_init(&s, pack_compression_level); - - hdrlen = encode_in_pack_object_header(out_buf, out_sz, OBJ_BLOB, len); - - s.next_out = out_buf + hdrlen; - s.avail_out = out_sz - hdrlen; - - while (status != Z_STREAM_END) { - if (0 < len && !s.avail_in) { - size_t cnt = in_sz < len ? in_sz : (size_t)len; - size_t n = fread(in_buf, 1, cnt, stdin); - if (!n && feof(stdin)) - die("EOF in data (%" PRIuMAX " bytes remaining)", len); - - the_hash_algo->update_fn(&c, in_buf, n); - s.next_in = in_buf; - s.avail_in = n; - len -= n; - } - - status = git_deflate(&s, len ? 0 : Z_FINISH); - - if (!s.avail_out || status == Z_STREAM_END) { - size_t n = s.next_out - out_buf; - hashwrite(pack_file, out_buf, n); - pack_size += n; - s.next_out = out_buf; - s.avail_out = out_sz; - } - - switch (status) { - case Z_OK: - case Z_BUF_ERROR: - case Z_STREAM_END: - continue; - default: - die("unexpected deflate failure: %d", status); - } - } - git_deflate_end(&s); - the_hash_algo->final_fn(oid.hash, &c); - - if (oidout) - oidcpy(oidout, &oid); - - e = insert_object(&oid); - - if (mark) - insert_mark(marks, mark, e); - - if (e->idx.offset) { - duplicate_count_by_type[OBJ_BLOB]++; - truncate_pack(&checkpoint); - - } else if (find_sha1_pack(oid.hash, - get_all_packs(the_repository))) { - e->type = OBJ_BLOB; - e->pack_id = MAX_PACK_ID; - e->idx.offset = 1; /* just not zero! */ - duplicate_count_by_type[OBJ_BLOB]++; - truncate_pack(&checkpoint); - - } else { - e->depth = 0; - e->type = OBJ_BLOB; - e->pack_id = pack_id; - e->idx.offset = offset; - e->idx.crc32 = crc32_end(pack_file); - object_count++; - object_count_by_type[OBJ_BLOB]++; - } - - free(in_buf); - free(out_buf); -} - -/* All calls must be guarded by find_object() or find_mark() to - * ensure the 'struct object_entry' passed was written by this - * process instance. We unpack the entry by the offset, avoiding - * the need for the corresponding .idx file. This unpacking rule - * works because we only use OBJ_REF_DELTA within the packfiles - * created by fast-import. - * - * oe must not be NULL. Such an oe usually comes from giving - * an unknown SHA-1 to find_object() or an undefined mark to - * find_mark(). Callers must test for this condition and use - * the standard read_sha1_file() when it happens. - * - * oe->pack_id must not be MAX_PACK_ID. Such an oe is usually from - * find_mark(), where the mark was reloaded from an existing marks - * file and is referencing an object that this fast-import process - * instance did not write out to a packfile. Callers must test for - * this condition and use read_sha1_file() instead. - */ -static void *gfi_unpack_entry( - struct object_entry *oe, - unsigned long *sizep) -{ - enum object_type type; - struct packed_git *p = all_packs[oe->pack_id]; - if (p == pack_data && p->pack_size < (pack_size + the_hash_algo->rawsz)) { - /* The object is stored in the packfile we are writing to - * and we have modified it since the last time we scanned - * back to read a previously written object. If an old - * window covered [p->pack_size, p->pack_size + rawsz) its - * data is stale and is not valid. Closing all windows - * and updating the packfile length ensures we can read - * the newly written data. - */ - close_pack_windows(p); - hashflush(pack_file); - - /* We have to offer rawsz bytes additional on the end of - * the packfile as the core unpacker code assumes the - * footer is present at the file end and must promise - * at least rawsz bytes within any window it maps. But - * we don't actually create the footer here. - */ - p->pack_size = pack_size + the_hash_algo->rawsz; - } - return unpack_entry(the_repository, p, oe->idx.offset, &type, sizep); -} - -static const char *get_mode(const char *str, uint16_t *modep) -{ - unsigned char c; - uint16_t mode = 0; - - while ((c = *str++) != ' ') { - if (c < '0' || c > '7') - return NULL; - mode = (mode << 3) + (c - '0'); - } - *modep = mode; - return str; -} - -static void load_tree(struct tree_entry *root) -{ - struct object_id *oid = &root->versions[1].oid; - struct object_entry *myoe; - struct tree_content *t; - unsigned long size; - char *buf; - const char *c; - - root->tree = t = new_tree_content(8); - if (is_null_oid(oid)) - return; - - myoe = find_object(oid); - if (myoe && myoe->pack_id != MAX_PACK_ID) { - if (myoe->type != OBJ_TREE) - die("Not a tree: %s", oid_to_hex(oid)); - t->delta_depth = myoe->depth; - buf = gfi_unpack_entry(myoe, &size); - if (!buf) - die("Can't load tree %s", oid_to_hex(oid)); - } else { - enum object_type type; - buf = read_object_file(oid, &type, &size); - if (!buf || type != OBJ_TREE) - die("Can't load tree %s", oid_to_hex(oid)); - } - - c = buf; - while (c != (buf + size)) { - struct tree_entry *e = new_tree_entry(); - - if (t->entry_count == t->entry_capacity) - root->tree = t = grow_tree_content(t, t->entry_count); - t->entries[t->entry_count++] = e; - - e->tree = NULL; - c = get_mode(c, &e->versions[1].mode); - if (!c) - die("Corrupt mode in %s", oid_to_hex(oid)); - e->versions[0].mode = e->versions[1].mode; - e->name = to_atom(c, strlen(c)); - c += e->name->str_len + 1; - hashcpy(e->versions[0].oid.hash, (unsigned char *)c); - hashcpy(e->versions[1].oid.hash, (unsigned char *)c); - c += the_hash_algo->rawsz; - } - free(buf); -} - -static int tecmp0 (const void *_a, const void *_b) -{ - struct tree_entry *a = *((struct tree_entry**)_a); - struct tree_entry *b = *((struct tree_entry**)_b); - return base_name_compare( - a->name->str_dat, a->name->str_len, a->versions[0].mode, - b->name->str_dat, b->name->str_len, b->versions[0].mode); -} - -static int tecmp1 (const void *_a, const void *_b) -{ - struct tree_entry *a = *((struct tree_entry**)_a); - struct tree_entry *b = *((struct tree_entry**)_b); - return base_name_compare( - a->name->str_dat, a->name->str_len, a->versions[1].mode, - b->name->str_dat, b->name->str_len, b->versions[1].mode); -} - -static void mktree(struct tree_content *t, int v, struct strbuf *b) -{ - size_t maxlen = 0; - unsigned int i; - - if (!v) - QSORT(t->entries, t->entry_count, tecmp0); - else - QSORT(t->entries, t->entry_count, tecmp1); - - for (i = 0; i < t->entry_count; i++) { - if (t->entries[i]->versions[v].mode) - maxlen += t->entries[i]->name->str_len + 34; - } - - strbuf_reset(b); - strbuf_grow(b, maxlen); - for (i = 0; i < t->entry_count; i++) { - struct tree_entry *e = t->entries[i]; - if (!e->versions[v].mode) - continue; - strbuf_addf(b, "%o %s%c", - (unsigned int)(e->versions[v].mode & ~NO_DELTA), - e->name->str_dat, '\0'); - strbuf_add(b, e->versions[v].oid.hash, the_hash_algo->rawsz); - } -} - -static void store_tree(struct tree_entry *root) -{ - struct tree_content *t; - unsigned int i, j, del; - struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 }; - struct object_entry *le = NULL; - - if (!is_null_oid(&root->versions[1].oid)) - return; - - if (!root->tree) - load_tree(root); - t = root->tree; - - for (i = 0; i < t->entry_count; i++) { - if (t->entries[i]->tree) - store_tree(t->entries[i]); - } - - if (!(root->versions[0].mode & NO_DELTA)) - le = find_object(&root->versions[0].oid); - if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) { - mktree(t, 0, &old_tree); - lo.data = old_tree; - lo.offset = le->idx.offset; - lo.depth = t->delta_depth; - } - - mktree(t, 1, &new_tree); - store_object(OBJ_TREE, &new_tree, &lo, &root->versions[1].oid, 0); - - t->delta_depth = lo.depth; - for (i = 0, j = 0, del = 0; i < t->entry_count; i++) { - struct tree_entry *e = t->entries[i]; - if (e->versions[1].mode) { - e->versions[0].mode = e->versions[1].mode; - oidcpy(&e->versions[0].oid, &e->versions[1].oid); - t->entries[j++] = e; - } else { - release_tree_entry(e); - del++; - } - } - t->entry_count -= del; -} - -static void tree_content_replace( - struct tree_entry *root, - const struct object_id *oid, - const uint16_t mode, - struct tree_content *newtree) -{ - if (!S_ISDIR(mode)) - die("Root cannot be a non-directory"); - oidclr(&root->versions[0].oid); - oidcpy(&root->versions[1].oid, oid); - if (root->tree) - release_tree_content_recursive(root->tree); - root->tree = newtree; -} - -static int tree_content_set( - struct tree_entry *root, - const char *p, - const struct object_id *oid, - const uint16_t mode, - struct tree_content *subtree) -{ - struct tree_content *t; - const char *slash1; - unsigned int i, n; - struct tree_entry *e; - - slash1 = strchrnul(p, '/'); - n = slash1 - p; - if (!n) - die("Empty path component found in input"); - if (!*slash1 && !S_ISDIR(mode) && subtree) - die("Non-directories cannot have subtrees"); - - if (!root->tree) - load_tree(root); - t = root->tree; - for (i = 0; i < t->entry_count; i++) { - e = t->entries[i]; - if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) { - if (!*slash1) { - if (!S_ISDIR(mode) - && e->versions[1].mode == mode - && oideq(&e->versions[1].oid, oid)) - return 0; - e->versions[1].mode = mode; - oidcpy(&e->versions[1].oid, oid); - if (e->tree) - release_tree_content_recursive(e->tree); - e->tree = subtree; - - /* - * We need to leave e->versions[0].sha1 alone - * to avoid modifying the preimage tree used - * when writing out the parent directory. - * But after replacing the subdir with a - * completely different one, it's not a good - * delta base any more, and besides, we've - * thrown away the tree entries needed to - * make a delta against it. - * - * So let's just explicitly disable deltas - * for the subtree. - */ - if (S_ISDIR(e->versions[0].mode)) - e->versions[0].mode |= NO_DELTA; - - oidclr(&root->versions[1].oid); - return 1; - } - if (!S_ISDIR(e->versions[1].mode)) { - e->tree = new_tree_content(8); - e->versions[1].mode = S_IFDIR; - } - if (!e->tree) - load_tree(e); - if (tree_content_set(e, slash1 + 1, oid, mode, subtree)) { - oidclr(&root->versions[1].oid); - return 1; - } - return 0; - } - } - - if (t->entry_count == t->entry_capacity) - root->tree = t = grow_tree_content(t, t->entry_count); - e = new_tree_entry(); - e->name = to_atom(p, n); - e->versions[0].mode = 0; - oidclr(&e->versions[0].oid); - t->entries[t->entry_count++] = e; - if (*slash1) { - e->tree = new_tree_content(8); - e->versions[1].mode = S_IFDIR; - tree_content_set(e, slash1 + 1, oid, mode, subtree); - } else { - e->tree = subtree; - e->versions[1].mode = mode; - oidcpy(&e->versions[1].oid, oid); - } - oidclr(&root->versions[1].oid); - return 1; -} - -static int tree_content_remove( - struct tree_entry *root, - const char *p, - struct tree_entry *backup_leaf, - int allow_root) -{ - struct tree_content *t; - const char *slash1; - unsigned int i, n; - struct tree_entry *e; - - slash1 = strchrnul(p, '/'); - n = slash1 - p; - - if (!root->tree) - load_tree(root); - - if (!*p && allow_root) { - e = root; - goto del_entry; - } - - t = root->tree; - for (i = 0; i < t->entry_count; i++) { - e = t->entries[i]; - if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) { - if (*slash1 && !S_ISDIR(e->versions[1].mode)) - /* - * If p names a file in some subdirectory, and a - * file or symlink matching the name of the - * parent directory of p exists, then p cannot - * exist and need not be deleted. - */ - return 1; - if (!*slash1 || !S_ISDIR(e->versions[1].mode)) - goto del_entry; - if (!e->tree) - load_tree(e); - if (tree_content_remove(e, slash1 + 1, backup_leaf, 0)) { - for (n = 0; n < e->tree->entry_count; n++) { - if (e->tree->entries[n]->versions[1].mode) { - oidclr(&root->versions[1].oid); - return 1; - } - } - backup_leaf = NULL; - goto del_entry; - } - return 0; - } - } - return 0; - -del_entry: - if (backup_leaf) - memcpy(backup_leaf, e, sizeof(*backup_leaf)); - else if (e->tree) - release_tree_content_recursive(e->tree); - e->tree = NULL; - e->versions[1].mode = 0; - oidclr(&e->versions[1].oid); - oidclr(&root->versions[1].oid); - return 1; -} - -static int tree_content_get( - struct tree_entry *root, - const char *p, - struct tree_entry *leaf, - int allow_root) -{ - struct tree_content *t; - const char *slash1; - unsigned int i, n; - struct tree_entry *e; - - slash1 = strchrnul(p, '/'); - n = slash1 - p; - if (!n && !allow_root) - die("Empty path component found in input"); - - if (!root->tree) - load_tree(root); - - if (!n) { - e = root; - goto found_entry; - } - - t = root->tree; - for (i = 0; i < t->entry_count; i++) { - e = t->entries[i]; - if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) { - if (!*slash1) - goto found_entry; - if (!S_ISDIR(e->versions[1].mode)) - return 0; - if (!e->tree) - load_tree(e); - return tree_content_get(e, slash1 + 1, leaf, 0); - } - } - return 0; - -found_entry: - memcpy(leaf, e, sizeof(*leaf)); - if (e->tree && is_null_oid(&e->versions[1].oid)) - leaf->tree = dup_tree_content(e->tree); - else - leaf->tree = NULL; - return 1; -} - -static int update_branch(struct branch *b) -{ - static const char *msg = "fast-import"; - struct ref_transaction *transaction; - struct object_id old_oid; - struct strbuf err = STRBUF_INIT; - - if (is_null_oid(&b->oid)) { - if (b->delete) - delete_ref(NULL, b->name, NULL, 0); - return 0; - } - if (read_ref(b->name, &old_oid)) - oidclr(&old_oid); - if (!force_update && !is_null_oid(&old_oid)) { - struct commit *old_cmit, *new_cmit; - - old_cmit = lookup_commit_reference_gently(the_repository, - &old_oid, 0); - new_cmit = lookup_commit_reference_gently(the_repository, - &b->oid, 0); - if (!old_cmit || !new_cmit) - return error("Branch %s is missing commits.", b->name); - - if (!in_merge_bases(old_cmit, new_cmit)) { - warning("Not updating %s" - " (new tip %s does not contain %s)", - b->name, oid_to_hex(&b->oid), - oid_to_hex(&old_oid)); - return -1; - } - } - transaction = ref_transaction_begin(&err); - if (!transaction || - ref_transaction_update(transaction, b->name, &b->oid, &old_oid, - 0, msg, &err) || - ref_transaction_commit(transaction, &err)) { - ref_transaction_free(transaction); - error("%s", err.buf); - strbuf_release(&err); - return -1; - } - ref_transaction_free(transaction); - strbuf_release(&err); - return 0; -} - -static void dump_branches(void) -{ - unsigned int i; - struct branch *b; - - for (i = 0; i < branch_table_sz; i++) { - for (b = branch_table[i]; b; b = b->table_next_branch) - failure |= update_branch(b); - } -} - -static void dump_tags(void) -{ - static const char *msg = "fast-import"; - struct tag *t; - struct strbuf ref_name = STRBUF_INIT; - struct strbuf err = STRBUF_INIT; - struct ref_transaction *transaction; - - transaction = ref_transaction_begin(&err); - if (!transaction) { - failure |= error("%s", err.buf); - goto cleanup; - } - for (t = first_tag; t; t = t->next_tag) { - strbuf_reset(&ref_name); - strbuf_addf(&ref_name, "refs/tags/%s", t->name); - - if (ref_transaction_update(transaction, ref_name.buf, - &t->oid, NULL, 0, msg, &err)) { - failure |= error("%s", err.buf); - goto cleanup; - } - } - if (ref_transaction_commit(transaction, &err)) - failure |= error("%s", err.buf); - - cleanup: - ref_transaction_free(transaction); - strbuf_release(&ref_name); - strbuf_release(&err); -} - -static void dump_marks(void) -{ - struct lock_file mark_lock = LOCK_INIT; - FILE *f; - - if (!export_marks_file || (import_marks_file && !import_marks_file_done)) - return; - - if (safe_create_leading_directories_const(export_marks_file)) { - failure |= error_errno("unable to create leading directories of %s", - export_marks_file); - return; - } - - if (hold_lock_file_for_update(&mark_lock, export_marks_file, 0) < 0) { - failure |= error_errno("Unable to write marks file %s", - export_marks_file); - return; - } - - f = fdopen_lock_file(&mark_lock, "w"); - if (!f) { - int saved_errno = errno; - rollback_lock_file(&mark_lock); - failure |= error("Unable to write marks file %s: %s", - export_marks_file, strerror(saved_errno)); - return; - } - - for_each_mark(marks, 0, dump_marks_fn, f); - if (commit_lock_file(&mark_lock)) { - failure |= error_errno("Unable to write file %s", - export_marks_file); - return; - } -} - -static void insert_object_entry(struct mark_set *s, struct object_id *oid, uintmax_t mark) -{ - struct object_entry *e; - e = find_object(oid); - if (!e) { - enum object_type type = oid_object_info(the_repository, - oid, NULL); - if (type < 0) - die("object not found: %s", oid_to_hex(oid)); - e = insert_object(oid); - e->type = type; - e->pack_id = MAX_PACK_ID; - e->idx.offset = 1; /* just not zero! */ - } - insert_mark(s, mark, e); -} - -static void insert_oid_entry(struct mark_set *s, struct object_id *oid, uintmax_t mark) -{ - insert_mark(s, mark, xmemdupz(oid, sizeof(*oid))); -} - -static void read_mark_file(struct mark_set *s, FILE *f, mark_set_inserter_t inserter) -{ - char line[512]; - while (fgets(line, sizeof(line), f)) { - uintmax_t mark; - char *end; - struct object_id oid; - - /* Ensure SHA-1 objects are padded with zeros. */ - memset(oid.hash, 0, sizeof(oid.hash)); - - end = strchr(line, '\n'); - if (line[0] != ':' || !end) - die("corrupt mark line: %s", line); - *end = 0; - mark = strtoumax(line + 1, &end, 10); - if (!mark || end == line + 1 - || *end != ' ' - || get_oid_hex_any(end + 1, &oid) == GIT_HASH_UNKNOWN) - die("corrupt mark line: %s", line); - inserter(s, &oid, mark); - } -} - -static void read_marks(void) -{ - FILE *f = fopen(import_marks_file, "r"); - if (f) - ; - else if (import_marks_file_ignore_missing && errno == ENOENT) - goto done; /* Marks file does not exist */ - else - die_errno("cannot read '%s'", import_marks_file); - read_mark_file(marks, f, insert_object_entry); - fclose(f); -done: - import_marks_file_done = 1; -} - - -static int read_next_command(void) -{ - static int stdin_eof = 0; - - if (stdin_eof) { - unread_command_buf = 0; - return EOF; - } - - for (;;) { - if (unread_command_buf) { - unread_command_buf = 0; - } else { - struct recent_command *rc; - - stdin_eof = strbuf_getline_lf(&command_buf, stdin); - if (stdin_eof) - return EOF; - - if (!seen_data_command - && !starts_with(command_buf.buf, "feature ") - && !starts_with(command_buf.buf, "option ")) { - parse_argv(); - } - - rc = rc_free; - if (rc) - rc_free = rc->next; - else { - rc = cmd_hist.next; - cmd_hist.next = rc->next; - cmd_hist.next->prev = &cmd_hist; - free(rc->buf); - } - - rc->buf = xstrdup(command_buf.buf); - rc->prev = cmd_tail; - rc->next = cmd_hist.prev; - rc->prev->next = rc; - cmd_tail = rc; - } - if (command_buf.buf[0] == '#') - continue; - return 0; - } -} - -static void skip_optional_lf(void) -{ - int term_char = fgetc(stdin); - if (term_char != '\n' && term_char != EOF) - ungetc(term_char, stdin); -} - -static void parse_mark(void) -{ - const char *v; - if (skip_prefix(command_buf.buf, "mark :", &v)) { - next_mark = strtoumax(v, NULL, 10); - read_next_command(); - } - else - next_mark = 0; -} - -static void parse_original_identifier(void) -{ - const char *v; - if (skip_prefix(command_buf.buf, "original-oid ", &v)) - read_next_command(); -} - -static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res) -{ - const char *data; - strbuf_reset(sb); - - if (!skip_prefix(command_buf.buf, "data ", &data)) - die("Expected 'data n' command, found: %s", command_buf.buf); - - if (skip_prefix(data, "<<", &data)) { - char *term = xstrdup(data); - size_t term_len = command_buf.len - (data - command_buf.buf); - - for (;;) { - if (strbuf_getline_lf(&command_buf, stdin) == EOF) - die("EOF in data (terminator '%s' not found)", term); - if (term_len == command_buf.len - && !strcmp(term, command_buf.buf)) - break; - strbuf_addbuf(sb, &command_buf); - strbuf_addch(sb, '\n'); - } - free(term); - } - else { - uintmax_t len = strtoumax(data, NULL, 10); - size_t n = 0, length = (size_t)len; - - if (limit && limit < len) { - *len_res = len; - return 0; - } - if (length < len) - die("data is too large to use in this context"); - - while (n < length) { - size_t s = strbuf_fread(sb, length - n, stdin); - if (!s && feof(stdin)) - die("EOF in data (%lu bytes remaining)", - (unsigned long)(length - n)); - n += s; - } - } - - skip_optional_lf(); - return 1; -} - -static int validate_raw_date(const char *src, struct strbuf *result, int strict) -{ - const char *orig_src = src; - char *endp; - unsigned long num; - - errno = 0; - - num = strtoul(src, &endp, 10); - /* - * NEEDSWORK: perhaps check for reasonable values? For example, we - * could error on values representing times more than a - * day in the future. - */ - if (errno || endp == src || *endp != ' ') - return -1; - - src = endp + 1; - if (*src != '-' && *src != '+') - return -1; - - num = strtoul(src + 1, &endp, 10); - /* - * NEEDSWORK: check for brokenness other than num > 1400, such as - * (num % 100) >= 60, or ((num % 100) % 15) != 0 ? - */ - if (errno || endp == src + 1 || *endp || /* did not parse */ - (strict && (1400 < num)) /* parsed a broken timezone */ - ) - return -1; - - strbuf_addstr(result, orig_src); - return 0; -} - -static char *parse_ident(const char *buf) -{ - const char *ltgt; - size_t name_len; - struct strbuf ident = STRBUF_INIT; - - /* ensure there is a space delimiter even if there is no name */ - if (*buf == '<') - --buf; - - ltgt = buf + strcspn(buf, "<>"); - if (*ltgt != '<') - die("Missing < in ident string: %s", buf); - if (ltgt != buf && ltgt[-1] != ' ') - die("Missing space before < in ident string: %s", buf); - ltgt = ltgt + 1 + strcspn(ltgt + 1, "<>"); - if (*ltgt != '>') - die("Missing > in ident string: %s", buf); - ltgt++; - if (*ltgt != ' ') - die("Missing space after > in ident string: %s", buf); - ltgt++; - name_len = ltgt - buf; - strbuf_add(&ident, buf, name_len); - - switch (whenspec) { - case WHENSPEC_RAW: - if (validate_raw_date(ltgt, &ident, 1) < 0) - die("Invalid raw date \"%s\" in ident: %s", ltgt, buf); - break; - case WHENSPEC_RAW_PERMISSIVE: - if (validate_raw_date(ltgt, &ident, 0) < 0) - die("Invalid raw date \"%s\" in ident: %s", ltgt, buf); - break; - case WHENSPEC_RFC2822: - if (parse_date(ltgt, &ident) < 0) - die("Invalid rfc2822 date \"%s\" in ident: %s", ltgt, buf); - break; - case WHENSPEC_NOW: - if (strcmp("now", ltgt)) - die("Date in ident must be 'now': %s", buf); - datestamp(&ident); - break; - } - - return strbuf_detach(&ident, NULL); -} - -static void parse_and_store_blob( - struct last_object *last, - struct object_id *oidout, - uintmax_t mark) -{ - static struct strbuf buf = STRBUF_INIT; - uintmax_t len; - - if (parse_data(&buf, big_file_threshold, &len)) - store_object(OBJ_BLOB, &buf, last, oidout, mark); - else { - if (last) { - strbuf_release(&last->data); - last->offset = 0; - last->depth = 0; - } - stream_blob(len, oidout, mark); - skip_optional_lf(); - } -} - -static void parse_new_blob(void) -{ - read_next_command(); - parse_mark(); - parse_original_identifier(); - parse_and_store_blob(&last_blob, NULL, next_mark); -} - -static void unload_one_branch(void) -{ - while (cur_active_branches - && cur_active_branches >= max_active_branches) { - uintmax_t min_commit = ULONG_MAX; - struct branch *e, *l = NULL, *p = NULL; - - for (e = active_branches; e; e = e->active_next_branch) { - if (e->last_commit < min_commit) { - p = l; - min_commit = e->last_commit; - } - l = e; - } - - if (p) { - e = p->active_next_branch; - p->active_next_branch = e->active_next_branch; - } else { - e = active_branches; - active_branches = e->active_next_branch; - } - e->active = 0; - e->active_next_branch = NULL; - if (e->branch_tree.tree) { - release_tree_content_recursive(e->branch_tree.tree); - e->branch_tree.tree = NULL; - } - cur_active_branches--; - } -} - -static void load_branch(struct branch *b) -{ - load_tree(&b->branch_tree); - if (!b->active) { - b->active = 1; - b->active_next_branch = active_branches; - active_branches = b; - cur_active_branches++; - branch_load_count++; - } -} - -static unsigned char convert_num_notes_to_fanout(uintmax_t num_notes) -{ - unsigned char fanout = 0; - while ((num_notes >>= 8)) - fanout++; - return fanout; -} - -static void construct_path_with_fanout(const char *hex_sha1, - unsigned char fanout, char *path) -{ - unsigned int i = 0, j = 0; - if (fanout >= the_hash_algo->rawsz) - die("Too large fanout (%u)", fanout); - while (fanout) { - path[i++] = hex_sha1[j++]; - path[i++] = hex_sha1[j++]; - path[i++] = '/'; - fanout--; - } - memcpy(path + i, hex_sha1 + j, the_hash_algo->hexsz - j); - path[i + the_hash_algo->hexsz - j] = '\0'; -} - -static uintmax_t do_change_note_fanout( - struct tree_entry *orig_root, struct tree_entry *root, - char *hex_oid, unsigned int hex_oid_len, - char *fullpath, unsigned int fullpath_len, - unsigned char fanout) -{ - struct tree_content *t; - struct tree_entry *e, leaf; - unsigned int i, tmp_hex_oid_len, tmp_fullpath_len; - uintmax_t num_notes = 0; - struct object_id oid; - /* hex oid + '/' between each pair of hex digits + NUL */ - char realpath[GIT_MAX_HEXSZ + ((GIT_MAX_HEXSZ / 2) - 1) + 1]; - const unsigned hexsz = the_hash_algo->hexsz; - - if (!root->tree) - load_tree(root); - t = root->tree; - - for (i = 0; t && i < t->entry_count; i++) { - e = t->entries[i]; - tmp_hex_oid_len = hex_oid_len + e->name->str_len; - tmp_fullpath_len = fullpath_len; - - /* - * We're interested in EITHER existing note entries (entries - * with exactly 40 hex chars in path, not including directory - * separators), OR directory entries that may contain note - * entries (with < 40 hex chars in path). - * Also, each path component in a note entry must be a multiple - * of 2 chars. - */ - if (!e->versions[1].mode || - tmp_hex_oid_len > hexsz || - e->name->str_len % 2) - continue; - - /* This _may_ be a note entry, or a subdir containing notes */ - memcpy(hex_oid + hex_oid_len, e->name->str_dat, - e->name->str_len); - if (tmp_fullpath_len) - fullpath[tmp_fullpath_len++] = '/'; - memcpy(fullpath + tmp_fullpath_len, e->name->str_dat, - e->name->str_len); - tmp_fullpath_len += e->name->str_len; - fullpath[tmp_fullpath_len] = '\0'; - - if (tmp_hex_oid_len == hexsz && !get_oid_hex(hex_oid, &oid)) { - /* This is a note entry */ - if (fanout == 0xff) { - /* Counting mode, no rename */ - num_notes++; - continue; - } - construct_path_with_fanout(hex_oid, fanout, realpath); - if (!strcmp(fullpath, realpath)) { - /* Note entry is in correct location */ - num_notes++; - continue; - } - - /* Rename fullpath to realpath */ - if (!tree_content_remove(orig_root, fullpath, &leaf, 0)) - die("Failed to remove path %s", fullpath); - tree_content_set(orig_root, realpath, - &leaf.versions[1].oid, - leaf.versions[1].mode, - leaf.tree); - } else if (S_ISDIR(e->versions[1].mode)) { - /* This is a subdir that may contain note entries */ - num_notes += do_change_note_fanout(orig_root, e, - hex_oid, tmp_hex_oid_len, - fullpath, tmp_fullpath_len, fanout); - } - - /* The above may have reallocated the current tree_content */ - t = root->tree; - } - return num_notes; -} - -static uintmax_t change_note_fanout(struct tree_entry *root, - unsigned char fanout) -{ - /* - * The size of path is due to one slash between every two hex digits, - * plus the terminating NUL. Note that there is no slash at the end, so - * the number of slashes is one less than half the number of hex - * characters. - */ - char hex_oid[GIT_MAX_HEXSZ], path[GIT_MAX_HEXSZ + (GIT_MAX_HEXSZ / 2) - 1 + 1]; - return do_change_note_fanout(root, root, hex_oid, 0, path, 0, fanout); -} - -static int parse_mapped_oid_hex(const char *hex, struct object_id *oid, const char **end) -{ - int algo; - khiter_t it; - - /* Make SHA-1 object IDs have all-zero padding. */ - memset(oid->hash, 0, sizeof(oid->hash)); - - algo = parse_oid_hex_any(hex, oid, end); - if (algo == GIT_HASH_UNKNOWN) - return -1; - - it = kh_get_oid_map(sub_oid_map, *oid); - /* No such object? */ - if (it == kh_end(sub_oid_map)) { - /* If we're using the same algorithm, pass it through. */ - if (hash_algos[algo].format_id == the_hash_algo->format_id) - return 0; - return -1; - } - oidcpy(oid, kh_value(sub_oid_map, it)); - return 0; -} - -/* - * Given a pointer into a string, parse a mark reference: - * - * idnum ::= ':' bigint; - * - * Return the first character after the value in *endptr. - * - * Complain if the following character is not what is expected, - * either a space or end of the string. - */ -static uintmax_t parse_mark_ref(const char *p, char **endptr) -{ - uintmax_t mark; - - assert(*p == ':'); - p++; - mark = strtoumax(p, endptr, 10); - if (*endptr == p) - die("No value after ':' in mark: %s", command_buf.buf); - return mark; -} - -/* - * Parse the mark reference, and complain if this is not the end of - * the string. - */ -static uintmax_t parse_mark_ref_eol(const char *p) -{ - char *end; - uintmax_t mark; - - mark = parse_mark_ref(p, &end); - if (*end != '\0') - die("Garbage after mark: %s", command_buf.buf); - return mark; -} - -/* - * Parse the mark reference, demanding a trailing space. Return a - * pointer to the space. - */ -static uintmax_t parse_mark_ref_space(const char **p) -{ - uintmax_t mark; - char *end; - - mark = parse_mark_ref(*p, &end); - if (*end++ != ' ') - die("Missing space after mark: %s", command_buf.buf); - *p = end; - return mark; -} - -static void file_change_m(const char *p, struct branch *b) -{ - static struct strbuf uq = STRBUF_INIT; - const char *endp; - struct object_entry *oe; - struct object_id oid; - uint16_t mode, inline_data = 0; - - p = get_mode(p, &mode); - if (!p) - die("Corrupt mode: %s", command_buf.buf); - switch (mode) { - case 0644: - case 0755: - mode |= S_IFREG; - case S_IFREG | 0644: - case S_IFREG | 0755: - case S_IFLNK: - case S_IFDIR: - case S_IFGITLINK: - /* ok */ - break; - default: - die("Corrupt mode: %s", command_buf.buf); - } - - if (*p == ':') { - oe = find_mark(marks, parse_mark_ref_space(&p)); - oidcpy(&oid, &oe->idx.oid); - } else if (skip_prefix(p, "inline ", &p)) { - inline_data = 1; - oe = NULL; /* not used with inline_data, but makes gcc happy */ - } else { - if (parse_mapped_oid_hex(p, &oid, &p)) - die("Invalid dataref: %s", command_buf.buf); - oe = find_object(&oid); - if (*p++ != ' ') - die("Missing space after SHA1: %s", command_buf.buf); - } - - strbuf_reset(&uq); - if (!unquote_c_style(&uq, p, &endp)) { - if (*endp) - die("Garbage after path in: %s", command_buf.buf); - p = uq.buf; - } - - /* Git does not track empty, non-toplevel directories. */ - if (S_ISDIR(mode) && is_empty_tree_oid(&oid) && *p) { - tree_content_remove(&b->branch_tree, p, NULL, 0); - return; - } - - if (S_ISGITLINK(mode)) { - if (inline_data) - die("Git links cannot be specified 'inline': %s", - command_buf.buf); - else if (oe) { - if (oe->type != OBJ_COMMIT) - die("Not a commit (actually a %s): %s", - type_name(oe->type), command_buf.buf); - } - /* - * Accept the sha1 without checking; it expected to be in - * another repository. - */ - } else if (inline_data) { - if (S_ISDIR(mode)) - die("Directories cannot be specified 'inline': %s", - command_buf.buf); - if (p != uq.buf) { - strbuf_addstr(&uq, p); - p = uq.buf; - } - while (read_next_command() != EOF) { - const char *v; - if (skip_prefix(command_buf.buf, "cat-blob ", &v)) - parse_cat_blob(v); - else { - parse_and_store_blob(&last_blob, &oid, 0); - break; - } - } - } else { - enum object_type expected = S_ISDIR(mode) ? - OBJ_TREE: OBJ_BLOB; - enum object_type type = oe ? oe->type : - oid_object_info(the_repository, &oid, - NULL); - if (type < 0) - die("%s not found: %s", - S_ISDIR(mode) ? "Tree" : "Blob", - command_buf.buf); - if (type != expected) - die("Not a %s (actually a %s): %s", - type_name(expected), type_name(type), - command_buf.buf); - } - - if (!*p) { - tree_content_replace(&b->branch_tree, &oid, mode, NULL); - return; - } - tree_content_set(&b->branch_tree, p, &oid, mode, NULL); -} - -static void file_change_d(const char *p, struct branch *b) -{ - static struct strbuf uq = STRBUF_INIT; - const char *endp; - - strbuf_reset(&uq); - if (!unquote_c_style(&uq, p, &endp)) { - if (*endp) - die("Garbage after path in: %s", command_buf.buf); - p = uq.buf; - } - tree_content_remove(&b->branch_tree, p, NULL, 1); -} - -static void file_change_cr(const char *s, struct branch *b, int rename) -{ - const char *d; - static struct strbuf s_uq = STRBUF_INIT; - static struct strbuf d_uq = STRBUF_INIT; - const char *endp; - struct tree_entry leaf; - - strbuf_reset(&s_uq); - if (!unquote_c_style(&s_uq, s, &endp)) { - if (*endp != ' ') - die("Missing space after source: %s", command_buf.buf); - } else { - endp = strchr(s, ' '); - if (!endp) - die("Missing space after source: %s", command_buf.buf); - strbuf_add(&s_uq, s, endp - s); - } - s = s_uq.buf; - - endp++; - if (!*endp) - die("Missing dest: %s", command_buf.buf); - - d = endp; - strbuf_reset(&d_uq); - if (!unquote_c_style(&d_uq, d, &endp)) { - if (*endp) - die("Garbage after dest in: %s", command_buf.buf); - d = d_uq.buf; - } - - memset(&leaf, 0, sizeof(leaf)); - if (rename) - tree_content_remove(&b->branch_tree, s, &leaf, 1); - else - tree_content_get(&b->branch_tree, s, &leaf, 1); - if (!leaf.versions[1].mode) - die("Path %s not in branch", s); - if (!*d) { /* C "path/to/subdir" "" */ - tree_content_replace(&b->branch_tree, - &leaf.versions[1].oid, - leaf.versions[1].mode, - leaf.tree); - return; - } - tree_content_set(&b->branch_tree, d, - &leaf.versions[1].oid, - leaf.versions[1].mode, - leaf.tree); -} - -static void note_change_n(const char *p, struct branch *b, unsigned char *old_fanout) -{ - static struct strbuf uq = STRBUF_INIT; - struct object_entry *oe; - struct branch *s; - struct object_id oid, commit_oid; - char path[GIT_MAX_RAWSZ * 3]; - uint16_t inline_data = 0; - unsigned char new_fanout; - - /* - * When loading a branch, we don't traverse its tree to count the real - * number of notes (too expensive to do this for all non-note refs). - * This means that recently loaded notes refs might incorrectly have - * b->num_notes == 0, and consequently, old_fanout might be wrong. - * - * Fix this by traversing the tree and counting the number of notes - * when b->num_notes == 0. If the notes tree is truly empty, the - * calculation should not take long. - */ - if (b->num_notes == 0 && *old_fanout == 0) { - /* Invoke change_note_fanout() in "counting mode". */ - b->num_notes = change_note_fanout(&b->branch_tree, 0xff); - *old_fanout = convert_num_notes_to_fanout(b->num_notes); - } - - /* Now parse the notemodify command. */ - /* <dataref> or 'inline' */ - if (*p == ':') { - oe = find_mark(marks, parse_mark_ref_space(&p)); - oidcpy(&oid, &oe->idx.oid); - } else if (skip_prefix(p, "inline ", &p)) { - inline_data = 1; - oe = NULL; /* not used with inline_data, but makes gcc happy */ - } else { - if (parse_mapped_oid_hex(p, &oid, &p)) - die("Invalid dataref: %s", command_buf.buf); - oe = find_object(&oid); - if (*p++ != ' ') - die("Missing space after SHA1: %s", command_buf.buf); - } - - /* <commit-ish> */ - s = lookup_branch(p); - if (s) { - if (is_null_oid(&s->oid)) - die("Can't add a note on empty branch."); - oidcpy(&commit_oid, &s->oid); - } else if (*p == ':') { - uintmax_t commit_mark = parse_mark_ref_eol(p); - struct object_entry *commit_oe = find_mark(marks, commit_mark); - if (commit_oe->type != OBJ_COMMIT) - die("Mark :%" PRIuMAX " not a commit", commit_mark); - oidcpy(&commit_oid, &commit_oe->idx.oid); - } else if (!get_oid(p, &commit_oid)) { - unsigned long size; - char *buf = read_object_with_reference(the_repository, - &commit_oid, - commit_type, &size, - &commit_oid); - if (!buf || size < the_hash_algo->hexsz + 6) - die("Not a valid commit: %s", p); - free(buf); - } else - die("Invalid ref name or SHA1 expression: %s", p); - - if (inline_data) { - if (p != uq.buf) { - strbuf_addstr(&uq, p); - p = uq.buf; - } - read_next_command(); - parse_and_store_blob(&last_blob, &oid, 0); - } else if (oe) { - if (oe->type != OBJ_BLOB) - die("Not a blob (actually a %s): %s", - type_name(oe->type), command_buf.buf); - } else if (!is_null_oid(&oid)) { - enum object_type type = oid_object_info(the_repository, &oid, - NULL); - if (type < 0) - die("Blob not found: %s", command_buf.buf); - if (type != OBJ_BLOB) - die("Not a blob (actually a %s): %s", - type_name(type), command_buf.buf); - } - - construct_path_with_fanout(oid_to_hex(&commit_oid), *old_fanout, path); - if (tree_content_remove(&b->branch_tree, path, NULL, 0)) - b->num_notes--; - - if (is_null_oid(&oid)) - return; /* nothing to insert */ - - b->num_notes++; - new_fanout = convert_num_notes_to_fanout(b->num_notes); - construct_path_with_fanout(oid_to_hex(&commit_oid), new_fanout, path); - tree_content_set(&b->branch_tree, path, &oid, S_IFREG | 0644, NULL); -} - -static void file_change_deleteall(struct branch *b) -{ - release_tree_content_recursive(b->branch_tree.tree); - oidclr(&b->branch_tree.versions[0].oid); - oidclr(&b->branch_tree.versions[1].oid); - load_tree(&b->branch_tree); - b->num_notes = 0; -} - -static void parse_from_commit(struct branch *b, char *buf, unsigned long size) -{ - if (!buf || size < the_hash_algo->hexsz + 6) - die("Not a valid commit: %s", oid_to_hex(&b->oid)); - if (memcmp("tree ", buf, 5) - || get_oid_hex(buf + 5, &b->branch_tree.versions[1].oid)) - die("The commit %s is corrupt", oid_to_hex(&b->oid)); - oidcpy(&b->branch_tree.versions[0].oid, - &b->branch_tree.versions[1].oid); -} - -static void parse_from_existing(struct branch *b) -{ - if (is_null_oid(&b->oid)) { - oidclr(&b->branch_tree.versions[0].oid); - oidclr(&b->branch_tree.versions[1].oid); - } else { - unsigned long size; - char *buf; - - buf = read_object_with_reference(the_repository, - &b->oid, commit_type, &size, - &b->oid); - parse_from_commit(b, buf, size); - free(buf); - } -} - -static int parse_objectish(struct branch *b, const char *objectish) -{ - struct branch *s; - struct object_id oid; - - oidcpy(&oid, &b->branch_tree.versions[1].oid); - - s = lookup_branch(objectish); - if (b == s) - die("Can't create a branch from itself: %s", b->name); - else if (s) { - struct object_id *t = &s->branch_tree.versions[1].oid; - oidcpy(&b->oid, &s->oid); - oidcpy(&b->branch_tree.versions[0].oid, t); - oidcpy(&b->branch_tree.versions[1].oid, t); - } else if (*objectish == ':') { - uintmax_t idnum = parse_mark_ref_eol(objectish); - struct object_entry *oe = find_mark(marks, idnum); - if (oe->type != OBJ_COMMIT) - die("Mark :%" PRIuMAX " not a commit", idnum); - if (!oideq(&b->oid, &oe->idx.oid)) { - oidcpy(&b->oid, &oe->idx.oid); - if (oe->pack_id != MAX_PACK_ID) { - unsigned long size; - char *buf = gfi_unpack_entry(oe, &size); - parse_from_commit(b, buf, size); - free(buf); - } else - parse_from_existing(b); - } - } else if (!get_oid(objectish, &b->oid)) { - parse_from_existing(b); - if (is_null_oid(&b->oid)) - b->delete = 1; - } - else - die("Invalid ref name or SHA1 expression: %s", objectish); - - if (b->branch_tree.tree && !oideq(&oid, &b->branch_tree.versions[1].oid)) { - release_tree_content_recursive(b->branch_tree.tree); - b->branch_tree.tree = NULL; - } - - read_next_command(); - return 1; -} - -static int parse_from(struct branch *b) -{ - const char *from; - - if (!skip_prefix(command_buf.buf, "from ", &from)) - return 0; - - return parse_objectish(b, from); -} - -static int parse_objectish_with_prefix(struct branch *b, const char *prefix) -{ - const char *base; - - if (!skip_prefix(command_buf.buf, prefix, &base)) - return 0; - - return parse_objectish(b, base); -} - -static struct hash_list *parse_merge(unsigned int *count) -{ - struct hash_list *list = NULL, **tail = &list, *n; - const char *from; - struct branch *s; - - *count = 0; - while (skip_prefix(command_buf.buf, "merge ", &from)) { - n = xmalloc(sizeof(*n)); - s = lookup_branch(from); - if (s) - oidcpy(&n->oid, &s->oid); - else if (*from == ':') { - uintmax_t idnum = parse_mark_ref_eol(from); - struct object_entry *oe = find_mark(marks, idnum); - if (oe->type != OBJ_COMMIT) - die("Mark :%" PRIuMAX " not a commit", idnum); - oidcpy(&n->oid, &oe->idx.oid); - } else if (!get_oid(from, &n->oid)) { - unsigned long size; - char *buf = read_object_with_reference(the_repository, - &n->oid, - commit_type, - &size, &n->oid); - if (!buf || size < the_hash_algo->hexsz + 6) - die("Not a valid commit: %s", from); - free(buf); - } else - die("Invalid ref name or SHA1 expression: %s", from); - - n->next = NULL; - *tail = n; - tail = &n->next; - - (*count)++; - read_next_command(); - } - return list; -} - -static void parse_new_commit(const char *arg) -{ - static struct strbuf msg = STRBUF_INIT; - struct branch *b; - char *author = NULL; - char *committer = NULL; - char *encoding = NULL; - struct hash_list *merge_list = NULL; - unsigned int merge_count; - unsigned char prev_fanout, new_fanout; - const char *v; - - b = lookup_branch(arg); - if (!b) - b = new_branch(arg); - - read_next_command(); - parse_mark(); - parse_original_identifier(); - if (skip_prefix(command_buf.buf, "author ", &v)) { - author = parse_ident(v); - read_next_command(); - } - if (skip_prefix(command_buf.buf, "committer ", &v)) { - committer = parse_ident(v); - read_next_command(); - } - if (!committer) - die("Expected committer but didn't get one"); - if (skip_prefix(command_buf.buf, "encoding ", &v)) { - encoding = xstrdup(v); - read_next_command(); - } - parse_data(&msg, 0, NULL); - read_next_command(); - parse_from(b); - merge_list = parse_merge(&merge_count); - - /* ensure the branch is active/loaded */ - if (!b->branch_tree.tree || !max_active_branches) { - unload_one_branch(); - load_branch(b); - } - - prev_fanout = convert_num_notes_to_fanout(b->num_notes); - - /* file_change* */ - while (command_buf.len > 0) { - if (skip_prefix(command_buf.buf, "M ", &v)) - file_change_m(v, b); - else if (skip_prefix(command_buf.buf, "D ", &v)) - file_change_d(v, b); - else if (skip_prefix(command_buf.buf, "R ", &v)) - file_change_cr(v, b, 1); - else if (skip_prefix(command_buf.buf, "C ", &v)) - file_change_cr(v, b, 0); - else if (skip_prefix(command_buf.buf, "N ", &v)) - note_change_n(v, b, &prev_fanout); - else if (!strcmp("deleteall", command_buf.buf)) - file_change_deleteall(b); - else if (skip_prefix(command_buf.buf, "ls ", &v)) - parse_ls(v, b); - else if (skip_prefix(command_buf.buf, "cat-blob ", &v)) - parse_cat_blob(v); - else { - unread_command_buf = 1; - break; - } - if (read_next_command() == EOF) - break; - } - - new_fanout = convert_num_notes_to_fanout(b->num_notes); - if (new_fanout != prev_fanout) - b->num_notes = change_note_fanout(&b->branch_tree, new_fanout); - - /* build the tree and the commit */ - store_tree(&b->branch_tree); - oidcpy(&b->branch_tree.versions[0].oid, - &b->branch_tree.versions[1].oid); - - strbuf_reset(&new_data); - strbuf_addf(&new_data, "tree %s\n", - oid_to_hex(&b->branch_tree.versions[1].oid)); - if (!is_null_oid(&b->oid)) - strbuf_addf(&new_data, "parent %s\n", - oid_to_hex(&b->oid)); - while (merge_list) { - struct hash_list *next = merge_list->next; - strbuf_addf(&new_data, "parent %s\n", - oid_to_hex(&merge_list->oid)); - free(merge_list); - merge_list = next; - } - strbuf_addf(&new_data, - "author %s\n" - "committer %s\n", - author ? author : committer, committer); - if (encoding) - strbuf_addf(&new_data, - "encoding %s\n", - encoding); - strbuf_addch(&new_data, '\n'); - strbuf_addbuf(&new_data, &msg); - free(author); - free(committer); - free(encoding); - - if (!store_object(OBJ_COMMIT, &new_data, NULL, &b->oid, next_mark)) - b->pack_id = pack_id; - b->last_commit = object_count_by_type[OBJ_COMMIT]; -} - -static void parse_new_tag(const char *arg) -{ - static struct strbuf msg = STRBUF_INIT; - const char *from; - char *tagger; - struct branch *s; - struct tag *t; - uintmax_t from_mark = 0; - struct object_id oid; - enum object_type type; - const char *v; - - t = mem_pool_alloc(&fi_mem_pool, sizeof(struct tag)); - memset(t, 0, sizeof(struct tag)); - t->name = mem_pool_strdup(&fi_mem_pool, arg); - if (last_tag) - last_tag->next_tag = t; - else - first_tag = t; - last_tag = t; - read_next_command(); - parse_mark(); - - /* from ... */ - if (!skip_prefix(command_buf.buf, "from ", &from)) - die("Expected from command, got %s", command_buf.buf); - s = lookup_branch(from); - if (s) { - if (is_null_oid(&s->oid)) - die("Can't tag an empty branch."); - oidcpy(&oid, &s->oid); - type = OBJ_COMMIT; - } else if (*from == ':') { - struct object_entry *oe; - from_mark = parse_mark_ref_eol(from); - oe = find_mark(marks, from_mark); - type = oe->type; - oidcpy(&oid, &oe->idx.oid); - } else if (!get_oid(from, &oid)) { - struct object_entry *oe = find_object(&oid); - if (!oe) { - type = oid_object_info(the_repository, &oid, NULL); - if (type < 0) - die("Not a valid object: %s", from); - } else - type = oe->type; - } else - die("Invalid ref name or SHA1 expression: %s", from); - read_next_command(); - - /* original-oid ... */ - parse_original_identifier(); - - /* tagger ... */ - if (skip_prefix(command_buf.buf, "tagger ", &v)) { - tagger = parse_ident(v); - read_next_command(); - } else - tagger = NULL; - - /* tag payload/message */ - parse_data(&msg, 0, NULL); - - /* build the tag object */ - strbuf_reset(&new_data); - - strbuf_addf(&new_data, - "object %s\n" - "type %s\n" - "tag %s\n", - oid_to_hex(&oid), type_name(type), t->name); - if (tagger) - strbuf_addf(&new_data, - "tagger %s\n", tagger); - strbuf_addch(&new_data, '\n'); - strbuf_addbuf(&new_data, &msg); - free(tagger); - - if (store_object(OBJ_TAG, &new_data, NULL, &t->oid, next_mark)) - t->pack_id = MAX_PACK_ID; - else - t->pack_id = pack_id; -} - -static void parse_reset_branch(const char *arg) -{ - struct branch *b; - const char *tag_name; - - b = lookup_branch(arg); - if (b) { - oidclr(&b->oid); - oidclr(&b->branch_tree.versions[0].oid); - oidclr(&b->branch_tree.versions[1].oid); - if (b->branch_tree.tree) { - release_tree_content_recursive(b->branch_tree.tree); - b->branch_tree.tree = NULL; - } - } - else - b = new_branch(arg); - read_next_command(); - parse_from(b); - if (b->delete && skip_prefix(b->name, "refs/tags/", &tag_name)) { - /* - * Elsewhere, we call dump_branches() before dump_tags(), - * and dump_branches() will handle ref deletions first, so - * in order to make sure the deletion actually takes effect, - * we need to remove the tag from our list of tags to update. - * - * NEEDSWORK: replace list of tags with hashmap for faster - * deletion? - */ - struct tag *t, *prev = NULL; - for (t = first_tag; t; t = t->next_tag) { - if (!strcmp(t->name, tag_name)) - break; - prev = t; - } - if (t) { - if (prev) - prev->next_tag = t->next_tag; - else - first_tag = t->next_tag; - if (!t->next_tag) - last_tag = prev; - /* There is no mem_pool_free(t) function to call. */ - } - } - if (command_buf.len > 0) - unread_command_buf = 1; -} - -static void cat_blob_write(const char *buf, unsigned long size) -{ - if (write_in_full(cat_blob_fd, buf, size) < 0) - die_errno("Write to frontend failed"); -} - -static void cat_blob(struct object_entry *oe, struct object_id *oid) -{ - struct strbuf line = STRBUF_INIT; - unsigned long size; - enum object_type type = 0; - char *buf; - - if (!oe || oe->pack_id == MAX_PACK_ID) { - buf = read_object_file(oid, &type, &size); - } else { - type = oe->type; - buf = gfi_unpack_entry(oe, &size); - } - - /* - * Output based on batch_one_object() from cat-file.c. - */ - if (type <= 0) { - strbuf_reset(&line); - strbuf_addf(&line, "%s missing\n", oid_to_hex(oid)); - cat_blob_write(line.buf, line.len); - strbuf_release(&line); - free(buf); - return; - } - if (!buf) - die("Can't read object %s", oid_to_hex(oid)); - if (type != OBJ_BLOB) - die("Object %s is a %s but a blob was expected.", - oid_to_hex(oid), type_name(type)); - strbuf_reset(&line); - strbuf_addf(&line, "%s %s %"PRIuMAX"\n", oid_to_hex(oid), - type_name(type), (uintmax_t)size); - cat_blob_write(line.buf, line.len); - strbuf_release(&line); - cat_blob_write(buf, size); - cat_blob_write("\n", 1); - if (oe && oe->pack_id == pack_id) { - last_blob.offset = oe->idx.offset; - strbuf_attach(&last_blob.data, buf, size, size); - last_blob.depth = oe->depth; - } else - free(buf); -} - -static void parse_get_mark(const char *p) -{ - struct object_entry *oe; - char output[GIT_MAX_HEXSZ + 2]; - - /* get-mark SP <object> LF */ - if (*p != ':') - die("Not a mark: %s", p); - - oe = find_mark(marks, parse_mark_ref_eol(p)); - if (!oe) - die("Unknown mark: %s", command_buf.buf); - - xsnprintf(output, sizeof(output), "%s\n", oid_to_hex(&oe->idx.oid)); - cat_blob_write(output, the_hash_algo->hexsz + 1); -} - -static void parse_cat_blob(const char *p) -{ - struct object_entry *oe; - struct object_id oid; - - /* cat-blob SP <object> LF */ - if (*p == ':') { - oe = find_mark(marks, parse_mark_ref_eol(p)); - if (!oe) - die("Unknown mark: %s", command_buf.buf); - oidcpy(&oid, &oe->idx.oid); - } else { - if (parse_mapped_oid_hex(p, &oid, &p)) - die("Invalid dataref: %s", command_buf.buf); - if (*p) - die("Garbage after SHA1: %s", command_buf.buf); - oe = find_object(&oid); - } - - cat_blob(oe, &oid); -} - -static struct object_entry *dereference(struct object_entry *oe, - struct object_id *oid) -{ - unsigned long size; - char *buf = NULL; - const unsigned hexsz = the_hash_algo->hexsz; - - if (!oe) { - enum object_type type = oid_object_info(the_repository, oid, - NULL); - if (type < 0) - die("object not found: %s", oid_to_hex(oid)); - /* cache it! */ - oe = insert_object(oid); - oe->type = type; - oe->pack_id = MAX_PACK_ID; - oe->idx.offset = 1; - } - switch (oe->type) { - case OBJ_TREE: /* easy case. */ - return oe; - case OBJ_COMMIT: - case OBJ_TAG: - break; - default: - die("Not a tree-ish: %s", command_buf.buf); - } - - if (oe->pack_id != MAX_PACK_ID) { /* in a pack being written */ - buf = gfi_unpack_entry(oe, &size); - } else { - enum object_type unused; - buf = read_object_file(oid, &unused, &size); - } - if (!buf) - die("Can't load object %s", oid_to_hex(oid)); - - /* Peel one layer. */ - switch (oe->type) { - case OBJ_TAG: - if (size < hexsz + strlen("object ") || - get_oid_hex(buf + strlen("object "), oid)) - die("Invalid SHA1 in tag: %s", command_buf.buf); - break; - case OBJ_COMMIT: - if (size < hexsz + strlen("tree ") || - get_oid_hex(buf + strlen("tree "), oid)) - die("Invalid SHA1 in commit: %s", command_buf.buf); - } - - free(buf); - return find_object(oid); -} - -static void insert_mapped_mark(uintmax_t mark, void *object, void *cbp) -{ - struct object_id *fromoid = object; - struct object_id *tooid = find_mark(cbp, mark); - int ret; - khiter_t it; - - it = kh_put_oid_map(sub_oid_map, *fromoid, &ret); - /* We've already seen this object. */ - if (ret == 0) - return; - kh_value(sub_oid_map, it) = tooid; -} - -static void build_mark_map_one(struct mark_set *from, struct mark_set *to) -{ - for_each_mark(from, 0, insert_mapped_mark, to); -} - -static void build_mark_map(struct string_list *from, struct string_list *to) -{ - struct string_list_item *fromp, *top; - - sub_oid_map = kh_init_oid_map(); - - for_each_string_list_item(fromp, from) { - top = string_list_lookup(to, fromp->string); - if (!fromp->util) { - die(_("Missing from marks for submodule '%s'"), fromp->string); - } else if (!top || !top->util) { - die(_("Missing to marks for submodule '%s'"), fromp->string); - } - build_mark_map_one(fromp->util, top->util); - } -} - -static struct object_entry *parse_treeish_dataref(const char **p) -{ - struct object_id oid; - struct object_entry *e; - - if (**p == ':') { /* <mark> */ - e = find_mark(marks, parse_mark_ref_space(p)); - if (!e) - die("Unknown mark: %s", command_buf.buf); - oidcpy(&oid, &e->idx.oid); - } else { /* <sha1> */ - if (parse_mapped_oid_hex(*p, &oid, p)) - die("Invalid dataref: %s", command_buf.buf); - e = find_object(&oid); - if (*(*p)++ != ' ') - die("Missing space after tree-ish: %s", command_buf.buf); - } - - while (!e || e->type != OBJ_TREE) - e = dereference(e, &oid); - return e; -} - -static void print_ls(int mode, const unsigned char *hash, const char *path) -{ - static struct strbuf line = STRBUF_INIT; - - /* See show_tree(). */ - const char *type = - S_ISGITLINK(mode) ? commit_type : - S_ISDIR(mode) ? tree_type : - blob_type; - - if (!mode) { - /* missing SP path LF */ - strbuf_reset(&line); - strbuf_addstr(&line, "missing "); - quote_c_style(path, &line, NULL, 0); - strbuf_addch(&line, '\n'); - } else { - /* mode SP type SP object_name TAB path LF */ - strbuf_reset(&line); - strbuf_addf(&line, "%06o %s %s\t", - mode & ~NO_DELTA, type, hash_to_hex(hash)); - quote_c_style(path, &line, NULL, 0); - strbuf_addch(&line, '\n'); - } - cat_blob_write(line.buf, line.len); -} - -static void parse_ls(const char *p, struct branch *b) -{ - struct tree_entry *root = NULL; - struct tree_entry leaf = {NULL}; - - /* ls SP (<tree-ish> SP)? <path> */ - if (*p == '"') { - if (!b) - die("Not in a commit: %s", command_buf.buf); - root = &b->branch_tree; - } else { - struct object_entry *e = parse_treeish_dataref(&p); - root = new_tree_entry(); - oidcpy(&root->versions[1].oid, &e->idx.oid); - if (!is_null_oid(&root->versions[1].oid)) - root->versions[1].mode = S_IFDIR; - load_tree(root); - } - if (*p == '"') { - static struct strbuf uq = STRBUF_INIT; - const char *endp; - strbuf_reset(&uq); - if (unquote_c_style(&uq, p, &endp)) - die("Invalid path: %s", command_buf.buf); - if (*endp) - die("Garbage after path in: %s", command_buf.buf); - p = uq.buf; - } - tree_content_get(root, p, &leaf, 1); - /* - * A directory in preparation would have a sha1 of zero - * until it is saved. Save, for simplicity. - */ - if (S_ISDIR(leaf.versions[1].mode)) - store_tree(&leaf); - - print_ls(leaf.versions[1].mode, leaf.versions[1].oid.hash, p); - if (leaf.tree) - release_tree_content_recursive(leaf.tree); - if (!b || root != &b->branch_tree) - release_tree_entry(root); -} - -static void checkpoint(void) -{ - checkpoint_requested = 0; - if (object_count) { - cycle_packfile(); - } - dump_branches(); - dump_tags(); - dump_marks(); -} - -static void parse_checkpoint(void) -{ - checkpoint_requested = 1; - skip_optional_lf(); -} - -static void parse_progress(void) -{ - fwrite(command_buf.buf, 1, command_buf.len, stdout); - fputc('\n', stdout); - fflush(stdout); - skip_optional_lf(); -} - -static void parse_alias(void) -{ - struct object_entry *e; - struct branch b; - - skip_optional_lf(); - read_next_command(); - - /* mark ... */ - parse_mark(); - if (!next_mark) - die(_("Expected 'mark' command, got %s"), command_buf.buf); - - /* to ... */ - memset(&b, 0, sizeof(b)); - if (!parse_objectish_with_prefix(&b, "to ")) - die(_("Expected 'to' command, got %s"), command_buf.buf); - e = find_object(&b.oid); - assert(e); - insert_mark(marks, next_mark, e); -} - -static char* make_fast_import_path(const char *path) -{ - if (!relative_marks_paths || is_absolute_path(path)) - return xstrdup(path); - return git_pathdup("info/fast-import/%s", path); -} - -static void option_import_marks(const char *marks, - int from_stream, int ignore_missing) -{ - if (import_marks_file) { - if (from_stream) - die("Only one import-marks command allowed per stream"); - - /* read previous mark file */ - if(!import_marks_file_from_stream) - read_marks(); - } - - import_marks_file = make_fast_import_path(marks); - import_marks_file_from_stream = from_stream; - import_marks_file_ignore_missing = ignore_missing; -} - -static void option_date_format(const char *fmt) -{ - if (!strcmp(fmt, "raw")) - whenspec = WHENSPEC_RAW; - else if (!strcmp(fmt, "raw-permissive")) - whenspec = WHENSPEC_RAW_PERMISSIVE; - else if (!strcmp(fmt, "rfc2822")) - whenspec = WHENSPEC_RFC2822; - else if (!strcmp(fmt, "now")) - whenspec = WHENSPEC_NOW; - else - die("unknown --date-format argument %s", fmt); -} - -static unsigned long ulong_arg(const char *option, const char *arg) -{ - char *endptr; - unsigned long rv = strtoul(arg, &endptr, 0); - if (strchr(arg, '-') || endptr == arg || *endptr) - die("%s: argument must be a non-negative integer", option); - return rv; -} - -static void option_depth(const char *depth) -{ - max_depth = ulong_arg("--depth", depth); - if (max_depth > MAX_DEPTH) - die("--depth cannot exceed %u", MAX_DEPTH); -} - -static void option_active_branches(const char *branches) -{ - max_active_branches = ulong_arg("--active-branches", branches); -} - -static void option_export_marks(const char *marks) -{ - export_marks_file = make_fast_import_path(marks); -} - -static void option_cat_blob_fd(const char *fd) -{ - unsigned long n = ulong_arg("--cat-blob-fd", fd); - if (n > (unsigned long) INT_MAX) - die("--cat-blob-fd cannot exceed %d", INT_MAX); - cat_blob_fd = (int) n; -} - -static void option_export_pack_edges(const char *edges) -{ - if (pack_edges) - fclose(pack_edges); - pack_edges = xfopen(edges, "a"); -} - -static void option_rewrite_submodules(const char *arg, struct string_list *list) -{ - struct mark_set *ms; - FILE *fp; - char *s = xstrdup(arg); - char *f = strchr(s, ':'); - if (!f) - die(_("Expected format name:filename for submodule rewrite option")); - *f = '\0'; - f++; - ms = xcalloc(1, sizeof(*ms)); - string_list_insert(list, s)->util = ms; - - fp = fopen(f, "r"); - if (!fp) - die_errno("cannot read '%s'", f); - read_mark_file(ms, fp, insert_oid_entry); - fclose(fp); -} - -static int parse_one_option(const char *option) -{ - if (skip_prefix(option, "max-pack-size=", &option)) { - unsigned long v; - if (!git_parse_ulong(option, &v)) - return 0; - if (v < 8192) { - warning("max-pack-size is now in bytes, assuming --max-pack-size=%lum", v); - v *= 1024 * 1024; - } else if (v < 1024 * 1024) { - warning("minimum max-pack-size is 1 MiB"); - v = 1024 * 1024; - } - max_packsize = v; - } else if (skip_prefix(option, "big-file-threshold=", &option)) { - unsigned long v; - if (!git_parse_ulong(option, &v)) - return 0; - big_file_threshold = v; - } else if (skip_prefix(option, "depth=", &option)) { - option_depth(option); - } else if (skip_prefix(option, "active-branches=", &option)) { - option_active_branches(option); - } else if (skip_prefix(option, "export-pack-edges=", &option)) { - option_export_pack_edges(option); - } else if (!strcmp(option, "quiet")) { - show_stats = 0; - } else if (!strcmp(option, "stats")) { - show_stats = 1; - } else if (!strcmp(option, "allow-unsafe-features")) { - ; /* already handled during early option parsing */ - } else { - return 0; - } - - return 1; -} - -static void check_unsafe_feature(const char *feature, int from_stream) -{ - if (from_stream && !allow_unsafe_features) - die(_("feature '%s' forbidden in input without --allow-unsafe-features"), - feature); -} - -static int parse_one_feature(const char *feature, int from_stream) -{ - const char *arg; - - if (skip_prefix(feature, "date-format=", &arg)) { - option_date_format(arg); - } else if (skip_prefix(feature, "import-marks=", &arg)) { - check_unsafe_feature("import-marks", from_stream); - option_import_marks(arg, from_stream, 0); - } else if (skip_prefix(feature, "import-marks-if-exists=", &arg)) { - check_unsafe_feature("import-marks-if-exists", from_stream); - option_import_marks(arg, from_stream, 1); - } else if (skip_prefix(feature, "export-marks=", &arg)) { - check_unsafe_feature(feature, from_stream); - option_export_marks(arg); - } else if (!strcmp(feature, "alias")) { - ; /* Don't die - this feature is supported */ - } else if (skip_prefix(feature, "rewrite-submodules-to=", &arg)) { - option_rewrite_submodules(arg, &sub_marks_to); - } else if (skip_prefix(feature, "rewrite-submodules-from=", &arg)) { - option_rewrite_submodules(arg, &sub_marks_from); - } else if (skip_prefix(feature, "rewrite-submodules-from=", &arg)) { - } else if (!strcmp(feature, "get-mark")) { - ; /* Don't die - this feature is supported */ - } else if (!strcmp(feature, "cat-blob")) { - ; /* Don't die - this feature is supported */ - } else if (!strcmp(feature, "relative-marks")) { - relative_marks_paths = 1; - } else if (!strcmp(feature, "no-relative-marks")) { - relative_marks_paths = 0; - } else if (!strcmp(feature, "done")) { - require_explicit_termination = 1; - } else if (!strcmp(feature, "force")) { - force_update = 1; - } else if (!strcmp(feature, "notes") || !strcmp(feature, "ls")) { - ; /* do nothing; we have the feature */ - } else { - return 0; - } - - return 1; -} - -static void parse_feature(const char *feature) -{ - if (seen_data_command) - die("Got feature command '%s' after data command", feature); - - if (parse_one_feature(feature, 1)) - return; - - die("This version of fast-import does not support feature %s.", feature); -} - -static void parse_option(const char *option) -{ - if (seen_data_command) - die("Got option command '%s' after data command", option); - - if (parse_one_option(option)) - return; - - die("This version of fast-import does not support option: %s", option); -} - -static void git_pack_config(void) -{ - int indexversion_value; - int limit; - unsigned long packsizelimit_value; - - if (!git_config_get_ulong("pack.depth", &max_depth)) { - if (max_depth > MAX_DEPTH) - max_depth = MAX_DEPTH; - } - if (!git_config_get_int("pack.indexversion", &indexversion_value)) { - pack_idx_opts.version = indexversion_value; - if (pack_idx_opts.version > 2) - git_die_config("pack.indexversion", - "bad pack.indexversion=%"PRIu32, pack_idx_opts.version); - } - if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value)) - max_packsize = packsizelimit_value; - - if (!git_config_get_int("fastimport.unpacklimit", &limit)) - unpack_limit = limit; - else if (!git_config_get_int("transfer.unpacklimit", &limit)) - unpack_limit = limit; - - git_config(git_default_config, NULL); -} - -static const char fast_import_usage[] = -"git fast-import [--date-format=<f>] [--max-pack-size=<n>] [--big-file-threshold=<n>] [--depth=<n>] [--active-branches=<n>] [--export-marks=<marks.file>]"; - -static void parse_argv(void) -{ - unsigned int i; - - for (i = 1; i < global_argc; i++) { - const char *a = global_argv[i]; - - if (*a != '-' || !strcmp(a, "--")) - break; - - if (!skip_prefix(a, "--", &a)) - die("unknown option %s", a); - - if (parse_one_option(a)) - continue; - - if (parse_one_feature(a, 0)) - continue; - - if (skip_prefix(a, "cat-blob-fd=", &a)) { - option_cat_blob_fd(a); - continue; - } - - die("unknown option --%s", a); - } - if (i != global_argc) - usage(fast_import_usage); - - seen_data_command = 1; - if (import_marks_file) - read_marks(); - build_mark_map(&sub_marks_from, &sub_marks_to); -} - -int cmd_fast_import(int argc, const char **argv, const char *prefix) -{ - unsigned int i; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage(fast_import_usage); - - reset_pack_idx_option(&pack_idx_opts); - git_pack_config(); - - alloc_objects(object_entry_alloc); - strbuf_init(&command_buf, 0); - atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*)); - branch_table = xcalloc(branch_table_sz, sizeof(struct branch*)); - avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*)); - marks = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set)); - - hashmap_init(&object_table, object_entry_hashcmp, NULL, 0); - - /* - * We don't parse most options until after we've seen the set of - * "feature" lines at the start of the stream (which allows the command - * line to override stream data). But we must do an early parse of any - * command-line options that impact how we interpret the feature lines. - */ - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (*arg != '-' || !strcmp(arg, "--")) - break; - if (!strcmp(arg, "--allow-unsafe-features")) - allow_unsafe_features = 1; - } - - global_argc = argc; - global_argv = argv; - - rc_free = mem_pool_alloc(&fi_mem_pool, cmd_save * sizeof(*rc_free)); - for (i = 0; i < (cmd_save - 1); i++) - rc_free[i].next = &rc_free[i + 1]; - rc_free[cmd_save - 1].next = NULL; - - start_packfile(); - set_die_routine(die_nicely); - set_checkpoint_signal(); - while (read_next_command() != EOF) { - const char *v; - if (!strcmp("blob", command_buf.buf)) - parse_new_blob(); - else if (skip_prefix(command_buf.buf, "commit ", &v)) - parse_new_commit(v); - else if (skip_prefix(command_buf.buf, "tag ", &v)) - parse_new_tag(v); - else if (skip_prefix(command_buf.buf, "reset ", &v)) - parse_reset_branch(v); - else if (skip_prefix(command_buf.buf, "ls ", &v)) - parse_ls(v, NULL); - else if (skip_prefix(command_buf.buf, "cat-blob ", &v)) - parse_cat_blob(v); - else if (skip_prefix(command_buf.buf, "get-mark ", &v)) - parse_get_mark(v); - else if (!strcmp("checkpoint", command_buf.buf)) - parse_checkpoint(); - else if (!strcmp("done", command_buf.buf)) - break; - else if (!strcmp("alias", command_buf.buf)) - parse_alias(); - else if (starts_with(command_buf.buf, "progress ")) - parse_progress(); - else if (skip_prefix(command_buf.buf, "feature ", &v)) - parse_feature(v); - else if (skip_prefix(command_buf.buf, "option git ", &v)) - parse_option(v); - else if (starts_with(command_buf.buf, "option ")) - /* ignore non-git options*/; - else - die("Unsupported command: %s", command_buf.buf); - - if (checkpoint_requested) - checkpoint(); - } - - /* argv hasn't been parsed yet, do so */ - if (!seen_data_command) - parse_argv(); - - if (require_explicit_termination && feof(stdin)) - die("stream ends early"); - - end_packfile(); - - dump_branches(); - dump_tags(); - unkeep_all_packs(); - dump_marks(); - - if (pack_edges) - fclose(pack_edges); - - if (show_stats) { - uintmax_t total_count = 0, duplicate_count = 0; - for (i = 0; i < ARRAY_SIZE(object_count_by_type); i++) - total_count += object_count_by_type[i]; - for (i = 0; i < ARRAY_SIZE(duplicate_count_by_type); i++) - duplicate_count += duplicate_count_by_type[i]; - - fprintf(stderr, "%s statistics:\n", argv[0]); - fprintf(stderr, "---------------------------------------------------------------------\n"); - fprintf(stderr, "Alloc'd objects: %10" PRIuMAX "\n", alloc_count); - fprintf(stderr, "Total objects: %10" PRIuMAX " (%10" PRIuMAX " duplicates )\n", total_count, duplicate_count); - fprintf(stderr, " blobs : %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas of %10" PRIuMAX" attempts)\n", object_count_by_type[OBJ_BLOB], duplicate_count_by_type[OBJ_BLOB], delta_count_by_type[OBJ_BLOB], delta_count_attempts_by_type[OBJ_BLOB]); - fprintf(stderr, " trees : %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas of %10" PRIuMAX" attempts)\n", object_count_by_type[OBJ_TREE], duplicate_count_by_type[OBJ_TREE], delta_count_by_type[OBJ_TREE], delta_count_attempts_by_type[OBJ_TREE]); - fprintf(stderr, " commits: %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas of %10" PRIuMAX" attempts)\n", object_count_by_type[OBJ_COMMIT], duplicate_count_by_type[OBJ_COMMIT], delta_count_by_type[OBJ_COMMIT], delta_count_attempts_by_type[OBJ_COMMIT]); - fprintf(stderr, " tags : %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas of %10" PRIuMAX" attempts)\n", object_count_by_type[OBJ_TAG], duplicate_count_by_type[OBJ_TAG], delta_count_by_type[OBJ_TAG], delta_count_attempts_by_type[OBJ_TAG]); - fprintf(stderr, "Total branches: %10lu (%10lu loads )\n", branch_count, branch_load_count); - fprintf(stderr, " marks: %10" PRIuMAX " (%10" PRIuMAX " unique )\n", (((uintmax_t)1) << marks->shift) * 1024, marks_set_count); - fprintf(stderr, " atoms: %10u\n", atom_cnt); - fprintf(stderr, "Memory total: %10" PRIuMAX " KiB\n", (tree_entry_allocd + fi_mem_pool.pool_alloc + alloc_count*sizeof(struct object_entry))/1024); - fprintf(stderr, " pools: %10lu KiB\n", (unsigned long)((tree_entry_allocd + fi_mem_pool.pool_alloc) /1024)); - fprintf(stderr, " objects: %10" PRIuMAX " KiB\n", (alloc_count*sizeof(struct object_entry))/1024); - fprintf(stderr, "---------------------------------------------------------------------\n"); - pack_report(); - fprintf(stderr, "---------------------------------------------------------------------\n"); - fprintf(stderr, "\n"); - } - - return failure ? 1 : 0; -} diff --git a/third_party/git/builtin/fetch-pack.c b/third_party/git/builtin/fetch-pack.c deleted file mode 100644 index 58b7c1fbdc47..000000000000 --- a/third_party/git/builtin/fetch-pack.c +++ /dev/null @@ -1,271 +0,0 @@ -#include "builtin.h" -#include "pkt-line.h" -#include "fetch-pack.h" -#include "remote.h" -#include "connect.h" -#include "oid-array.h" -#include "protocol.h" - -static const char fetch_pack_usage[] = -"git fetch-pack [--all] [--stdin] [--quiet | -q] [--keep | -k] [--thin] " -"[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] " -"[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]"; - -static void add_sought_entry(struct ref ***sought, int *nr, int *alloc, - const char *name) -{ - struct ref *ref; - struct object_id oid; - const char *p; - - if (!parse_oid_hex(name, &oid, &p)) { - if (*p == ' ') { - /* <oid> <ref>, find refname */ - name = p + 1; - } else if (*p == '\0') { - ; /* <oid>, leave oid as name */ - } else { - /* <ref>, clear cruft from oid */ - oidclr(&oid); - } - } else { - /* <ref>, clear cruft from get_oid_hex */ - oidclr(&oid); - } - - ref = alloc_ref(name); - oidcpy(&ref->old_oid, &oid); - (*nr)++; - ALLOC_GROW(*sought, *nr, *alloc); - (*sought)[*nr - 1] = ref; -} - -int cmd_fetch_pack(int argc, const char **argv, const char *prefix) -{ - int i, ret; - struct ref *ref = NULL; - const char *dest = NULL; - struct ref **sought = NULL; - int nr_sought = 0, alloc_sought = 0; - int fd[2]; - struct string_list pack_lockfiles = STRING_LIST_INIT_DUP; - struct string_list *pack_lockfiles_ptr = NULL; - struct child_process *conn; - struct fetch_pack_args args; - struct oid_array shallow = OID_ARRAY_INIT; - struct string_list deepen_not = STRING_LIST_INIT_DUP; - struct packet_reader reader; - enum protocol_version version; - - fetch_if_missing = 0; - - packet_trace_identity("fetch-pack"); - - memset(&args, 0, sizeof(args)); - args.uploadpack = "git-upload-pack"; - - for (i = 1; i < argc && *argv[i] == '-'; i++) { - const char *arg = argv[i]; - - if (skip_prefix(arg, "--upload-pack=", &arg)) { - args.uploadpack = arg; - continue; - } - if (skip_prefix(arg, "--exec=", &arg)) { - args.uploadpack = arg; - continue; - } - if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) { - args.quiet = 1; - continue; - } - if (!strcmp("--keep", arg) || !strcmp("-k", arg)) { - args.lock_pack = args.keep_pack; - args.keep_pack = 1; - continue; - } - if (!strcmp("--thin", arg)) { - args.use_thin_pack = 1; - continue; - } - if (!strcmp("--include-tag", arg)) { - args.include_tag = 1; - continue; - } - if (!strcmp("--all", arg)) { - args.fetch_all = 1; - continue; - } - if (!strcmp("--stdin", arg)) { - args.stdin_refs = 1; - continue; - } - if (!strcmp("--diag-url", arg)) { - args.diag_url = 1; - continue; - } - if (!strcmp("-v", arg)) { - args.verbose = 1; - continue; - } - if (skip_prefix(arg, "--depth=", &arg)) { - args.depth = strtol(arg, NULL, 0); - continue; - } - if (skip_prefix(arg, "--shallow-since=", &arg)) { - args.deepen_since = xstrdup(arg); - continue; - } - if (skip_prefix(arg, "--shallow-exclude=", &arg)) { - string_list_append(&deepen_not, arg); - continue; - } - if (!strcmp(arg, "--deepen-relative")) { - args.deepen_relative = 1; - continue; - } - if (!strcmp("--no-progress", arg)) { - args.no_progress = 1; - continue; - } - if (!strcmp("--stateless-rpc", arg)) { - args.stateless_rpc = 1; - continue; - } - if (!strcmp("--lock-pack", arg)) { - args.lock_pack = 1; - pack_lockfiles_ptr = &pack_lockfiles; - continue; - } - if (!strcmp("--check-self-contained-and-connected", arg)) { - args.check_self_contained_and_connected = 1; - continue; - } - if (!strcmp("--cloning", arg)) { - args.cloning = 1; - continue; - } - if (!strcmp("--update-shallow", arg)) { - args.update_shallow = 1; - continue; - } - if (!strcmp("--from-promisor", arg)) { - args.from_promisor = 1; - continue; - } - if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) { - parse_list_objects_filter(&args.filter_options, arg); - continue; - } - if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) { - list_objects_filter_set_no_filter(&args.filter_options); - continue; - } - usage(fetch_pack_usage); - } - if (deepen_not.nr) - args.deepen_not = &deepen_not; - - if (i < argc) - dest = argv[i++]; - else - usage(fetch_pack_usage); - - /* - * Copy refs from cmdline to growable list, then append any - * refs from the standard input: - */ - for (; i < argc; i++) - add_sought_entry(&sought, &nr_sought, &alloc_sought, argv[i]); - if (args.stdin_refs) { - if (args.stateless_rpc) { - /* in stateless RPC mode we use pkt-line to read - * from stdin, until we get a flush packet - */ - for (;;) { - char *line = packet_read_line(0, NULL); - if (!line) - break; - add_sought_entry(&sought, &nr_sought, &alloc_sought, line); - } - } - else { - /* read from stdin one ref per line, until EOF */ - struct strbuf line = STRBUF_INIT; - while (strbuf_getline_lf(&line, stdin) != EOF) - add_sought_entry(&sought, &nr_sought, &alloc_sought, line.buf); - strbuf_release(&line); - } - } - - if (args.stateless_rpc) { - conn = NULL; - fd[0] = 0; - fd[1] = 1; - } else { - int flags = args.verbose ? CONNECT_VERBOSE : 0; - if (args.diag_url) - flags |= CONNECT_DIAG_URL; - conn = git_connect(fd, dest, args.uploadpack, - flags); - if (!conn) - return args.diag_url ? 0 : 1; - } - - packet_reader_init(&reader, fd[0], NULL, 0, - PACKET_READ_CHOMP_NEWLINE | - PACKET_READ_GENTLE_ON_EOF | - PACKET_READ_DIE_ON_ERR_PACKET); - - version = discover_version(&reader); - switch (version) { - case protocol_v2: - get_remote_refs(fd[1], &reader, &ref, 0, NULL, NULL, args.stateless_rpc); - break; - case protocol_v1: - case protocol_v0: - get_remote_heads(&reader, &ref, 0, NULL, &shallow); - break; - case protocol_unknown_version: - BUG("unknown protocol version"); - } - - ref = fetch_pack(&args, fd, ref, sought, nr_sought, - &shallow, pack_lockfiles_ptr, version); - if (pack_lockfiles.nr) { - int i; - - printf("lock %s\n", pack_lockfiles.items[0].string); - fflush(stdout); - for (i = 1; i < pack_lockfiles.nr; i++) - warning(_("Lockfile created but not reported: %s"), - pack_lockfiles.items[i].string); - } - if (args.check_self_contained_and_connected && - args.self_contained_and_connected) { - printf("connectivity-ok\n"); - fflush(stdout); - } - close(fd[0]); - close(fd[1]); - if (finish_connect(conn)) - return 1; - - ret = !ref; - - /* - * If the heads to pull were given, we should have consumed - * all of them by matching the remote. Otherwise, 'git fetch - * remote no-such-ref' would silently succeed without issuing - * an error. - */ - ret |= report_unmatched_refs(sought, nr_sought); - - while (ref) { - printf("%s %s\n", - oid_to_hex(&ref->old_oid), ref->name); - ref = ref->next; - } - - return ret; -} diff --git a/third_party/git/builtin/fetch.c b/third_party/git/builtin/fetch.c deleted file mode 100644 index f9c3c49f14d9..000000000000 --- a/third_party/git/builtin/fetch.c +++ /dev/null @@ -1,1943 +0,0 @@ -/* - * "git fetch" - */ -#include "cache.h" -#include "config.h" -#include "repository.h" -#include "refs.h" -#include "refspec.h" -#include "object-store.h" -#include "oidset.h" -#include "commit.h" -#include "builtin.h" -#include "string-list.h" -#include "remote.h" -#include "transport.h" -#include "run-command.h" -#include "parse-options.h" -#include "sigchain.h" -#include "submodule-config.h" -#include "submodule.h" -#include "connected.h" -#include "strvec.h" -#include "utf8.h" -#include "packfile.h" -#include "list-objects-filter-options.h" -#include "commit-reach.h" -#include "branch.h" -#include "promisor-remote.h" -#include "commit-graph.h" -#include "shallow.h" - -#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000) - -static const char * const builtin_fetch_usage[] = { - N_("git fetch [<options>] [<repository> [<refspec>...]]"), - N_("git fetch [<options>] <group>"), - N_("git fetch --multiple [<options>] [(<repository> | <group>)...]"), - N_("git fetch --all [<options>]"), - NULL -}; - -enum { - TAGS_UNSET = 0, - TAGS_DEFAULT = 1, - TAGS_SET = 2 -}; - -static int fetch_prune_config = -1; /* unspecified */ -static int fetch_show_forced_updates = 1; -static uint64_t forced_updates_ms = 0; -static int prune = -1; /* unspecified */ -#define PRUNE_BY_DEFAULT 0 /* do we prune by default? */ - -static int fetch_prune_tags_config = -1; /* unspecified */ -static int prune_tags = -1; /* unspecified */ -#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */ - -static int all, append, dry_run, force, keep, multiple, update_head_ok; -static int write_fetch_head = 1; -static int verbosity, deepen_relative, set_upstream; -static int progress = -1; -static int enable_auto_gc = 1; -static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen; -static int max_jobs = -1, submodule_fetch_jobs_config = -1; -static int fetch_parallel_config = 1; -static enum transport_family family; -static const char *depth; -static const char *deepen_since; -static const char *upload_pack; -static struct string_list deepen_not = STRING_LIST_INIT_NODUP; -static struct strbuf default_rla = STRBUF_INIT; -static struct transport *gtransport; -static struct transport *gsecondary; -static const char *submodule_prefix = ""; -static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; -static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND; -static int shown_url = 0; -static struct refspec refmap = REFSPEC_INIT_FETCH; -static struct list_objects_filter_options filter_options; -static struct string_list server_options = STRING_LIST_INIT_DUP; -static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP; -static int fetch_write_commit_graph = -1; -static int stdin_refspecs = 0; - -static int git_fetch_config(const char *k, const char *v, void *cb) -{ - if (!strcmp(k, "fetch.prune")) { - fetch_prune_config = git_config_bool(k, v); - return 0; - } - - if (!strcmp(k, "fetch.prunetags")) { - fetch_prune_tags_config = git_config_bool(k, v); - return 0; - } - - if (!strcmp(k, "fetch.showforcedupdates")) { - fetch_show_forced_updates = git_config_bool(k, v); - return 0; - } - - if (!strcmp(k, "submodule.recurse")) { - int r = git_config_bool(k, v) ? - RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF; - recurse_submodules = r; - } - - if (!strcmp(k, "submodule.fetchjobs")) { - submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v); - return 0; - } else if (!strcmp(k, "fetch.recursesubmodules")) { - recurse_submodules = parse_fetch_recurse_submodules_arg(k, v); - return 0; - } - - if (!strcmp(k, "fetch.parallel")) { - fetch_parallel_config = git_config_int(k, v); - if (fetch_parallel_config < 0) - die(_("fetch.parallel cannot be negative")); - return 0; - } - - return git_default_config(k, v, cb); -} - -static int parse_refmap_arg(const struct option *opt, const char *arg, int unset) -{ - BUG_ON_OPT_NEG(unset); - - /* - * "git fetch --refmap='' origin foo" - * can be used to tell the command not to store anywhere - */ - refspec_append(&refmap, arg); - - return 0; -} - -static struct option builtin_fetch_options[] = { - OPT__VERBOSITY(&verbosity), - OPT_BOOL(0, "all", &all, - N_("fetch from all remotes")), - OPT_BOOL(0, "set-upstream", &set_upstream, - N_("set upstream for git pull/fetch")), - OPT_BOOL('a', "append", &append, - N_("append to .git/FETCH_HEAD instead of overwriting")), - OPT_STRING(0, "upload-pack", &upload_pack, N_("path"), - N_("path to upload pack on remote end")), - OPT__FORCE(&force, N_("force overwrite of local reference"), 0), - OPT_BOOL('m', "multiple", &multiple, - N_("fetch from multiple remotes")), - OPT_SET_INT('t', "tags", &tags, - N_("fetch all tags and associated objects"), TAGS_SET), - OPT_SET_INT('n', NULL, &tags, - N_("do not fetch all tags (--no-tags)"), TAGS_UNSET), - OPT_INTEGER('j', "jobs", &max_jobs, - N_("number of submodules fetched in parallel")), - OPT_BOOL('p', "prune", &prune, - N_("prune remote-tracking branches no longer on remote")), - OPT_BOOL('P', "prune-tags", &prune_tags, - N_("prune local tags no longer on remote and clobber changed tags")), - OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules, N_("on-demand"), - N_("control recursive fetching of submodules"), - PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules), - OPT_BOOL(0, "dry-run", &dry_run, - N_("dry run")), - OPT_BOOL(0, "write-fetch-head", &write_fetch_head, - N_("write fetched references to the FETCH_HEAD file")), - OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")), - OPT_BOOL('u', "update-head-ok", &update_head_ok, - N_("allow updating of HEAD ref")), - OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), - OPT_STRING(0, "depth", &depth, N_("depth"), - N_("deepen history of shallow clone")), - OPT_STRING(0, "shallow-since", &deepen_since, N_("time"), - N_("deepen history of shallow repository based on time")), - OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"), - N_("deepen history of shallow clone, excluding rev")), - OPT_INTEGER(0, "deepen", &deepen_relative, - N_("deepen history of shallow clone")), - OPT_SET_INT_F(0, "unshallow", &unshallow, - N_("convert to a complete repository"), - 1, PARSE_OPT_NONEG), - { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"), - N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN }, - OPT_CALLBACK_F(0, "recurse-submodules-default", - &recurse_submodules_default, N_("on-demand"), - N_("default for recursive fetching of submodules " - "(lower priority than config files)"), - PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules), - OPT_BOOL(0, "update-shallow", &update_shallow, - N_("accept refs that update .git/shallow")), - OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"), - N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg), - OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")), - OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), - TRANSPORT_FAMILY_IPV4), - OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), - TRANSPORT_FAMILY_IPV6), - OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"), - N_("report that we have only objects reachable from this object")), - OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), - OPT_BOOL(0, "auto-maintenance", &enable_auto_gc, - N_("run 'maintenance --auto' after fetching")), - OPT_BOOL(0, "auto-gc", &enable_auto_gc, - N_("run 'maintenance --auto' after fetching")), - OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates, - N_("check for forced-updates on all updated branches")), - OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph, - N_("write the commit-graph after fetching")), - OPT_BOOL(0, "stdin", &stdin_refspecs, - N_("accept refspecs from stdin")), - OPT_END() -}; - -static void unlock_pack(void) -{ - if (gtransport) - transport_unlock_pack(gtransport); - if (gsecondary) - transport_unlock_pack(gsecondary); -} - -static void unlock_pack_on_signal(int signo) -{ - unlock_pack(); - sigchain_pop(signo); - raise(signo); -} - -static void add_merge_config(struct ref **head, - const struct ref *remote_refs, - struct branch *branch, - struct ref ***tail) -{ - int i; - - for (i = 0; i < branch->merge_nr; i++) { - struct ref *rm, **old_tail = *tail; - struct refspec_item refspec; - - for (rm = *head; rm; rm = rm->next) { - if (branch_merge_matches(branch, i, rm->name)) { - rm->fetch_head_status = FETCH_HEAD_MERGE; - break; - } - } - if (rm) - continue; - - /* - * Not fetched to a remote-tracking branch? We need to fetch - * it anyway to allow this branch's "branch.$name.merge" - * to be honored by 'git pull', but we do not have to - * fail if branch.$name.merge is misconfigured to point - * at a nonexisting branch. If we were indeed called by - * 'git pull', it will notice the misconfiguration because - * there is no entry in the resulting FETCH_HEAD marked - * for merging. - */ - memset(&refspec, 0, sizeof(refspec)); - refspec.src = branch->merge[i]->src; - get_fetch_map(remote_refs, &refspec, tail, 1); - for (rm = *old_tail; rm; rm = rm->next) - rm->fetch_head_status = FETCH_HEAD_MERGE; - } -} - -static void create_fetch_oidset(struct ref **head, struct oidset *out) -{ - struct ref *rm = *head; - while (rm) { - oidset_insert(out, &rm->old_oid); - rm = rm->next; - } -} - -struct refname_hash_entry { - struct hashmap_entry ent; - struct object_id oid; - int ignore; - char refname[FLEX_ARRAY]; -}; - -static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data, - const struct hashmap_entry *eptr, - const struct hashmap_entry *entry_or_key, - const void *keydata) -{ - const struct refname_hash_entry *e1, *e2; - - e1 = container_of(eptr, const struct refname_hash_entry, ent); - e2 = container_of(entry_or_key, const struct refname_hash_entry, ent); - return strcmp(e1->refname, keydata ? keydata : e2->refname); -} - -static struct refname_hash_entry *refname_hash_add(struct hashmap *map, - const char *refname, - const struct object_id *oid) -{ - struct refname_hash_entry *ent; - size_t len = strlen(refname); - - FLEX_ALLOC_MEM(ent, refname, refname, len); - hashmap_entry_init(&ent->ent, strhash(refname)); - oidcpy(&ent->oid, oid); - hashmap_add(map, &ent->ent); - return ent; -} - -static int add_one_refname(const char *refname, - const struct object_id *oid, - int flag, void *cbdata) -{ - struct hashmap *refname_map = cbdata; - - (void) refname_hash_add(refname_map, refname, oid); - return 0; -} - -static void refname_hash_init(struct hashmap *map) -{ - hashmap_init(map, refname_hash_entry_cmp, NULL, 0); -} - -static int refname_hash_exists(struct hashmap *map, const char *refname) -{ - return !!hashmap_get_from_hash(map, strhash(refname), refname); -} - -static void clear_item(struct refname_hash_entry *item) -{ - item->ignore = 1; -} - -static void find_non_local_tags(const struct ref *refs, - struct ref **head, - struct ref ***tail) -{ - struct hashmap existing_refs; - struct hashmap remote_refs; - struct oidset fetch_oids = OIDSET_INIT; - struct string_list remote_refs_list = STRING_LIST_INIT_NODUP; - struct string_list_item *remote_ref_item; - const struct ref *ref; - struct refname_hash_entry *item = NULL; - const int quick_flags = OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT; - - refname_hash_init(&existing_refs); - refname_hash_init(&remote_refs); - create_fetch_oidset(head, &fetch_oids); - - for_each_ref(add_one_refname, &existing_refs); - for (ref = refs; ref; ref = ref->next) { - if (!starts_with(ref->name, "refs/tags/")) - continue; - - /* - * The peeled ref always follows the matching base - * ref, so if we see a peeled ref that we don't want - * to fetch then we can mark the ref entry in the list - * as one to ignore by setting util to NULL. - */ - if (ends_with(ref->name, "^{}")) { - if (item && - !has_object_file_with_flags(&ref->old_oid, quick_flags) && - !oidset_contains(&fetch_oids, &ref->old_oid) && - !has_object_file_with_flags(&item->oid, quick_flags) && - !oidset_contains(&fetch_oids, &item->oid)) - clear_item(item); - item = NULL; - continue; - } - - /* - * If item is non-NULL here, then we previously saw a - * ref not followed by a peeled reference, so we need - * to check if it is a lightweight tag that we want to - * fetch. - */ - if (item && - !has_object_file_with_flags(&item->oid, quick_flags) && - !oidset_contains(&fetch_oids, &item->oid)) - clear_item(item); - - item = NULL; - - /* skip duplicates and refs that we already have */ - if (refname_hash_exists(&remote_refs, ref->name) || - refname_hash_exists(&existing_refs, ref->name)) - continue; - - item = refname_hash_add(&remote_refs, ref->name, &ref->old_oid); - string_list_insert(&remote_refs_list, ref->name); - } - hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent); - - /* - * We may have a final lightweight tag that needs to be - * checked to see if it needs fetching. - */ - if (item && - !has_object_file_with_flags(&item->oid, quick_flags) && - !oidset_contains(&fetch_oids, &item->oid)) - clear_item(item); - - /* - * For all the tags in the remote_refs_list, - * add them to the list of refs to be fetched - */ - for_each_string_list_item(remote_ref_item, &remote_refs_list) { - const char *refname = remote_ref_item->string; - struct ref *rm; - unsigned int hash = strhash(refname); - - item = hashmap_get_entry_from_hash(&remote_refs, hash, refname, - struct refname_hash_entry, ent); - if (!item) - BUG("unseen remote ref?"); - - /* Unless we have already decided to ignore this item... */ - if (item->ignore) - continue; - - rm = alloc_ref(item->refname); - rm->peer_ref = alloc_ref(item->refname); - oidcpy(&rm->old_oid, &item->oid); - **tail = rm; - *tail = &rm->next; - } - hashmap_free_entries(&remote_refs, struct refname_hash_entry, ent); - string_list_clear(&remote_refs_list, 0); - oidset_clear(&fetch_oids); -} - -static struct ref *get_ref_map(struct remote *remote, - const struct ref *remote_refs, - struct refspec *rs, - int tags, int *autotags) -{ - int i; - struct ref *rm; - struct ref *ref_map = NULL; - struct ref **tail = &ref_map; - - /* opportunistically-updated references: */ - struct ref *orefs = NULL, **oref_tail = &orefs; - - struct hashmap existing_refs; - int existing_refs_populated = 0; - - if (rs->nr) { - struct refspec *fetch_refspec; - - for (i = 0; i < rs->nr; i++) { - get_fetch_map(remote_refs, &rs->items[i], &tail, 0); - if (rs->items[i].dst && rs->items[i].dst[0]) - *autotags = 1; - } - /* Merge everything on the command line (but not --tags) */ - for (rm = ref_map; rm; rm = rm->next) - rm->fetch_head_status = FETCH_HEAD_MERGE; - - /* - * For any refs that we happen to be fetching via - * command-line arguments, the destination ref might - * have been missing or have been different than the - * remote-tracking ref that would be derived from the - * configured refspec. In these cases, we want to - * take the opportunity to update their configured - * remote-tracking reference. However, we do not want - * to mention these entries in FETCH_HEAD at all, as - * they would simply be duplicates of existing - * entries, so we set them FETCH_HEAD_IGNORE below. - * - * We compute these entries now, based only on the - * refspecs specified on the command line. But we add - * them to the list following the refspecs resulting - * from the tags option so that one of the latter, - * which has FETCH_HEAD_NOT_FOR_MERGE, is not removed - * by ref_remove_duplicates() in favor of one of these - * opportunistic entries with FETCH_HEAD_IGNORE. - */ - if (refmap.nr) - fetch_refspec = &refmap; - else - fetch_refspec = &remote->fetch; - - for (i = 0; i < fetch_refspec->nr; i++) - get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1); - } else if (refmap.nr) { - die("--refmap option is only meaningful with command-line refspec(s)."); - } else { - /* Use the defaults */ - struct branch *branch = branch_get(NULL); - int has_merge = branch_has_merge_config(branch); - if (remote && - (remote->fetch.nr || - /* Note: has_merge implies non-NULL branch->remote_name */ - (has_merge && !strcmp(branch->remote_name, remote->name)))) { - for (i = 0; i < remote->fetch.nr; i++) { - get_fetch_map(remote_refs, &remote->fetch.items[i], &tail, 0); - if (remote->fetch.items[i].dst && - remote->fetch.items[i].dst[0]) - *autotags = 1; - if (!i && !has_merge && ref_map && - !remote->fetch.items[0].pattern) - ref_map->fetch_head_status = FETCH_HEAD_MERGE; - } - /* - * if the remote we're fetching from is the same - * as given in branch.<name>.remote, we add the - * ref given in branch.<name>.merge, too. - * - * Note: has_merge implies non-NULL branch->remote_name - */ - if (has_merge && - !strcmp(branch->remote_name, remote->name)) - add_merge_config(&ref_map, remote_refs, branch, &tail); - } else { - ref_map = get_remote_ref(remote_refs, "HEAD"); - if (!ref_map) - die(_("Couldn't find remote ref HEAD")); - ref_map->fetch_head_status = FETCH_HEAD_MERGE; - tail = &ref_map->next; - } - } - - if (tags == TAGS_SET) - /* also fetch all tags */ - get_fetch_map(remote_refs, tag_refspec, &tail, 0); - else if (tags == TAGS_DEFAULT && *autotags) - find_non_local_tags(remote_refs, &ref_map, &tail); - - /* Now append any refs to be updated opportunistically: */ - *tail = orefs; - for (rm = orefs; rm; rm = rm->next) { - rm->fetch_head_status = FETCH_HEAD_IGNORE; - tail = &rm->next; - } - - /* - * apply negative refspecs first, before we remove duplicates. This is - * necessary as negative refspecs might remove an otherwise conflicting - * duplicate. - */ - if (rs->nr) - ref_map = apply_negative_refspecs(ref_map, rs); - else - ref_map = apply_negative_refspecs(ref_map, &remote->fetch); - - ref_map = ref_remove_duplicates(ref_map); - - for (rm = ref_map; rm; rm = rm->next) { - if (rm->peer_ref) { - const char *refname = rm->peer_ref->name; - struct refname_hash_entry *peer_item; - unsigned int hash = strhash(refname); - - if (!existing_refs_populated) { - refname_hash_init(&existing_refs); - for_each_ref(add_one_refname, &existing_refs); - existing_refs_populated = 1; - } - - peer_item = hashmap_get_entry_from_hash(&existing_refs, - hash, refname, - struct refname_hash_entry, ent); - if (peer_item) { - struct object_id *old_oid = &peer_item->oid; - oidcpy(&rm->peer_ref->old_oid, old_oid); - } - } - } - if (existing_refs_populated) - hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent); - - return ref_map; -} - -#define STORE_REF_ERROR_OTHER 1 -#define STORE_REF_ERROR_DF_CONFLICT 2 - -static int s_update_ref(const char *action, - struct ref *ref, - int check_old) -{ - char *msg; - char *rla = getenv("GIT_REFLOG_ACTION"); - struct ref_transaction *transaction; - struct strbuf err = STRBUF_INIT; - int ret, df_conflict = 0; - - if (dry_run) - return 0; - if (!rla) - rla = default_rla.buf; - msg = xstrfmt("%s: %s", rla, action); - - transaction = ref_transaction_begin(&err); - if (!transaction || - ref_transaction_update(transaction, ref->name, - &ref->new_oid, - check_old ? &ref->old_oid : NULL, - 0, msg, &err)) - goto fail; - - ret = ref_transaction_commit(transaction, &err); - if (ret) { - df_conflict = (ret == TRANSACTION_NAME_CONFLICT); - goto fail; - } - - ref_transaction_free(transaction); - strbuf_release(&err); - free(msg); - return 0; -fail: - ref_transaction_free(transaction); - error("%s", err.buf); - strbuf_release(&err); - free(msg); - return df_conflict ? STORE_REF_ERROR_DF_CONFLICT - : STORE_REF_ERROR_OTHER; -} - -static int refcol_width = 10; -static int compact_format; - -static void adjust_refcol_width(const struct ref *ref) -{ - int max, rlen, llen, len; - - /* uptodate lines are only shown on high verbosity level */ - if (!verbosity && oideq(&ref->peer_ref->old_oid, &ref->old_oid)) - return; - - max = term_columns(); - rlen = utf8_strwidth(prettify_refname(ref->name)); - - llen = utf8_strwidth(prettify_refname(ref->peer_ref->name)); - - /* - * rough estimation to see if the output line is too long and - * should not be counted (we can't do precise calculation - * anyway because we don't know if the error explanation part - * will be printed in update_local_ref) - */ - if (compact_format) { - llen = 0; - max = max * 2 / 3; - } - len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen; - if (len >= max) - return; - - /* - * Not precise calculation for compact mode because '*' can - * appear on the left hand side of '->' and shrink the column - * back. - */ - if (refcol_width < rlen) - refcol_width = rlen; -} - -static void prepare_format_display(struct ref *ref_map) -{ - struct ref *rm; - const char *format = "full"; - - git_config_get_string_tmp("fetch.output", &format); - if (!strcasecmp(format, "full")) - compact_format = 0; - else if (!strcasecmp(format, "compact")) - compact_format = 1; - else - die(_("configuration fetch.output contains invalid value %s"), - format); - - for (rm = ref_map; rm; rm = rm->next) { - if (rm->status == REF_STATUS_REJECT_SHALLOW || - !rm->peer_ref || - !strcmp(rm->name, "HEAD")) - continue; - - adjust_refcol_width(rm); - } -} - -static void print_remote_to_local(struct strbuf *display, - const char *remote, const char *local) -{ - strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local); -} - -static int find_and_replace(struct strbuf *haystack, - const char *needle, - const char *placeholder) -{ - const char *p = NULL; - int plen, nlen; - - nlen = strlen(needle); - if (ends_with(haystack->buf, needle)) - p = haystack->buf + haystack->len - nlen; - else - p = strstr(haystack->buf, needle); - if (!p) - return 0; - - if (p > haystack->buf && p[-1] != '/') - return 0; - - plen = strlen(p); - if (plen > nlen && p[nlen] != '/') - return 0; - - strbuf_splice(haystack, p - haystack->buf, nlen, - placeholder, strlen(placeholder)); - return 1; -} - -static void print_compact(struct strbuf *display, - const char *remote, const char *local) -{ - struct strbuf r = STRBUF_INIT; - struct strbuf l = STRBUF_INIT; - - if (!strcmp(remote, local)) { - strbuf_addf(display, "%-*s -> *", refcol_width, remote); - return; - } - - strbuf_addstr(&r, remote); - strbuf_addstr(&l, local); - - if (!find_and_replace(&r, local, "*")) - find_and_replace(&l, remote, "*"); - print_remote_to_local(display, r.buf, l.buf); - - strbuf_release(&r); - strbuf_release(&l); -} - -static void format_display(struct strbuf *display, char code, - const char *summary, const char *error, - const char *remote, const char *local, - int summary_width) -{ - int width = (summary_width + strlen(summary) - gettext_width(summary)); - - strbuf_addf(display, "%c %-*s ", code, width, summary); - if (!compact_format) - print_remote_to_local(display, remote, local); - else - print_compact(display, remote, local); - if (error) - strbuf_addf(display, " (%s)", error); -} - -static int update_local_ref(struct ref *ref, - const char *remote, - const struct ref *remote_ref, - struct strbuf *display, - int summary_width) -{ - struct commit *current = NULL, *updated; - enum object_type type; - struct branch *current_branch = branch_get(NULL); - const char *pretty_ref = prettify_refname(ref->name); - int fast_forward = 0; - - type = oid_object_info(the_repository, &ref->new_oid, NULL); - if (type < 0) - die(_("object %s not found"), oid_to_hex(&ref->new_oid)); - - if (oideq(&ref->old_oid, &ref->new_oid)) { - if (verbosity > 0) - format_display(display, '=', _("[up to date]"), NULL, - remote, pretty_ref, summary_width); - return 0; - } - - if (current_branch && - !strcmp(ref->name, current_branch->name) && - !(update_head_ok || is_bare_repository()) && - !is_null_oid(&ref->old_oid)) { - /* - * If this is the head, and it's not okay to update - * the head, and the old value of the head isn't empty... - */ - format_display(display, '!', _("[rejected]"), - _("can't fetch in current branch"), - remote, pretty_ref, summary_width); - return 1; - } - - if (!is_null_oid(&ref->old_oid) && - starts_with(ref->name, "refs/tags/")) { - if (force || ref->force) { - int r; - r = s_update_ref("updating tag", ref, 0); - format_display(display, r ? '!' : 't', _("[tag update]"), - r ? _("unable to update local ref") : NULL, - remote, pretty_ref, summary_width); - return r; - } else { - format_display(display, '!', _("[rejected]"), _("would clobber existing tag"), - remote, pretty_ref, summary_width); - return 1; - } - } - - current = lookup_commit_reference_gently(the_repository, - &ref->old_oid, 1); - updated = lookup_commit_reference_gently(the_repository, - &ref->new_oid, 1); - if (!current || !updated) { - const char *msg; - const char *what; - int r; - /* - * Nicely describe the new ref we're fetching. - * Base this on the remote's ref name, as it's - * more likely to follow a standard layout. - */ - const char *name = remote_ref ? remote_ref->name : ""; - if (starts_with(name, "refs/tags/")) { - msg = "storing tag"; - what = _("[new tag]"); - } else if (starts_with(name, "refs/heads/")) { - msg = "storing head"; - what = _("[new branch]"); - } else { - msg = "storing ref"; - what = _("[new ref]"); - } - - r = s_update_ref(msg, ref, 0); - format_display(display, r ? '!' : '*', what, - r ? _("unable to update local ref") : NULL, - remote, pretty_ref, summary_width); - return r; - } - - if (fetch_show_forced_updates) { - uint64_t t_before = getnanotime(); - fast_forward = in_merge_bases(current, updated); - forced_updates_ms += (getnanotime() - t_before) / 1000000; - } else { - fast_forward = 1; - } - - if (fast_forward) { - struct strbuf quickref = STRBUF_INIT; - int r; - - strbuf_add_unique_abbrev(&quickref, ¤t->object.oid, DEFAULT_ABBREV); - strbuf_addstr(&quickref, ".."); - strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV); - r = s_update_ref("fast-forward", ref, 1); - format_display(display, r ? '!' : ' ', quickref.buf, - r ? _("unable to update local ref") : NULL, - remote, pretty_ref, summary_width); - strbuf_release(&quickref); - return r; - } else if (force || ref->force) { - struct strbuf quickref = STRBUF_INIT; - int r; - strbuf_add_unique_abbrev(&quickref, ¤t->object.oid, DEFAULT_ABBREV); - strbuf_addstr(&quickref, "..."); - strbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV); - r = s_update_ref("forced-update", ref, 1); - format_display(display, r ? '!' : '+', quickref.buf, - r ? _("unable to update local ref") : _("forced update"), - remote, pretty_ref, summary_width); - strbuf_release(&quickref); - return r; - } else { - format_display(display, '!', _("[rejected]"), _("non-fast-forward"), - remote, pretty_ref, summary_width); - return 1; - } -} - -static int iterate_ref_map(void *cb_data, struct object_id *oid) -{ - struct ref **rm = cb_data; - struct ref *ref = *rm; - - while (ref && ref->status == REF_STATUS_REJECT_SHALLOW) - ref = ref->next; - if (!ref) - return -1; /* end of the list */ - *rm = ref->next; - oidcpy(oid, &ref->old_oid); - return 0; -} - -static const char warn_show_forced_updates[] = -N_("Fetch normally indicates which branches had a forced update,\n" - "but that check has been disabled. To re-enable, use '--show-forced-updates'\n" - "flag or run 'git config fetch.showForcedUpdates true'."); -static const char warn_time_show_forced_updates[] = -N_("It took %.2f seconds to check forced updates. You can use\n" - "'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates false'\n" - " to avoid this check.\n"); - -static int store_updated_refs(const char *raw_url, const char *remote_name, - int connectivity_checked, struct ref *ref_map) -{ - FILE *fp; - struct commit *commit; - int url_len, i, rc = 0; - struct strbuf note = STRBUF_INIT; - const char *what, *kind; - struct ref *rm; - char *url; - const char *filename = (!write_fetch_head - ? "/dev/null" - : git_path_fetch_head(the_repository)); - int want_status; - int summary_width = transport_summary_width(ref_map); - - fp = fopen(filename, "a"); - if (!fp) - return error_errno(_("cannot open %s"), filename); - - if (raw_url) - url = transport_anonymize_url(raw_url); - else - url = xstrdup("foreign"); - - if (!connectivity_checked) { - struct check_connected_options opt = CHECK_CONNECTED_INIT; - - rm = ref_map; - if (check_connected(iterate_ref_map, &rm, &opt)) { - rc = error(_("%s did not send all necessary objects\n"), url); - goto abort; - } - } - - prepare_format_display(ref_map); - - /* - * We do a pass for each fetch_head_status type in their enum order, so - * merged entries are written before not-for-merge. That lets readers - * use FETCH_HEAD as a refname to refer to the ref to be merged. - */ - for (want_status = FETCH_HEAD_MERGE; - want_status <= FETCH_HEAD_IGNORE; - want_status++) { - for (rm = ref_map; rm; rm = rm->next) { - struct ref *ref = NULL; - const char *merge_status_marker = ""; - - if (rm->status == REF_STATUS_REJECT_SHALLOW) { - if (want_status == FETCH_HEAD_MERGE) - warning(_("reject %s because shallow roots are not allowed to be updated"), - rm->peer_ref ? rm->peer_ref->name : rm->name); - continue; - } - - commit = lookup_commit_reference_gently(the_repository, - &rm->old_oid, - 1); - if (!commit) - rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE; - - if (rm->fetch_head_status != want_status) - continue; - - if (rm->peer_ref) { - ref = alloc_ref(rm->peer_ref->name); - oidcpy(&ref->old_oid, &rm->peer_ref->old_oid); - oidcpy(&ref->new_oid, &rm->old_oid); - ref->force = rm->peer_ref->force; - } - - if (recurse_submodules != RECURSE_SUBMODULES_OFF && - (!rm->peer_ref || !oideq(&ref->old_oid, &ref->new_oid))) { - check_for_new_submodule_commits(&rm->old_oid); - } - - if (!strcmp(rm->name, "HEAD")) { - kind = ""; - what = ""; - } - else if (skip_prefix(rm->name, "refs/heads/", &what)) - kind = "branch"; - else if (skip_prefix(rm->name, "refs/tags/", &what)) - kind = "tag"; - else if (skip_prefix(rm->name, "refs/remotes/", &what)) - kind = "remote-tracking branch"; - else { - kind = ""; - what = rm->name; - } - - url_len = strlen(url); - for (i = url_len - 1; url[i] == '/' && 0 <= i; i--) - ; - url_len = i + 1; - if (4 < i && !strncmp(".git", url + i - 3, 4)) - url_len = i - 3; - - strbuf_reset(¬e); - if (*what) { - if (*kind) - strbuf_addf(¬e, "%s ", kind); - strbuf_addf(¬e, "'%s' of ", what); - } - switch (rm->fetch_head_status) { - case FETCH_HEAD_NOT_FOR_MERGE: - merge_status_marker = "not-for-merge"; - /* fall-through */ - case FETCH_HEAD_MERGE: - fprintf(fp, "%s\t%s\t%s", - oid_to_hex(&rm->old_oid), - merge_status_marker, - note.buf); - for (i = 0; i < url_len; ++i) - if ('\n' == url[i]) - fputs("\\n", fp); - else - fputc(url[i], fp); - fputc('\n', fp); - break; - default: - /* do not write anything to FETCH_HEAD */ - break; - } - - strbuf_reset(¬e); - if (ref) { - rc |= update_local_ref(ref, what, rm, ¬e, - summary_width); - free(ref); - } else if (write_fetch_head || dry_run) { - /* - * Display fetches written to FETCH_HEAD (or - * would be written to FETCH_HEAD, if --dry-run - * is set). - */ - format_display(¬e, '*', - *kind ? kind : "branch", NULL, - *what ? what : "HEAD", - "FETCH_HEAD", summary_width); - } - if (note.len) { - if (verbosity >= 0 && !shown_url) { - fprintf(stderr, _("From %.*s\n"), - url_len, url); - shown_url = 1; - } - if (verbosity >= 0) - fprintf(stderr, " %s\n", note.buf); - } - } - } - - if (rc & STORE_REF_ERROR_DF_CONFLICT) - error(_("some local refs could not be updated; try running\n" - " 'git remote prune %s' to remove any old, conflicting " - "branches"), remote_name); - - if (advice_fetch_show_forced_updates) { - if (!fetch_show_forced_updates) { - warning(_(warn_show_forced_updates)); - } else if (forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) { - warning(_(warn_time_show_forced_updates), - forced_updates_ms / 1000.0); - } - } - - abort: - strbuf_release(¬e); - free(url); - fclose(fp); - return rc; -} - -/* - * We would want to bypass the object transfer altogether if - * everything we are going to fetch already exists and is connected - * locally. - */ -static int check_exist_and_connected(struct ref *ref_map) -{ - struct ref *rm = ref_map; - struct check_connected_options opt = CHECK_CONNECTED_INIT; - struct ref *r; - - /* - * If we are deepening a shallow clone we already have these - * objects reachable. Running rev-list here will return with - * a good (0) exit status and we'll bypass the fetch that we - * really need to perform. Claiming failure now will ensure - * we perform the network exchange to deepen our history. - */ - if (deepen) - return -1; - - /* - * check_connected() allows objects to merely be promised, but - * we need all direct targets to exist. - */ - for (r = rm; r; r = r->next) { - if (!has_object_file_with_flags(&r->old_oid, - OBJECT_INFO_SKIP_FETCH_OBJECT)) - return -1; - } - - opt.quiet = 1; - return check_connected(iterate_ref_map, &rm, &opt); -} - -static int fetch_refs(struct transport *transport, struct ref *ref_map) -{ - int ret = check_exist_and_connected(ref_map); - if (ret) { - trace2_region_enter("fetch", "fetch_refs", the_repository); - ret = transport_fetch_refs(transport, ref_map); - trace2_region_leave("fetch", "fetch_refs", the_repository); - } - if (!ret) - /* - * Keep the new pack's ".keep" file around to allow the caller - * time to update refs to reference the new objects. - */ - return 0; - transport_unlock_pack(transport); - return ret; -} - -/* Update local refs based on the ref values fetched from a remote */ -static int consume_refs(struct transport *transport, struct ref *ref_map) -{ - int connectivity_checked = transport->smart_options - ? transport->smart_options->connectivity_checked : 0; - int ret; - trace2_region_enter("fetch", "consume_refs", the_repository); - ret = store_updated_refs(transport->url, - transport->remote->name, - connectivity_checked, - ref_map); - transport_unlock_pack(transport); - trace2_region_leave("fetch", "consume_refs", the_repository); - return ret; -} - -static int prune_refs(struct refspec *rs, struct ref *ref_map, - const char *raw_url) -{ - int url_len, i, result = 0; - struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map); - char *url; - int summary_width = transport_summary_width(stale_refs); - const char *dangling_msg = dry_run - ? _(" (%s will become dangling)") - : _(" (%s has become dangling)"); - - if (raw_url) - url = transport_anonymize_url(raw_url); - else - url = xstrdup("foreign"); - - url_len = strlen(url); - for (i = url_len - 1; url[i] == '/' && 0 <= i; i--) - ; - - url_len = i + 1; - if (4 < i && !strncmp(".git", url + i - 3, 4)) - url_len = i - 3; - - if (!dry_run) { - struct string_list refnames = STRING_LIST_INIT_NODUP; - - for (ref = stale_refs; ref; ref = ref->next) - string_list_append(&refnames, ref->name); - - result = delete_refs("fetch: prune", &refnames, 0); - string_list_clear(&refnames, 0); - } - - if (verbosity >= 0) { - for (ref = stale_refs; ref; ref = ref->next) { - struct strbuf sb = STRBUF_INIT; - if (!shown_url) { - fprintf(stderr, _("From %.*s\n"), url_len, url); - shown_url = 1; - } - format_display(&sb, '-', _("[deleted]"), NULL, - _("(none)"), prettify_refname(ref->name), - summary_width); - fprintf(stderr, " %s\n",sb.buf); - strbuf_release(&sb); - warn_dangling_symref(stderr, dangling_msg, ref->name); - } - } - - free(url); - free_refs(stale_refs); - return result; -} - -static void check_not_current_branch(struct ref *ref_map) -{ - struct branch *current_branch = branch_get(NULL); - - if (is_bare_repository() || !current_branch) - return; - - for (; ref_map; ref_map = ref_map->next) - if (ref_map->peer_ref && !strcmp(current_branch->refname, - ref_map->peer_ref->name)) - die(_("Refusing to fetch into current branch %s " - "of non-bare repository"), current_branch->refname); -} - -static int truncate_fetch_head(void) -{ - const char *filename = git_path_fetch_head(the_repository); - FILE *fp = fopen_for_writing(filename); - - if (!fp) - return error_errno(_("cannot open %s"), filename); - fclose(fp); - return 0; -} - -static void set_option(struct transport *transport, const char *name, const char *value) -{ - int r = transport_set_option(transport, name, value); - if (r < 0) - die(_("Option \"%s\" value \"%s\" is not valid for %s"), - name, value, transport->url); - if (r > 0) - warning(_("Option \"%s\" is ignored for %s\n"), - name, transport->url); -} - - -static int add_oid(const char *refname, const struct object_id *oid, int flags, - void *cb_data) -{ - struct oid_array *oids = cb_data; - - oid_array_append(oids, oid); - return 0; -} - -static void add_negotiation_tips(struct git_transport_options *smart_options) -{ - struct oid_array *oids = xcalloc(1, sizeof(*oids)); - int i; - - for (i = 0; i < negotiation_tip.nr; i++) { - const char *s = negotiation_tip.items[i].string; - int old_nr; - if (!has_glob_specials(s)) { - struct object_id oid; - if (get_oid(s, &oid)) - die("%s is not a valid object", s); - oid_array_append(oids, &oid); - continue; - } - old_nr = oids->nr; - for_each_glob_ref(add_oid, s, oids); - if (old_nr == oids->nr) - warning("Ignoring --negotiation-tip=%s because it does not match any refs", - s); - } - smart_options->negotiation_tips = oids; -} - -static struct transport *prepare_transport(struct remote *remote, int deepen) -{ - struct transport *transport; - - transport = transport_get(remote, NULL); - transport_set_verbosity(transport, verbosity, progress); - transport->family = family; - if (upload_pack) - set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack); - if (keep) - set_option(transport, TRANS_OPT_KEEP, "yes"); - if (depth) - set_option(transport, TRANS_OPT_DEPTH, depth); - if (deepen && deepen_since) - set_option(transport, TRANS_OPT_DEEPEN_SINCE, deepen_since); - if (deepen && deepen_not.nr) - set_option(transport, TRANS_OPT_DEEPEN_NOT, - (const char *)&deepen_not); - if (deepen_relative) - set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes"); - if (update_shallow) - set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes"); - if (filter_options.choice) { - const char *spec = - expand_list_objects_filter_spec(&filter_options); - set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, spec); - set_option(transport, TRANS_OPT_FROM_PROMISOR, "1"); - } - if (negotiation_tip.nr) { - if (transport->smart_options) - add_negotiation_tips(transport->smart_options); - else - warning("Ignoring --negotiation-tip because the protocol does not support it."); - } - return transport; -} - -static void backfill_tags(struct transport *transport, struct ref *ref_map) -{ - int cannot_reuse; - - /* - * Once we have set TRANS_OPT_DEEPEN_SINCE, we can't unset it - * when remote helper is used (setting it to an empty string - * is not unsetting). We could extend the remote helper - * protocol for that, but for now, just force a new connection - * without deepen-since. Similar story for deepen-not. - */ - cannot_reuse = transport->cannot_reuse || - deepen_since || deepen_not.nr; - if (cannot_reuse) { - gsecondary = prepare_transport(transport->remote, 0); - transport = gsecondary; - } - - transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL); - transport_set_option(transport, TRANS_OPT_DEPTH, "0"); - transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL); - if (!fetch_refs(transport, ref_map)) - consume_refs(transport, ref_map); - - if (gsecondary) { - transport_disconnect(gsecondary); - gsecondary = NULL; - } -} - -static int do_fetch(struct transport *transport, - struct refspec *rs) -{ - struct ref *ref_map; - int autotags = (transport->remote->fetch_tags == 1); - int retcode = 0; - const struct ref *remote_refs; - struct strvec ref_prefixes = STRVEC_INIT; - int must_list_refs = 1; - - if (tags == TAGS_DEFAULT) { - if (transport->remote->fetch_tags == 2) - tags = TAGS_SET; - if (transport->remote->fetch_tags == -1) - tags = TAGS_UNSET; - } - - /* if not appending, truncate FETCH_HEAD */ - if (!append && write_fetch_head) { - retcode = truncate_fetch_head(); - if (retcode) - goto cleanup; - } - - if (rs->nr) { - int i; - - refspec_ref_prefixes(rs, &ref_prefixes); - - /* - * We can avoid listing refs if all of them are exact - * OIDs - */ - must_list_refs = 0; - for (i = 0; i < rs->nr; i++) { - if (!rs->items[i].exact_sha1) { - must_list_refs = 1; - break; - } - } - } else if (transport->remote && transport->remote->fetch.nr) - refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes); - - if (tags == TAGS_SET || tags == TAGS_DEFAULT) { - must_list_refs = 1; - if (ref_prefixes.nr) - strvec_push(&ref_prefixes, "refs/tags/"); - } - - if (must_list_refs) { - trace2_region_enter("fetch", "remote_refs", the_repository); - remote_refs = transport_get_remote_refs(transport, &ref_prefixes); - trace2_region_leave("fetch", "remote_refs", the_repository); - } else - remote_refs = NULL; - - strvec_clear(&ref_prefixes); - - ref_map = get_ref_map(transport->remote, remote_refs, rs, - tags, &autotags); - if (!update_head_ok) - check_not_current_branch(ref_map); - - if (tags == TAGS_DEFAULT && autotags) - transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); - if (prune) { - /* - * We only prune based on refspecs specified - * explicitly (via command line or configuration); we - * don't care whether --tags was specified. - */ - if (rs->nr) { - prune_refs(rs, ref_map, transport->url); - } else { - prune_refs(&transport->remote->fetch, - ref_map, - transport->url); - } - } - if (fetch_refs(transport, ref_map) || consume_refs(transport, ref_map)) { - free_refs(ref_map); - retcode = 1; - goto cleanup; - } - - if (set_upstream) { - struct branch *branch = branch_get("HEAD"); - struct ref *rm; - struct ref *source_ref = NULL; - - /* - * We're setting the upstream configuration for the - * current branch. The relevant upstream is the - * fetched branch that is meant to be merged with the - * current one, i.e. the one fetched to FETCH_HEAD. - * - * When there are several such branches, consider the - * request ambiguous and err on the safe side by doing - * nothing and just emit a warning. - */ - for (rm = ref_map; rm; rm = rm->next) { - if (!rm->peer_ref) { - if (source_ref) { - warning(_("multiple branches detected, incompatible with --set-upstream")); - goto skip; - } else { - source_ref = rm; - } - } - } - if (source_ref) { - if (!strcmp(source_ref->name, "HEAD") || - starts_with(source_ref->name, "refs/heads/")) - install_branch_config(0, - branch->name, - transport->remote->name, - source_ref->name); - else if (starts_with(source_ref->name, "refs/remotes/")) - warning(_("not setting upstream for a remote remote-tracking branch")); - else if (starts_with(source_ref->name, "refs/tags/")) - warning(_("not setting upstream for a remote tag")); - else - warning(_("unknown branch type")); - } else { - warning(_("no source branch found.\n" - "you need to specify exactly one branch with the --set-upstream option.")); - } - } - skip: - free_refs(ref_map); - - /* if neither --no-tags nor --tags was specified, do automated tag - * following ... */ - if (tags == TAGS_DEFAULT && autotags) { - struct ref **tail = &ref_map; - ref_map = NULL; - find_non_local_tags(remote_refs, &ref_map, &tail); - if (ref_map) - backfill_tags(transport, ref_map); - free_refs(ref_map); - } - - cleanup: - return retcode; -} - -static int get_one_remote_for_fetch(struct remote *remote, void *priv) -{ - struct string_list *list = priv; - if (!remote->skip_default_update) - string_list_append(list, remote->name); - return 0; -} - -struct remote_group_data { - const char *name; - struct string_list *list; -}; - -static int get_remote_group(const char *key, const char *value, void *priv) -{ - struct remote_group_data *g = priv; - - if (skip_prefix(key, "remotes.", &key) && !strcmp(key, g->name)) { - /* split list by white space */ - while (*value) { - size_t wordlen = strcspn(value, " \t\n"); - - if (wordlen >= 1) - string_list_append_nodup(g->list, - xstrndup(value, wordlen)); - value += wordlen + (value[wordlen] != '\0'); - } - } - - return 0; -} - -static int add_remote_or_group(const char *name, struct string_list *list) -{ - int prev_nr = list->nr; - struct remote_group_data g; - g.name = name; g.list = list; - - git_config(get_remote_group, &g); - if (list->nr == prev_nr) { - struct remote *remote = remote_get(name); - if (!remote_is_configured(remote, 0)) - return 0; - string_list_append(list, remote->name); - } - return 1; -} - -static void add_options_to_argv(struct strvec *argv) -{ - if (dry_run) - strvec_push(argv, "--dry-run"); - if (prune != -1) - strvec_push(argv, prune ? "--prune" : "--no-prune"); - if (prune_tags != -1) - strvec_push(argv, prune_tags ? "--prune-tags" : "--no-prune-tags"); - if (update_head_ok) - strvec_push(argv, "--update-head-ok"); - if (force) - strvec_push(argv, "--force"); - if (keep) - strvec_push(argv, "--keep"); - if (recurse_submodules == RECURSE_SUBMODULES_ON) - strvec_push(argv, "--recurse-submodules"); - else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) - strvec_push(argv, "--recurse-submodules=on-demand"); - if (tags == TAGS_SET) - strvec_push(argv, "--tags"); - else if (tags == TAGS_UNSET) - strvec_push(argv, "--no-tags"); - if (verbosity >= 2) - strvec_push(argv, "-v"); - if (verbosity >= 1) - strvec_push(argv, "-v"); - else if (verbosity < 0) - strvec_push(argv, "-q"); - if (family == TRANSPORT_FAMILY_IPV4) - strvec_push(argv, "--ipv4"); - else if (family == TRANSPORT_FAMILY_IPV6) - strvec_push(argv, "--ipv6"); -} - -/* Fetch multiple remotes in parallel */ - -struct parallel_fetch_state { - const char **argv; - struct string_list *remotes; - int next, result; -}; - -static int fetch_next_remote(struct child_process *cp, struct strbuf *out, - void *cb, void **task_cb) -{ - struct parallel_fetch_state *state = cb; - char *remote; - - if (state->next < 0 || state->next >= state->remotes->nr) - return 0; - - remote = state->remotes->items[state->next++].string; - *task_cb = remote; - - strvec_pushv(&cp->args, state->argv); - strvec_push(&cp->args, remote); - cp->git_cmd = 1; - - if (verbosity >= 0) - printf(_("Fetching %s\n"), remote); - - return 1; -} - -static int fetch_failed_to_start(struct strbuf *out, void *cb, void *task_cb) -{ - struct parallel_fetch_state *state = cb; - const char *remote = task_cb; - - state->result = error(_("Could not fetch %s"), remote); - - return 0; -} - -static int fetch_finished(int result, struct strbuf *out, - void *cb, void *task_cb) -{ - struct parallel_fetch_state *state = cb; - const char *remote = task_cb; - - if (result) { - strbuf_addf(out, _("could not fetch '%s' (exit code: %d)\n"), - remote, result); - state->result = -1; - } - - return 0; -} - -static int fetch_multiple(struct string_list *list, int max_children) -{ - int i, result = 0; - struct strvec argv = STRVEC_INIT; - - if (!append && write_fetch_head) { - int errcode = truncate_fetch_head(); - if (errcode) - return errcode; - } - - strvec_pushl(&argv, "fetch", "--append", "--no-auto-gc", - "--no-write-commit-graph", NULL); - add_options_to_argv(&argv); - - if (max_children != 1 && list->nr != 1) { - struct parallel_fetch_state state = { argv.v, list, 0, 0 }; - - strvec_push(&argv, "--end-of-options"); - result = run_processes_parallel_tr2(max_children, - &fetch_next_remote, - &fetch_failed_to_start, - &fetch_finished, - &state, - "fetch", "parallel/fetch"); - - if (!result) - result = state.result; - } else - for (i = 0; i < list->nr; i++) { - const char *name = list->items[i].string; - strvec_push(&argv, name); - if (verbosity >= 0) - printf(_("Fetching %s\n"), name); - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) { - error(_("Could not fetch %s"), name); - result = 1; - } - strvec_pop(&argv); - } - - strvec_clear(&argv); - return !!result; -} - -/* - * Fetching from the promisor remote should use the given filter-spec - * or inherit the default filter-spec from the config. - */ -static inline void fetch_one_setup_partial(struct remote *remote) -{ - /* - * Explicit --no-filter argument overrides everything, regardless - * of any prior partial clones and fetches. - */ - if (filter_options.no_filter) - return; - - /* - * If no prior partial clone/fetch and the current fetch DID NOT - * request a partial-fetch, do a normal fetch. - */ - if (!has_promisor_remote() && !filter_options.choice) - return; - - /* - * If this is a partial-fetch request, we enable partial on - * this repo if not already enabled and remember the given - * filter-spec as the default for subsequent fetches to this - * remote if there is currently no default filter-spec. - */ - if (filter_options.choice) { - partial_clone_register(remote->name, &filter_options); - return; - } - - /* - * Do a partial-fetch from the promisor remote using either the - * explicitly given filter-spec or inherit the filter-spec from - * the config. - */ - if (!filter_options.choice) - partial_clone_get_default_filter_spec(&filter_options, remote->name); - return; -} - -static int fetch_one(struct remote *remote, int argc, const char **argv, - int prune_tags_ok, int use_stdin_refspecs) -{ - struct refspec rs = REFSPEC_INIT_FETCH; - int i; - int exit_code; - int maybe_prune_tags; - int remote_via_config = remote_is_configured(remote, 0); - - if (!remote) - die(_("No remote repository specified. Please, specify either a URL or a\n" - "remote name from which new revisions should be fetched.")); - - gtransport = prepare_transport(remote, 1); - - if (prune < 0) { - /* no command line request */ - if (0 <= remote->prune) - prune = remote->prune; - else if (0 <= fetch_prune_config) - prune = fetch_prune_config; - else - prune = PRUNE_BY_DEFAULT; - } - - if (prune_tags < 0) { - /* no command line request */ - if (0 <= remote->prune_tags) - prune_tags = remote->prune_tags; - else if (0 <= fetch_prune_tags_config) - prune_tags = fetch_prune_tags_config; - else - prune_tags = PRUNE_TAGS_BY_DEFAULT; - } - - maybe_prune_tags = prune_tags_ok && prune_tags; - if (maybe_prune_tags && remote_via_config) - refspec_append(&remote->fetch, TAG_REFSPEC); - - if (maybe_prune_tags && (argc || !remote_via_config)) - refspec_append(&rs, TAG_REFSPEC); - - for (i = 0; i < argc; i++) { - if (!strcmp(argv[i], "tag")) { - i++; - if (i >= argc) - die(_("You need to specify a tag name.")); - - refspec_appendf(&rs, "refs/tags/%s:refs/tags/%s", - argv[i], argv[i]); - } else { - refspec_append(&rs, argv[i]); - } - } - - if (use_stdin_refspecs) { - struct strbuf line = STRBUF_INIT; - while (strbuf_getline_lf(&line, stdin) != EOF) - refspec_append(&rs, line.buf); - strbuf_release(&line); - } - - if (server_options.nr) - gtransport->server_options = &server_options; - - sigchain_push_common(unlock_pack_on_signal); - atexit(unlock_pack); - sigchain_push(SIGPIPE, SIG_IGN); - exit_code = do_fetch(gtransport, &rs); - sigchain_pop(SIGPIPE); - refspec_clear(&rs); - transport_disconnect(gtransport); - gtransport = NULL; - return exit_code; -} - -int cmd_fetch(int argc, const char **argv, const char *prefix) -{ - int i; - struct string_list list = STRING_LIST_INIT_DUP; - struct remote *remote = NULL; - int result = 0; - int prune_tags_ok = 1; - - packet_trace_identity("fetch"); - - /* Record the command line for the reflog */ - strbuf_addstr(&default_rla, "fetch"); - for (i = 1; i < argc; i++) { - /* This handles non-URLs gracefully */ - char *anon = transport_anonymize_url(argv[i]); - - strbuf_addf(&default_rla, " %s", anon); - free(anon); - } - - git_config(git_fetch_config, NULL); - - argc = parse_options(argc, argv, prefix, - builtin_fetch_options, builtin_fetch_usage, 0); - if (recurse_submodules != RECURSE_SUBMODULES_OFF) { - int *sfjc = submodule_fetch_jobs_config == -1 - ? &submodule_fetch_jobs_config : NULL; - int *rs = recurse_submodules == RECURSE_SUBMODULES_DEFAULT - ? &recurse_submodules : NULL; - - fetch_config_from_gitmodules(sfjc, rs); - } - - if (deepen_relative) { - if (deepen_relative < 0) - die(_("Negative depth in --deepen is not supported")); - if (depth) - die(_("--deepen and --depth are mutually exclusive")); - depth = xstrfmt("%d", deepen_relative); - } - if (unshallow) { - if (depth) - die(_("--depth and --unshallow cannot be used together")); - else if (!is_repository_shallow(the_repository)) - die(_("--unshallow on a complete repository does not make sense")); - else - depth = xstrfmt("%d", INFINITE_DEPTH); - } - - /* no need to be strict, transport_set_option() will validate it again */ - if (depth && atoi(depth) < 1) - die(_("depth %s is not a positive number"), depth); - if (depth || deepen_since || deepen_not.nr) - deepen = 1; - - /* FETCH_HEAD never gets updated in --dry-run mode */ - if (dry_run) - write_fetch_head = 0; - - if (all) { - if (argc == 1) - die(_("fetch --all does not take a repository argument")); - else if (argc > 1) - die(_("fetch --all does not make sense with refspecs")); - (void) for_each_remote(get_one_remote_for_fetch, &list); - } else if (argc == 0) { - /* No arguments -- use default remote */ - remote = remote_get(NULL); - } else if (multiple) { - /* All arguments are assumed to be remotes or groups */ - for (i = 0; i < argc; i++) - if (!add_remote_or_group(argv[i], &list)) - die(_("No such remote or remote group: %s"), argv[i]); - } else { - /* Single remote or group */ - (void) add_remote_or_group(argv[0], &list); - if (list.nr > 1) { - /* More than one remote */ - if (argc > 1) - die(_("Fetching a group and specifying refspecs does not make sense")); - } else { - /* Zero or one remotes */ - remote = remote_get(argv[0]); - prune_tags_ok = (argc == 1); - argc--; - argv++; - } - } - - if (remote) { - if (filter_options.choice || has_promisor_remote()) - fetch_one_setup_partial(remote); - result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs); - } else { - int max_children = max_jobs; - - if (filter_options.choice) - die(_("--filter can only be used with the remote " - "configured in extensions.partialclone")); - - if (stdin_refspecs) - die(_("--stdin can only be used when fetching " - "from one remote")); - - if (max_children < 0) - max_children = fetch_parallel_config; - - /* TODO should this also die if we have a previous partial-clone? */ - result = fetch_multiple(&list, max_children); - } - - if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) { - struct strvec options = STRVEC_INIT; - int max_children = max_jobs; - - if (max_children < 0) - max_children = submodule_fetch_jobs_config; - if (max_children < 0) - max_children = fetch_parallel_config; - - add_options_to_argv(&options); - result = fetch_populated_submodules(the_repository, - &options, - submodule_prefix, - recurse_submodules, - recurse_submodules_default, - verbosity < 0, - max_children); - strvec_clear(&options); - } - - string_list_clear(&list, 0); - - prepare_repo_settings(the_repository); - if (fetch_write_commit_graph > 0 || - (fetch_write_commit_graph < 0 && - the_repository->settings.fetch_write_commit_graph)) { - int commit_graph_flags = COMMIT_GRAPH_WRITE_SPLIT; - - if (progress) - commit_graph_flags |= COMMIT_GRAPH_WRITE_PROGRESS; - - write_commit_graph_reachable(the_repository->objects->odb, - commit_graph_flags, - NULL); - } - - close_object_store(the_repository->objects); - - if (enable_auto_gc) - run_auto_maintenance(verbosity < 0); - - return result; -} diff --git a/third_party/git/builtin/fmt-merge-msg.c b/third_party/git/builtin/fmt-merge-msg.c deleted file mode 100644 index 48a8699de728..000000000000 --- a/third_party/git/builtin/fmt-merge-msg.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "builtin.h" -#include "config.h" -#include "fmt-merge-msg.h" -#include "parse-options.h" - -static const char * const fmt_merge_msg_usage[] = { - N_("git fmt-merge-msg [-m <message>] [--log[=<n>] | --no-log] [--file <file>]"), - NULL -}; - -int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) -{ - const char *inpath = NULL; - const char *message = NULL; - int shortlog_len = -1; - struct option options[] = { - { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"), - N_("populate log with at most <n> entries from shortlog"), - PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN }, - { OPTION_INTEGER, 0, "summary", &shortlog_len, N_("n"), - N_("alias for --log (deprecated)"), - PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, NULL, - DEFAULT_MERGE_LOG_LEN }, - OPT_STRING('m', "message", &message, N_("text"), - N_("use <text> as start of message")), - OPT_FILENAME('F', "file", &inpath, N_("file to read from")), - OPT_END() - }; - - FILE *in = stdin; - struct strbuf input = STRBUF_INIT, output = STRBUF_INIT; - int ret; - struct fmt_merge_msg_opts opts; - - git_config(fmt_merge_msg_config, NULL); - argc = parse_options(argc, argv, prefix, options, fmt_merge_msg_usage, - 0); - if (argc > 0) - usage_with_options(fmt_merge_msg_usage, options); - if (shortlog_len < 0) - shortlog_len = (merge_log_config > 0) ? merge_log_config : 0; - - if (inpath && strcmp(inpath, "-")) { - in = fopen(inpath, "r"); - if (!in) - die_errno("cannot open '%s'", inpath); - } - - if (strbuf_read(&input, fileno(in), 0) < 0) - die_errno("could not read input file"); - - if (message) - strbuf_addstr(&output, message); - - memset(&opts, 0, sizeof(opts)); - opts.add_title = !message; - opts.credit_people = 1; - opts.shortlog_len = shortlog_len; - - ret = fmt_merge_msg(&input, &output, &opts); - if (ret) - return ret; - write_in_full(STDOUT_FILENO, output.buf, output.len); - return 0; -} diff --git a/third_party/git/builtin/for-each-ref.c b/third_party/git/builtin/for-each-ref.c deleted file mode 100644 index 9d1ecda2b8f3..000000000000 --- a/third_party/git/builtin/for-each-ref.c +++ /dev/null @@ -1,87 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "refs.h" -#include "object.h" -#include "parse-options.h" -#include "ref-filter.h" - -static char const * const for_each_ref_usage[] = { - N_("git for-each-ref [<options>] [<pattern>]"), - N_("git for-each-ref [--points-at <object>]"), - N_("git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"), - N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"), - NULL -}; - -int cmd_for_each_ref(int argc, const char **argv, const char *prefix) -{ - int i; - struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; - int maxcount = 0, icase = 0; - struct ref_array array; - struct ref_filter filter; - struct ref_format format = REF_FORMAT_INIT; - - struct option opts[] = { - OPT_BIT('s', "shell", &format.quote_style, - N_("quote placeholders suitably for shells"), QUOTE_SHELL), - OPT_BIT('p', "perl", &format.quote_style, - N_("quote placeholders suitably for perl"), QUOTE_PERL), - OPT_BIT(0 , "python", &format.quote_style, - N_("quote placeholders suitably for python"), QUOTE_PYTHON), - OPT_BIT(0 , "tcl", &format.quote_style, - N_("quote placeholders suitably for Tcl"), QUOTE_TCL), - - OPT_GROUP(""), - OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")), - OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")), - OPT__COLOR(&format.use_color, N_("respect format colors")), - OPT_REF_SORT(sorting_tail), - OPT_CALLBACK(0, "points-at", &filter.points_at, - N_("object"), N_("print only refs which points at the given object"), - parse_opt_object_name), - OPT_MERGED(&filter, N_("print only refs that are merged")), - OPT_NO_MERGED(&filter, N_("print only refs that are not merged")), - OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")), - OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")), - OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")), - OPT_END(), - }; - - memset(&array, 0, sizeof(array)); - memset(&filter, 0, sizeof(filter)); - - format.format = "%(objectname) %(objecttype)\t%(refname)"; - - git_config(git_default_config, NULL); - - parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0); - if (maxcount < 0) { - error("invalid --count argument: `%d'", maxcount); - usage_with_options(for_each_ref_usage, opts); - } - if (HAS_MULTI_BITS(format.quote_style)) { - error("more than one quoting style?"); - usage_with_options(for_each_ref_usage, opts); - } - if (verify_ref_format(&format)) - usage_with_options(for_each_ref_usage, opts); - - if (!sorting) - sorting = ref_default_sorting(); - ref_sorting_icase_all(sorting, icase); - filter.ignore_case = icase; - - filter.name_patterns = argv; - filter.match_as_path = 1; - filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN); - ref_array_sort(sorting, &array); - - if (!maxcount || array.nr < maxcount) - maxcount = array.nr; - for (i = 0; i < maxcount; i++) - show_ref_array_item(array.items[i], &format); - ref_array_clear(&array); - return 0; -} diff --git a/third_party/git/builtin/fsck.c b/third_party/git/builtin/fsck.c deleted file mode 100644 index fbf26cafcfd7..000000000000 --- a/third_party/git/builtin/fsck.c +++ /dev/null @@ -1,961 +0,0 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "cache.h" -#include "repository.h" -#include "config.h" -#include "commit.h" -#include "tree.h" -#include "blob.h" -#include "tag.h" -#include "refs.h" -#include "pack.h" -#include "cache-tree.h" -#include "tree-walk.h" -#include "fsck.h" -#include "parse-options.h" -#include "dir.h" -#include "progress.h" -#include "streaming.h" -#include "decorate.h" -#include "packfile.h" -#include "object-store.h" -#include "run-command.h" -#include "worktree.h" - -#define REACHABLE 0x0001 -#define SEEN 0x0002 -#define HAS_OBJ 0x0004 -/* This flag is set if something points to this object. */ -#define USED 0x0008 - -static int show_root; -static int show_tags; -static int show_unreachable; -static int include_reflogs = 1; -static int check_full = 1; -static int connectivity_only; -static int check_strict; -static int keep_cache_objects; -static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT; -static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT; -static int errors_found; -static int write_lost_and_found; -static int verbose; -static int show_progress = -1; -static int show_dangling = 1; -static int name_objects; -#define ERROR_OBJECT 01 -#define ERROR_REACHABLE 02 -#define ERROR_PACK 04 -#define ERROR_REFS 010 -#define ERROR_COMMIT_GRAPH 020 -#define ERROR_MULTI_PACK_INDEX 040 - -static const char *describe_object(const struct object_id *oid) -{ - return fsck_describe_object(&fsck_walk_options, oid); -} - -static const char *printable_type(const struct object_id *oid, - enum object_type type) -{ - const char *ret; - - if (type == OBJ_NONE) - type = oid_object_info(the_repository, oid, NULL); - - ret = type_name(type); - if (!ret) - ret = _("unknown"); - - return ret; -} - -static int fsck_config(const char *var, const char *value, void *cb) -{ - if (strcmp(var, "fsck.skiplist") == 0) { - const char *path; - struct strbuf sb = STRBUF_INIT; - - if (git_config_pathname(&path, var, value)) - return 1; - strbuf_addf(&sb, "skiplist=%s", path); - free((char *)path); - fsck_set_msg_types(&fsck_obj_options, sb.buf); - strbuf_release(&sb); - return 0; - } - - if (skip_prefix(var, "fsck.", &var)) { - fsck_set_msg_type(&fsck_obj_options, var, value); - return 0; - } - - return git_default_config(var, value, cb); -} - -static int objerror(struct object *obj, const char *err) -{ - errors_found |= ERROR_OBJECT; - /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */ - fprintf_ln(stderr, _("error in %s %s: %s"), - printable_type(&obj->oid, obj->type), - describe_object(&obj->oid), err); - return -1; -} - -static int fsck_error_func(struct fsck_options *o, - const struct object_id *oid, - enum object_type object_type, - int msg_type, const char *message) -{ - switch (msg_type) { - case FSCK_WARN: - /* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */ - fprintf_ln(stderr, _("warning in %s %s: %s"), - printable_type(oid, object_type), - describe_object(oid), message); - return 0; - case FSCK_ERROR: - /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */ - fprintf_ln(stderr, _("error in %s %s: %s"), - printable_type(oid, object_type), - describe_object(oid), message); - return 1; - default: - BUG("%d (FSCK_IGNORE?) should never trigger this callback", - msg_type); - } -} - -static struct object_array pending; - -static int mark_object(struct object *obj, int type, void *data, struct fsck_options *options) -{ - struct object *parent = data; - - /* - * The only case data is NULL or type is OBJ_ANY is when - * mark_object_reachable() calls us. All the callers of - * that function has non-NULL obj hence ... - */ - if (!obj) { - /* ... these references to parent->fld are safe here */ - printf_ln(_("broken link from %7s %s"), - printable_type(&parent->oid, parent->type), - describe_object(&parent->oid)); - printf_ln(_("broken link from %7s %s"), - (type == OBJ_ANY ? _("unknown") : type_name(type)), - _("unknown")); - errors_found |= ERROR_REACHABLE; - return 1; - } - - if (type != OBJ_ANY && obj->type != type) - /* ... and the reference to parent is safe here */ - objerror(parent, _("wrong object type in link")); - - if (obj->flags & REACHABLE) - return 0; - obj->flags |= REACHABLE; - - if (is_promisor_object(&obj->oid)) - /* - * Further recursion does not need to be performed on this - * object since it is a promisor object (so it does not need to - * be added to "pending"). - */ - return 0; - - if (!(obj->flags & HAS_OBJ)) { - if (parent && !has_object(the_repository, &obj->oid, 1)) { - printf_ln(_("broken link from %7s %s\n" - " to %7s %s"), - printable_type(&parent->oid, parent->type), - describe_object(&parent->oid), - printable_type(&obj->oid, obj->type), - describe_object(&obj->oid)); - errors_found |= ERROR_REACHABLE; - } - return 1; - } - - add_object_array(obj, NULL, &pending); - return 0; -} - -static void mark_object_reachable(struct object *obj) -{ - mark_object(obj, OBJ_ANY, NULL, NULL); -} - -static int traverse_one_object(struct object *obj) -{ - int result = fsck_walk(obj, obj, &fsck_walk_options); - - if (obj->type == OBJ_TREE) { - struct tree *tree = (struct tree *)obj; - free_tree_buffer(tree); - } - return result; -} - -static int traverse_reachable(void) -{ - struct progress *progress = NULL; - unsigned int nr = 0; - int result = 0; - if (show_progress) - progress = start_delayed_progress(_("Checking connectivity"), 0); - while (pending.nr) { - result |= traverse_one_object(object_array_pop(&pending)); - display_progress(progress, ++nr); - } - stop_progress(&progress); - return !!result; -} - -static int mark_used(struct object *obj, int type, void *data, struct fsck_options *options) -{ - if (!obj) - return 1; - obj->flags |= USED; - return 0; -} - -static void mark_unreachable_referents(const struct object_id *oid) -{ - struct fsck_options options = FSCK_OPTIONS_DEFAULT; - struct object *obj = lookup_object(the_repository, oid); - - if (!obj || !(obj->flags & HAS_OBJ)) - return; /* not part of our original set */ - if (obj->flags & REACHABLE) - return; /* reachable objects already traversed */ - - /* - * Avoid passing OBJ_NONE to fsck_walk, which will parse the object - * (and we want to avoid parsing blobs). - */ - if (obj->type == OBJ_NONE) { - enum object_type type = oid_object_info(the_repository, - &obj->oid, NULL); - if (type > 0) - object_as_type(obj, type, 0); - } - - options.walk = mark_used; - fsck_walk(obj, NULL, &options); -} - -static int mark_loose_unreachable_referents(const struct object_id *oid, - const char *path, - void *data) -{ - mark_unreachable_referents(oid); - return 0; -} - -static int mark_packed_unreachable_referents(const struct object_id *oid, - struct packed_git *pack, - uint32_t pos, - void *data) -{ - mark_unreachable_referents(oid); - return 0; -} - -/* - * Check a single reachable object - */ -static void check_reachable_object(struct object *obj) -{ - /* - * We obviously want the object to be parsed, - * except if it was in a pack-file and we didn't - * do a full fsck - */ - if (!(obj->flags & HAS_OBJ)) { - if (is_promisor_object(&obj->oid)) - return; - if (has_object_pack(&obj->oid)) - return; /* it is in pack - forget about it */ - printf_ln(_("missing %s %s"), - printable_type(&obj->oid, obj->type), - describe_object(&obj->oid)); - errors_found |= ERROR_REACHABLE; - return; - } -} - -/* - * Check a single unreachable object - */ -static void check_unreachable_object(struct object *obj) -{ - /* - * Missing unreachable object? Ignore it. It's not like - * we miss it (since it can't be reached), nor do we want - * to complain about it being unreachable (since it does - * not exist). - */ - if (!(obj->flags & HAS_OBJ)) - return; - - /* - * Unreachable object that exists? Show it if asked to, - * since this is something that is prunable. - */ - if (show_unreachable) { - printf_ln(_("unreachable %s %s"), - printable_type(&obj->oid, obj->type), - describe_object(&obj->oid)); - return; - } - - /* - * "!USED" means that nothing at all points to it, including - * other unreachable objects. In other words, it's the "tip" - * of some set of unreachable objects, usually a commit that - * got dropped. - * - * Such starting points are more interesting than some random - * set of unreachable objects, so we show them even if the user - * hasn't asked for _all_ unreachable objects. If you have - * deleted a branch by mistake, this is a prime candidate to - * start looking at, for example. - */ - if (!(obj->flags & USED)) { - if (show_dangling) - printf_ln(_("dangling %s %s"), - printable_type(&obj->oid, obj->type), - describe_object(&obj->oid)); - if (write_lost_and_found) { - char *filename = git_pathdup("lost-found/%s/%s", - obj->type == OBJ_COMMIT ? "commit" : "other", - describe_object(&obj->oid)); - FILE *f; - - if (safe_create_leading_directories_const(filename)) { - error(_("could not create lost-found")); - free(filename); - return; - } - f = xfopen(filename, "w"); - if (obj->type == OBJ_BLOB) { - if (stream_blob_to_fd(fileno(f), &obj->oid, NULL, 1)) - die_errno(_("could not write '%s'"), filename); - } else - fprintf(f, "%s\n", describe_object(&obj->oid)); - if (fclose(f)) - die_errno(_("could not finish '%s'"), - filename); - free(filename); - } - return; - } - - /* - * Otherwise? It's there, it's unreachable, and some other unreachable - * object points to it. Ignore it - it's not interesting, and we showed - * all the interesting cases above. - */ -} - -static void check_object(struct object *obj) -{ - if (verbose) - fprintf_ln(stderr, _("Checking %s"), describe_object(&obj->oid)); - - if (obj->flags & REACHABLE) - check_reachable_object(obj); - else - check_unreachable_object(obj); -} - -static void check_connectivity(void) -{ - int i, max; - - /* Traverse the pending reachable objects */ - traverse_reachable(); - - /* - * With --connectivity-only, we won't have actually opened and marked - * unreachable objects with USED. Do that now to make --dangling, etc - * accurate. - */ - if (connectivity_only && (show_dangling || write_lost_and_found)) { - /* - * Even though we already have a "struct object" for each of - * these in memory, we must not iterate over the internal - * object hash as we do below. Our loop would potentially - * resize the hash, making our iteration invalid. - * - * Instead, we'll just go back to the source list of objects, - * and ignore any that weren't present in our earlier - * traversal. - */ - for_each_loose_object(mark_loose_unreachable_referents, NULL, 0); - for_each_packed_object(mark_packed_unreachable_referents, NULL, 0); - } - - /* Look up all the requirements, warn about missing objects.. */ - max = get_max_object_index(); - if (verbose) - fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max); - - for (i = 0; i < max; i++) { - struct object *obj = get_indexed_object(i); - - if (obj) - check_object(obj); - } -} - -static int fsck_obj(struct object *obj, void *buffer, unsigned long size) -{ - int err; - - if (obj->flags & SEEN) - return 0; - obj->flags |= SEEN; - - if (verbose) - fprintf_ln(stderr, _("Checking %s %s"), - printable_type(&obj->oid, obj->type), - describe_object(&obj->oid)); - - if (fsck_walk(obj, NULL, &fsck_obj_options)) - objerror(obj, _("broken links")); - err = fsck_object(obj, buffer, size, &fsck_obj_options); - if (err) - goto out; - - if (obj->type == OBJ_COMMIT) { - struct commit *commit = (struct commit *) obj; - - if (!commit->parents && show_root) - printf_ln(_("root %s"), - describe_object(&commit->object.oid)); - } - - if (obj->type == OBJ_TAG) { - struct tag *tag = (struct tag *) obj; - - if (show_tags && tag->tagged) { - printf_ln(_("tagged %s %s (%s) in %s"), - printable_type(&tag->tagged->oid, tag->tagged->type), - describe_object(&tag->tagged->oid), - tag->tag, - describe_object(&tag->object.oid)); - } - } - -out: - if (obj->type == OBJ_TREE) - free_tree_buffer((struct tree *)obj); - if (obj->type == OBJ_COMMIT) - free_commit_buffer(the_repository->parsed_objects, - (struct commit *)obj); - return err; -} - -static int fsck_obj_buffer(const struct object_id *oid, enum object_type type, - unsigned long size, void *buffer, int *eaten) -{ - /* - * Note, buffer may be NULL if type is OBJ_BLOB. See - * verify_packfile(), data_valid variable for details. - */ - struct object *obj; - obj = parse_object_buffer(the_repository, oid, type, size, buffer, - eaten); - if (!obj) { - errors_found |= ERROR_OBJECT; - return error(_("%s: object corrupt or missing"), - oid_to_hex(oid)); - } - obj->flags &= ~(REACHABLE | SEEN); - obj->flags |= HAS_OBJ; - return fsck_obj(obj, buffer, size); -} - -static int default_refs; - -static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, - timestamp_t timestamp) -{ - struct object *obj; - - if (!is_null_oid(oid)) { - obj = lookup_object(the_repository, oid); - if (obj && (obj->flags & HAS_OBJ)) { - if (timestamp) - fsck_put_object_name(&fsck_walk_options, oid, - "%s@{%"PRItime"}", - refname, timestamp); - obj->flags |= USED; - mark_object_reachable(obj); - } else if (!is_promisor_object(oid)) { - error(_("%s: invalid reflog entry %s"), - refname, oid_to_hex(oid)); - errors_found |= ERROR_REACHABLE; - } - } -} - -static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, timestamp_t timestamp, int tz, - const char *message, void *cb_data) -{ - const char *refname = cb_data; - - if (verbose) - fprintf_ln(stderr, _("Checking reflog %s->%s"), - oid_to_hex(ooid), oid_to_hex(noid)); - - fsck_handle_reflog_oid(refname, ooid, 0); - fsck_handle_reflog_oid(refname, noid, timestamp); - return 0; -} - -static int fsck_handle_reflog(const char *logname, const struct object_id *oid, - int flag, void *cb_data) -{ - struct strbuf refname = STRBUF_INIT; - - strbuf_worktree_ref(cb_data, &refname, logname); - for_each_reflog_ent(refname.buf, fsck_handle_reflog_ent, refname.buf); - strbuf_release(&refname); - return 0; -} - -static int fsck_handle_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) -{ - struct object *obj; - - obj = parse_object(the_repository, oid); - if (!obj) { - if (is_promisor_object(oid)) { - /* - * Increment default_refs anyway, because this is a - * valid ref. - */ - default_refs++; - return 0; - } - error(_("%s: invalid sha1 pointer %s"), - refname, oid_to_hex(oid)); - errors_found |= ERROR_REACHABLE; - /* We'll continue with the rest despite the error.. */ - return 0; - } - if (obj->type != OBJ_COMMIT && is_branch(refname)) { - error(_("%s: not a commit"), refname); - errors_found |= ERROR_REFS; - } - default_refs++; - obj->flags |= USED; - fsck_put_object_name(&fsck_walk_options, - oid, "%s", refname); - mark_object_reachable(obj); - - return 0; -} - -static int fsck_head_link(const char *head_ref_name, - const char **head_points_at, - struct object_id *head_oid); - -static void get_default_heads(void) -{ - struct worktree **worktrees, **p; - const char *head_points_at; - struct object_id head_oid; - - for_each_rawref(fsck_handle_ref, NULL); - - worktrees = get_worktrees(); - for (p = worktrees; *p; p++) { - struct worktree *wt = *p; - struct strbuf ref = STRBUF_INIT; - - strbuf_worktree_ref(wt, &ref, "HEAD"); - fsck_head_link(ref.buf, &head_points_at, &head_oid); - if (head_points_at && !is_null_oid(&head_oid)) - fsck_handle_ref(ref.buf, &head_oid, 0, NULL); - strbuf_release(&ref); - - if (include_reflogs) - refs_for_each_reflog(get_worktree_ref_store(wt), - fsck_handle_reflog, wt); - } - free_worktrees(worktrees); - - /* - * Not having any default heads isn't really fatal, but - * it does mean that "--unreachable" no longer makes any - * sense (since in this case everything will obviously - * be unreachable by definition. - * - * Showing dangling objects is valid, though (as those - * dangling objects are likely lost heads). - * - * So we just print a warning about it, and clear the - * "show_unreachable" flag. - */ - if (!default_refs) { - fprintf_ln(stderr, _("notice: No default references")); - show_unreachable = 0; - } -} - -static int fsck_loose(const struct object_id *oid, const char *path, void *data) -{ - struct object *obj; - enum object_type type; - unsigned long size; - void *contents; - int eaten; - - if (read_loose_object(path, oid, &type, &size, &contents) < 0) { - errors_found |= ERROR_OBJECT; - error(_("%s: object corrupt or missing: %s"), - oid_to_hex(oid), path); - return 0; /* keep checking other objects */ - } - - if (!contents && type != OBJ_BLOB) - BUG("read_loose_object streamed a non-blob"); - - obj = parse_object_buffer(the_repository, oid, type, size, - contents, &eaten); - - if (!obj) { - errors_found |= ERROR_OBJECT; - error(_("%s: object could not be parsed: %s"), - oid_to_hex(oid), path); - if (!eaten) - free(contents); - return 0; /* keep checking other objects */ - } - - obj->flags &= ~(REACHABLE | SEEN); - obj->flags |= HAS_OBJ; - if (fsck_obj(obj, contents, size)) - errors_found |= ERROR_OBJECT; - - if (!eaten) - free(contents); - return 0; /* keep checking other objects, even if we saw an error */ -} - -static int fsck_cruft(const char *basename, const char *path, void *data) -{ - if (!starts_with(basename, "tmp_obj_")) - fprintf_ln(stderr, _("bad sha1 file: %s"), path); - return 0; -} - -static int fsck_subdir(unsigned int nr, const char *path, void *progress) -{ - display_progress(progress, nr + 1); - return 0; -} - -static void fsck_object_dir(const char *path) -{ - struct progress *progress = NULL; - - if (verbose) - fprintf_ln(stderr, _("Checking object directory")); - - if (show_progress) - progress = start_progress(_("Checking object directories"), 256); - - for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir, - progress); - display_progress(progress, 256); - stop_progress(&progress); -} - -static int fsck_head_link(const char *head_ref_name, - const char **head_points_at, - struct object_id *head_oid) -{ - int null_is_error = 0; - - if (verbose) - fprintf_ln(stderr, _("Checking %s link"), head_ref_name); - - *head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL); - if (!*head_points_at) { - errors_found |= ERROR_REFS; - return error(_("invalid %s"), head_ref_name); - } - if (!strcmp(*head_points_at, head_ref_name)) - /* detached HEAD */ - null_is_error = 1; - else if (!starts_with(*head_points_at, "refs/heads/")) { - errors_found |= ERROR_REFS; - return error(_("%s points to something strange (%s)"), - head_ref_name, *head_points_at); - } - if (is_null_oid(head_oid)) { - if (null_is_error) { - errors_found |= ERROR_REFS; - return error(_("%s: detached HEAD points at nothing"), - head_ref_name); - } - fprintf_ln(stderr, - _("notice: %s points to an unborn branch (%s)"), - head_ref_name, *head_points_at + 11); - } - return 0; -} - -static int fsck_cache_tree(struct cache_tree *it) -{ - int i; - int err = 0; - - if (verbose) - fprintf_ln(stderr, _("Checking cache tree")); - - if (0 <= it->entry_count) { - struct object *obj = parse_object(the_repository, &it->oid); - if (!obj) { - error(_("%s: invalid sha1 pointer in cache-tree"), - oid_to_hex(&it->oid)); - errors_found |= ERROR_REFS; - return 1; - } - obj->flags |= USED; - fsck_put_object_name(&fsck_walk_options, &it->oid, ":"); - mark_object_reachable(obj); - if (obj->type != OBJ_TREE) - err |= objerror(obj, _("non-tree in cache-tree")); - } - for (i = 0; i < it->subtree_nr; i++) - err |= fsck_cache_tree(it->down[i]->cache_tree); - return err; -} - -static void mark_object_for_connectivity(const struct object_id *oid) -{ - struct object *obj = lookup_unknown_object(oid); - obj->flags |= HAS_OBJ; -} - -static int mark_loose_for_connectivity(const struct object_id *oid, - const char *path, - void *data) -{ - mark_object_for_connectivity(oid); - return 0; -} - -static int mark_packed_for_connectivity(const struct object_id *oid, - struct packed_git *pack, - uint32_t pos, - void *data) -{ - mark_object_for_connectivity(oid); - return 0; -} - -static char const * const fsck_usage[] = { - N_("git fsck [<options>] [<object>...]"), - NULL -}; - -static struct option fsck_opts[] = { - OPT__VERBOSE(&verbose, N_("be verbose")), - OPT_BOOL(0, "unreachable", &show_unreachable, N_("show unreachable objects")), - OPT_BOOL(0, "dangling", &show_dangling, N_("show dangling objects")), - OPT_BOOL(0, "tags", &show_tags, N_("report tags")), - OPT_BOOL(0, "root", &show_root, N_("report root nodes")), - OPT_BOOL(0, "cache", &keep_cache_objects, N_("make index objects head nodes")), - OPT_BOOL(0, "reflogs", &include_reflogs, N_("make reflogs head nodes (default)")), - OPT_BOOL(0, "full", &check_full, N_("also consider packs and alternate objects")), - OPT_BOOL(0, "connectivity-only", &connectivity_only, N_("check only connectivity")), - OPT_BOOL(0, "strict", &check_strict, N_("enable more strict checking")), - OPT_BOOL(0, "lost-found", &write_lost_and_found, - N_("write dangling objects in .git/lost-found")), - OPT_BOOL(0, "progress", &show_progress, N_("show progress")), - OPT_BOOL(0, "name-objects", &name_objects, N_("show verbose names for reachable objects")), - OPT_END(), -}; - -int cmd_fsck(int argc, const char **argv, const char *prefix) -{ - int i; - struct object_directory *odb; - - /* fsck knows how to handle missing promisor objects */ - fetch_if_missing = 0; - - errors_found = 0; - read_replace_refs = 0; - - argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); - - fsck_walk_options.walk = mark_object; - fsck_obj_options.walk = mark_used; - fsck_obj_options.error_func = fsck_error_func; - if (check_strict) - fsck_obj_options.strict = 1; - - if (show_progress == -1) - show_progress = isatty(2); - if (verbose) - show_progress = 0; - - if (write_lost_and_found) { - check_full = 1; - include_reflogs = 0; - } - - if (name_objects) - fsck_enable_object_names(&fsck_walk_options); - - git_config(fsck_config, NULL); - - if (connectivity_only) { - for_each_loose_object(mark_loose_for_connectivity, NULL, 0); - for_each_packed_object(mark_packed_for_connectivity, NULL, 0); - } else { - prepare_alt_odb(the_repository); - for (odb = the_repository->objects->odb; odb; odb = odb->next) - fsck_object_dir(odb->path); - - if (check_full) { - struct packed_git *p; - uint32_t total = 0, count = 0; - struct progress *progress = NULL; - - if (show_progress) { - for (p = get_all_packs(the_repository); p; - p = p->next) { - if (open_pack_index(p)) - continue; - total += p->num_objects; - } - - progress = start_progress(_("Checking objects"), total); - } - for (p = get_all_packs(the_repository); p; - p = p->next) { - /* verify gives error messages itself */ - if (verify_pack(the_repository, - p, fsck_obj_buffer, - progress, count)) - errors_found |= ERROR_PACK; - count += p->num_objects; - } - stop_progress(&progress); - } - - if (fsck_finish(&fsck_obj_options)) - errors_found |= ERROR_OBJECT; - } - - for (i = 0; i < argc; i++) { - const char *arg = argv[i]; - struct object_id oid; - if (!get_oid(arg, &oid)) { - struct object *obj = lookup_object(the_repository, - &oid); - - if (!obj || !(obj->flags & HAS_OBJ)) { - if (is_promisor_object(&oid)) - continue; - error(_("%s: object missing"), oid_to_hex(&oid)); - errors_found |= ERROR_OBJECT; - continue; - } - - obj->flags |= USED; - fsck_put_object_name(&fsck_walk_options, &oid, - "%s", arg); - mark_object_reachable(obj); - continue; - } - error(_("invalid parameter: expected sha1, got '%s'"), arg); - errors_found |= ERROR_OBJECT; - } - - /* - * If we've not been given any explicit head information, do the - * default ones from .git/refs. We also consider the index file - * in this case (ie this implies --cache). - */ - if (!argc) { - get_default_heads(); - keep_cache_objects = 1; - } - - if (keep_cache_objects) { - verify_index_checksum = 1; - verify_ce_order = 1; - read_cache(); - for (i = 0; i < active_nr; i++) { - unsigned int mode; - struct blob *blob; - struct object *obj; - - mode = active_cache[i]->ce_mode; - if (S_ISGITLINK(mode)) - continue; - blob = lookup_blob(the_repository, - &active_cache[i]->oid); - if (!blob) - continue; - obj = &blob->object; - obj->flags |= USED; - fsck_put_object_name(&fsck_walk_options, &obj->oid, - ":%s", active_cache[i]->name); - mark_object_reachable(obj); - } - if (active_cache_tree) - fsck_cache_tree(active_cache_tree); - } - - check_connectivity(); - - if (!git_config_get_bool("core.commitgraph", &i) && i) { - struct child_process commit_graph_verify = CHILD_PROCESS_INIT; - const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL }; - - prepare_alt_odb(the_repository); - for (odb = the_repository->objects->odb; odb; odb = odb->next) { - child_process_init(&commit_graph_verify); - commit_graph_verify.argv = verify_argv; - commit_graph_verify.git_cmd = 1; - verify_argv[2] = "--object-dir"; - verify_argv[3] = odb->path; - if (run_command(&commit_graph_verify)) - errors_found |= ERROR_COMMIT_GRAPH; - } - } - - if (!git_config_get_bool("core.multipackindex", &i) && i) { - struct child_process midx_verify = CHILD_PROCESS_INIT; - const char *midx_argv[] = { "multi-pack-index", "verify", NULL, NULL, NULL }; - - prepare_alt_odb(the_repository); - for (odb = the_repository->objects->odb; odb; odb = odb->next) { - child_process_init(&midx_verify); - midx_verify.argv = midx_argv; - midx_verify.git_cmd = 1; - midx_argv[2] = "--object-dir"; - midx_argv[3] = odb->path; - if (run_command(&midx_verify)) - errors_found |= ERROR_MULTI_PACK_INDEX; - } - } - - return errors_found; -} diff --git a/third_party/git/builtin/gc.c b/third_party/git/builtin/gc.c deleted file mode 100644 index 090959350e06..000000000000 --- a/third_party/git/builtin/gc.c +++ /dev/null @@ -1,1038 +0,0 @@ -/* - * git gc builtin command - * - * Cleanup unreachable files and optimize the repository. - * - * Copyright (c) 2007 James Bowes - * - * Based on git-gc.sh, which is - * - * Copyright (c) 2006 Shawn O. Pearce - */ - -#include "builtin.h" -#include "repository.h" -#include "config.h" -#include "tempfile.h" -#include "lockfile.h" -#include "parse-options.h" -#include "run-command.h" -#include "sigchain.h" -#include "strvec.h" -#include "commit.h" -#include "commit-graph.h" -#include "packfile.h" -#include "object-store.h" -#include "pack.h" -#include "pack-objects.h" -#include "blob.h" -#include "tree.h" -#include "promisor-remote.h" -#include "refs.h" - -#define FAILED_RUN "failed to run %s" - -static const char * const builtin_gc_usage[] = { - N_("git gc [<options>]"), - NULL -}; - -static int pack_refs = 1; -static int prune_reflogs = 1; -static int aggressive_depth = 50; -static int aggressive_window = 250; -static int gc_auto_threshold = 6700; -static int gc_auto_pack_limit = 50; -static int detach_auto = 1; -static timestamp_t gc_log_expire_time; -static const char *gc_log_expire = "1.day.ago"; -static const char *prune_expire = "2.weeks.ago"; -static const char *prune_worktrees_expire = "3.months.ago"; -static unsigned long big_pack_threshold; -static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE; - -static struct strvec pack_refs_cmd = STRVEC_INIT; -static struct strvec reflog = STRVEC_INIT; -static struct strvec repack = STRVEC_INIT; -static struct strvec prune = STRVEC_INIT; -static struct strvec prune_worktrees = STRVEC_INIT; -static struct strvec rerere = STRVEC_INIT; - -static struct tempfile *pidfile; -static struct lock_file log_lock; - -static struct string_list pack_garbage = STRING_LIST_INIT_DUP; - -static void clean_pack_garbage(void) -{ - int i; - for (i = 0; i < pack_garbage.nr; i++) - unlink_or_warn(pack_garbage.items[i].string); - string_list_clear(&pack_garbage, 0); -} - -static void report_pack_garbage(unsigned seen_bits, const char *path) -{ - if (seen_bits == PACKDIR_FILE_IDX) - string_list_append(&pack_garbage, path); -} - -static void process_log_file(void) -{ - struct stat st; - if (fstat(get_lock_file_fd(&log_lock), &st)) { - /* - * Perhaps there was an i/o error or another - * unlikely situation. Try to make a note of - * this in gc.log along with any existing - * messages. - */ - int saved_errno = errno; - fprintf(stderr, _("Failed to fstat %s: %s"), - get_tempfile_path(log_lock.tempfile), - strerror(saved_errno)); - fflush(stderr); - commit_lock_file(&log_lock); - errno = saved_errno; - } else if (st.st_size) { - /* There was some error recorded in the lock file */ - commit_lock_file(&log_lock); - } else { - /* No error, clean up any old gc.log */ - unlink(git_path("gc.log")); - rollback_lock_file(&log_lock); - } -} - -static void process_log_file_at_exit(void) -{ - fflush(stderr); - process_log_file(); -} - -static void process_log_file_on_signal(int signo) -{ - process_log_file(); - sigchain_pop(signo); - raise(signo); -} - -static int gc_config_is_timestamp_never(const char *var) -{ - const char *value; - timestamp_t expire; - - if (!git_config_get_value(var, &value) && value) { - if (parse_expiry_date(value, &expire)) - die(_("failed to parse '%s' value '%s'"), var, value); - return expire == 0; - } - return 0; -} - -static void gc_config(void) -{ - const char *value; - - if (!git_config_get_value("gc.packrefs", &value)) { - if (value && !strcmp(value, "notbare")) - pack_refs = -1; - else - pack_refs = git_config_bool("gc.packrefs", value); - } - - if (gc_config_is_timestamp_never("gc.reflogexpire") && - gc_config_is_timestamp_never("gc.reflogexpireunreachable")) - prune_reflogs = 0; - - git_config_get_int("gc.aggressivewindow", &aggressive_window); - git_config_get_int("gc.aggressivedepth", &aggressive_depth); - git_config_get_int("gc.auto", &gc_auto_threshold); - git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit); - git_config_get_bool("gc.autodetach", &detach_auto); - git_config_get_expiry("gc.pruneexpire", &prune_expire); - git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire); - git_config_get_expiry("gc.logexpiry", &gc_log_expire); - - git_config_get_ulong("gc.bigpackthreshold", &big_pack_threshold); - git_config_get_ulong("pack.deltacachesize", &max_delta_cache_size); - - git_config(git_default_config, NULL); -} - -static int too_many_loose_objects(void) -{ - /* - * Quickly check if a "gc" is needed, by estimating how - * many loose objects there are. Because SHA-1 is evenly - * distributed, we can check only one and get a reasonable - * estimate. - */ - DIR *dir; - struct dirent *ent; - int auto_threshold; - int num_loose = 0; - int needed = 0; - const unsigned hexsz_loose = the_hash_algo->hexsz - 2; - - dir = opendir(git_path("objects/17")); - if (!dir) - return 0; - - auto_threshold = DIV_ROUND_UP(gc_auto_threshold, 256); - while ((ent = readdir(dir)) != NULL) { - if (strspn(ent->d_name, "0123456789abcdef") != hexsz_loose || - ent->d_name[hexsz_loose] != '\0') - continue; - if (++num_loose > auto_threshold) { - needed = 1; - break; - } - } - closedir(dir); - return needed; -} - -static struct packed_git *find_base_packs(struct string_list *packs, - unsigned long limit) -{ - struct packed_git *p, *base = NULL; - - for (p = get_all_packs(the_repository); p; p = p->next) { - if (!p->pack_local) - continue; - if (limit) { - if (p->pack_size >= limit) - string_list_append(packs, p->pack_name); - } else if (!base || base->pack_size < p->pack_size) { - base = p; - } - } - - if (base) - string_list_append(packs, base->pack_name); - - return base; -} - -static int too_many_packs(void) -{ - struct packed_git *p; - int cnt; - - if (gc_auto_pack_limit <= 0) - return 0; - - for (cnt = 0, p = get_all_packs(the_repository); p; p = p->next) { - if (!p->pack_local) - continue; - if (p->pack_keep) - continue; - /* - * Perhaps check the size of the pack and count only - * very small ones here? - */ - cnt++; - } - return gc_auto_pack_limit < cnt; -} - -static uint64_t total_ram(void) -{ -#if defined(HAVE_SYSINFO) - struct sysinfo si; - - if (!sysinfo(&si)) - return si.totalram; -#elif defined(HAVE_BSD_SYSCTL) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM)) - int64_t physical_memory; - int mib[2]; - size_t length; - - mib[0] = CTL_HW; -# if defined(HW_MEMSIZE) - mib[1] = HW_MEMSIZE; -# else - mib[1] = HW_PHYSMEM; -# endif - length = sizeof(int64_t); - if (!sysctl(mib, 2, &physical_memory, &length, NULL, 0)) - return physical_memory; -#elif defined(GIT_WINDOWS_NATIVE) - MEMORYSTATUSEX memInfo; - - memInfo.dwLength = sizeof(MEMORYSTATUSEX); - if (GlobalMemoryStatusEx(&memInfo)) - return memInfo.ullTotalPhys; -#endif - return 0; -} - -static uint64_t estimate_repack_memory(struct packed_git *pack) -{ - unsigned long nr_objects = approximate_object_count(); - size_t os_cache, heap; - - if (!pack || !nr_objects) - return 0; - - /* - * First we have to scan through at least one pack. - * Assume enough room in OS file cache to keep the entire pack - * or we may accidentally evict data of other processes from - * the cache. - */ - os_cache = pack->pack_size + pack->index_size; - /* then pack-objects needs lots more for book keeping */ - heap = sizeof(struct object_entry) * nr_objects; - /* - * internal rev-list --all --objects takes up some memory too, - * let's say half of it is for blobs - */ - heap += sizeof(struct blob) * nr_objects / 2; - /* - * and the other half is for trees (commits and tags are - * usually insignificant) - */ - heap += sizeof(struct tree) * nr_objects / 2; - /* and then obj_hash[], underestimated in fact */ - heap += sizeof(struct object *) * nr_objects; - /* revindex is used also */ - heap += sizeof(struct revindex_entry) * nr_objects; - /* - * read_sha1_file() (either at delta calculation phase, or - * writing phase) also fills up the delta base cache - */ - heap += delta_base_cache_limit; - /* and of course pack-objects has its own delta cache */ - heap += max_delta_cache_size; - - return os_cache + heap; -} - -static int keep_one_pack(struct string_list_item *item, void *data) -{ - strvec_pushf(&repack, "--keep-pack=%s", basename(item->string)); - return 0; -} - -static void add_repack_all_option(struct string_list *keep_pack) -{ - if (prune_expire && !strcmp(prune_expire, "now")) - strvec_push(&repack, "-a"); - else { - strvec_push(&repack, "-A"); - if (prune_expire) - strvec_pushf(&repack, "--unpack-unreachable=%s", prune_expire); - } - - if (keep_pack) - for_each_string_list(keep_pack, keep_one_pack, NULL); -} - -static void add_repack_incremental_option(void) -{ - strvec_push(&repack, "--no-write-bitmap-index"); -} - -static int need_to_gc(void) -{ - /* - * Setting gc.auto to 0 or negative can disable the - * automatic gc. - */ - if (gc_auto_threshold <= 0) - return 0; - - /* - * If there are too many loose objects, but not too many - * packs, we run "repack -d -l". If there are too many packs, - * we run "repack -A -d -l". Otherwise we tell the caller - * there is no need. - */ - if (too_many_packs()) { - struct string_list keep_pack = STRING_LIST_INIT_NODUP; - - if (big_pack_threshold) { - find_base_packs(&keep_pack, big_pack_threshold); - if (keep_pack.nr >= gc_auto_pack_limit) { - big_pack_threshold = 0; - string_list_clear(&keep_pack, 0); - find_base_packs(&keep_pack, 0); - } - } else { - struct packed_git *p = find_base_packs(&keep_pack, 0); - uint64_t mem_have, mem_want; - - mem_have = total_ram(); - mem_want = estimate_repack_memory(p); - - /* - * Only allow 1/2 of memory for pack-objects, leave - * the rest for the OS and other processes in the - * system. - */ - if (!mem_have || mem_want < mem_have / 2) - string_list_clear(&keep_pack, 0); - } - - add_repack_all_option(&keep_pack); - string_list_clear(&keep_pack, 0); - } else if (too_many_loose_objects()) - add_repack_incremental_option(); - else - return 0; - - if (run_hook_le(NULL, "pre-auto-gc", NULL)) - return 0; - return 1; -} - -/* return NULL on success, else hostname running the gc */ -static const char *lock_repo_for_gc(int force, pid_t* ret_pid) -{ - struct lock_file lock = LOCK_INIT; - char my_host[HOST_NAME_MAX + 1]; - struct strbuf sb = STRBUF_INIT; - struct stat st; - uintmax_t pid; - FILE *fp; - int fd; - char *pidfile_path; - - if (is_tempfile_active(pidfile)) - /* already locked */ - return NULL; - - if (xgethostname(my_host, sizeof(my_host))) - xsnprintf(my_host, sizeof(my_host), "unknown"); - - pidfile_path = git_pathdup("gc.pid"); - fd = hold_lock_file_for_update(&lock, pidfile_path, - LOCK_DIE_ON_ERROR); - if (!force) { - static char locking_host[HOST_NAME_MAX + 1]; - static char *scan_fmt; - int should_exit; - - if (!scan_fmt) - scan_fmt = xstrfmt("%s %%%ds", "%"SCNuMAX, HOST_NAME_MAX); - fp = fopen(pidfile_path, "r"); - memset(locking_host, 0, sizeof(locking_host)); - should_exit = - fp != NULL && - !fstat(fileno(fp), &st) && - /* - * 12 hour limit is very generous as gc should - * never take that long. On the other hand we - * don't really need a strict limit here, - * running gc --auto one day late is not a big - * problem. --force can be used in manual gc - * after the user verifies that no gc is - * running. - */ - time(NULL) - st.st_mtime <= 12 * 3600 && - fscanf(fp, scan_fmt, &pid, locking_host) == 2 && - /* be gentle to concurrent "gc" on remote hosts */ - (strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM); - if (fp != NULL) - fclose(fp); - if (should_exit) { - if (fd >= 0) - rollback_lock_file(&lock); - *ret_pid = pid; - free(pidfile_path); - return locking_host; - } - } - - strbuf_addf(&sb, "%"PRIuMAX" %s", - (uintmax_t) getpid(), my_host); - write_in_full(fd, sb.buf, sb.len); - strbuf_release(&sb); - commit_lock_file(&lock); - pidfile = register_tempfile(pidfile_path); - free(pidfile_path); - return NULL; -} - -/* - * Returns 0 if there was no previous error and gc can proceed, 1 if - * gc should not proceed due to an error in the last run. Prints a - * message and returns -1 if an error occurred while reading gc.log - */ -static int report_last_gc_error(void) -{ - struct strbuf sb = STRBUF_INIT; - int ret = 0; - ssize_t len; - struct stat st; - char *gc_log_path = git_pathdup("gc.log"); - - if (stat(gc_log_path, &st)) { - if (errno == ENOENT) - goto done; - - ret = error_errno(_("cannot stat '%s'"), gc_log_path); - goto done; - } - - if (st.st_mtime < gc_log_expire_time) - goto done; - - len = strbuf_read_file(&sb, gc_log_path, 0); - if (len < 0) - ret = error_errno(_("cannot read '%s'"), gc_log_path); - else if (len > 0) { - /* - * A previous gc failed. Report the error, and don't - * bother with an automatic gc run since it is likely - * to fail in the same way. - */ - warning(_("The last gc run reported the following. " - "Please correct the root cause\n" - "and remove %s.\n" - "Automatic cleanup will not be performed " - "until the file is removed.\n\n" - "%s"), - gc_log_path, sb.buf); - ret = 1; - } - strbuf_release(&sb); -done: - free(gc_log_path); - return ret; -} - -static void gc_before_repack(void) -{ - /* - * We may be called twice, as both the pre- and - * post-daemonized phases will call us, but running these - * commands more than once is pointless and wasteful. - */ - static int done = 0; - if (done++) - return; - - if (pack_refs && run_command_v_opt(pack_refs_cmd.v, RUN_GIT_CMD)) - die(FAILED_RUN, pack_refs_cmd.v[0]); - - if (prune_reflogs && run_command_v_opt(reflog.v, RUN_GIT_CMD)) - die(FAILED_RUN, reflog.v[0]); -} - -int cmd_gc(int argc, const char **argv, const char *prefix) -{ - int aggressive = 0; - int auto_gc = 0; - int quiet = 0; - int force = 0; - const char *name; - pid_t pid; - int daemonized = 0; - int keep_base_pack = -1; - timestamp_t dummy; - - struct option builtin_gc_options[] = { - OPT__QUIET(&quiet, N_("suppress progress reporting")), - { OPTION_STRING, 0, "prune", &prune_expire, N_("date"), - N_("prune unreferenced objects"), - PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire }, - OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")), - OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"), - PARSE_OPT_NOCOMPLETE), - OPT_BOOL_F(0, "force", &force, - N_("force running gc even if there may be another gc running"), - PARSE_OPT_NOCOMPLETE), - OPT_BOOL(0, "keep-largest-pack", &keep_base_pack, - N_("repack all other packs except the largest pack")), - OPT_END() - }; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage_with_options(builtin_gc_usage, builtin_gc_options); - - strvec_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL); - strvec_pushl(&reflog, "reflog", "expire", "--all", NULL); - strvec_pushl(&repack, "repack", "-d", "-l", NULL); - strvec_pushl(&prune, "prune", "--expire", NULL); - strvec_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL); - strvec_pushl(&rerere, "rerere", "gc", NULL); - - /* default expiry time, overwritten in gc_config */ - gc_config(); - if (parse_expiry_date(gc_log_expire, &gc_log_expire_time)) - die(_("failed to parse gc.logexpiry value %s"), gc_log_expire); - - if (pack_refs < 0) - pack_refs = !is_bare_repository(); - - argc = parse_options(argc, argv, prefix, builtin_gc_options, - builtin_gc_usage, 0); - if (argc > 0) - usage_with_options(builtin_gc_usage, builtin_gc_options); - - if (prune_expire && parse_expiry_date(prune_expire, &dummy)) - die(_("failed to parse prune expiry value %s"), prune_expire); - - if (aggressive) { - strvec_push(&repack, "-f"); - if (aggressive_depth > 0) - strvec_pushf(&repack, "--depth=%d", aggressive_depth); - if (aggressive_window > 0) - strvec_pushf(&repack, "--window=%d", aggressive_window); - } - if (quiet) - strvec_push(&repack, "-q"); - - if (auto_gc) { - /* - * Auto-gc should be least intrusive as possible. - */ - if (!need_to_gc()) - return 0; - if (!quiet) { - if (detach_auto) - fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n")); - else - fprintf(stderr, _("Auto packing the repository for optimum performance.\n")); - fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n")); - } - if (detach_auto) { - int ret = report_last_gc_error(); - if (ret < 0) - /* an I/O error occurred, already reported */ - exit(128); - if (ret == 1) - /* Last gc --auto failed. Skip this one. */ - return 0; - - if (lock_repo_for_gc(force, &pid)) - return 0; - gc_before_repack(); /* dies on failure */ - delete_tempfile(&pidfile); - - /* - * failure to daemonize is ok, we'll continue - * in foreground - */ - daemonized = !daemonize(); - } - } else { - struct string_list keep_pack = STRING_LIST_INIT_NODUP; - - if (keep_base_pack != -1) { - if (keep_base_pack) - find_base_packs(&keep_pack, 0); - } else if (big_pack_threshold) { - find_base_packs(&keep_pack, big_pack_threshold); - } - - add_repack_all_option(&keep_pack); - string_list_clear(&keep_pack, 0); - } - - name = lock_repo_for_gc(force, &pid); - if (name) { - if (auto_gc) - return 0; /* be quiet on --auto */ - die(_("gc is already running on machine '%s' pid %"PRIuMAX" (use --force if not)"), - name, (uintmax_t)pid); - } - - if (daemonized) { - hold_lock_file_for_update(&log_lock, - git_path("gc.log"), - LOCK_DIE_ON_ERROR); - dup2(get_lock_file_fd(&log_lock), 2); - sigchain_push_common(process_log_file_on_signal); - atexit(process_log_file_at_exit); - } - - gc_before_repack(); - - if (!repository_format_precious_objects) { - close_object_store(the_repository->objects); - if (run_command_v_opt(repack.v, RUN_GIT_CMD)) - die(FAILED_RUN, repack.v[0]); - - if (prune_expire) { - strvec_push(&prune, prune_expire); - if (quiet) - strvec_push(&prune, "--no-progress"); - if (has_promisor_remote()) - strvec_push(&prune, - "--exclude-promisor-objects"); - if (run_command_v_opt(prune.v, RUN_GIT_CMD)) - die(FAILED_RUN, prune.v[0]); - } - } - - if (prune_worktrees_expire) { - strvec_push(&prune_worktrees, prune_worktrees_expire); - if (run_command_v_opt(prune_worktrees.v, RUN_GIT_CMD)) - die(FAILED_RUN, prune_worktrees.v[0]); - } - - if (run_command_v_opt(rerere.v, RUN_GIT_CMD)) - die(FAILED_RUN, rerere.v[0]); - - report_garbage = report_pack_garbage; - reprepare_packed_git(the_repository); - if (pack_garbage.nr > 0) { - close_object_store(the_repository->objects); - clean_pack_garbage(); - } - - prepare_repo_settings(the_repository); - if (the_repository->settings.gc_write_commit_graph == 1) - write_commit_graph_reachable(the_repository->objects->odb, - !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0, - NULL); - - if (auto_gc && too_many_loose_objects()) - warning(_("There are too many unreachable loose objects; " - "run 'git prune' to remove them.")); - - if (!daemonized) - unlink(git_path("gc.log")); - - return 0; -} - -static const char * const builtin_maintenance_run_usage[] = { - N_("git maintenance run [--auto] [--[no-]quiet] [--task=<task>]"), - NULL -}; - -struct maintenance_run_opts { - int auto_flag; - int quiet; -}; - -/* Remember to update object flag allocation in object.h */ -#define SEEN (1u<<0) - -struct cg_auto_data { - int num_not_in_graph; - int limit; -}; - -static int dfs_on_ref(const char *refname, - const struct object_id *oid, int flags, - void *cb_data) -{ - struct cg_auto_data *data = (struct cg_auto_data *)cb_data; - int result = 0; - struct object_id peeled; - struct commit_list *stack = NULL; - struct commit *commit; - - if (!peel_ref(refname, &peeled)) - oid = &peeled; - if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT) - return 0; - - commit = lookup_commit(the_repository, oid); - if (!commit) - return 0; - if (parse_commit(commit)) - return 0; - - commit_list_append(commit, &stack); - - while (!result && stack) { - struct commit_list *parent; - - commit = pop_commit(&stack); - - for (parent = commit->parents; parent; parent = parent->next) { - if (parse_commit(parent->item) || - commit_graph_position(parent->item) != COMMIT_NOT_FROM_GRAPH || - parent->item->object.flags & SEEN) - continue; - - parent->item->object.flags |= SEEN; - data->num_not_in_graph++; - - if (data->num_not_in_graph >= data->limit) { - result = 1; - break; - } - - commit_list_append(parent->item, &stack); - } - } - - free_commit_list(stack); - return result; -} - -static int should_write_commit_graph(void) -{ - int result; - struct cg_auto_data data; - - data.num_not_in_graph = 0; - data.limit = 100; - git_config_get_int("maintenance.commit-graph.auto", - &data.limit); - - if (!data.limit) - return 0; - if (data.limit < 0) - return 1; - - result = for_each_ref(dfs_on_ref, &data); - - clear_commit_marks_all(SEEN); - - return result; -} - -static int run_write_commit_graph(struct maintenance_run_opts *opts) -{ - struct child_process child = CHILD_PROCESS_INIT; - - child.git_cmd = 1; - strvec_pushl(&child.args, "commit-graph", "write", - "--split", "--reachable", NULL); - - if (opts->quiet) - strvec_push(&child.args, "--no-progress"); - - return !!run_command(&child); -} - -static int maintenance_task_commit_graph(struct maintenance_run_opts *opts) -{ - close_object_store(the_repository->objects); - if (run_write_commit_graph(opts)) { - error(_("failed to write commit-graph")); - return 1; - } - - return 0; -} - -static int maintenance_task_gc(struct maintenance_run_opts *opts) -{ - struct child_process child = CHILD_PROCESS_INIT; - - child.git_cmd = 1; - strvec_push(&child.args, "gc"); - - if (opts->auto_flag) - strvec_push(&child.args, "--auto"); - if (opts->quiet) - strvec_push(&child.args, "--quiet"); - else - strvec_push(&child.args, "--no-quiet"); - - close_object_store(the_repository->objects); - return run_command(&child); -} - -typedef int maintenance_task_fn(struct maintenance_run_opts *opts); - -/* - * An auto condition function returns 1 if the task should run - * and 0 if the task should NOT run. See needs_to_gc() for an - * example. - */ -typedef int maintenance_auto_fn(void); - -struct maintenance_task { - const char *name; - maintenance_task_fn *fn; - maintenance_auto_fn *auto_condition; - unsigned enabled:1; - - /* -1 if not selected. */ - int selected_order; -}; - -enum maintenance_task_label { - TASK_GC, - TASK_COMMIT_GRAPH, - - /* Leave as final value */ - TASK__COUNT -}; - -static struct maintenance_task tasks[] = { - [TASK_GC] = { - "gc", - maintenance_task_gc, - need_to_gc, - 1, - }, - [TASK_COMMIT_GRAPH] = { - "commit-graph", - maintenance_task_commit_graph, - should_write_commit_graph, - }, -}; - -static int compare_tasks_by_selection(const void *a_, const void *b_) -{ - const struct maintenance_task *a, *b; - - a = (const struct maintenance_task *)&a_; - b = (const struct maintenance_task *)&b_; - - return b->selected_order - a->selected_order; -} - -static int maintenance_run_tasks(struct maintenance_run_opts *opts) -{ - int i, found_selected = 0; - int result = 0; - struct lock_file lk; - struct repository *r = the_repository; - char *lock_path = xstrfmt("%s/maintenance", r->objects->odb->path); - - if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) { - /* - * Another maintenance command is running. - * - * If --auto was provided, then it is likely due to a - * recursive process stack. Do not report an error in - * that case. - */ - if (!opts->auto_flag && !opts->quiet) - warning(_("lock file '%s' exists, skipping maintenance"), - lock_path); - free(lock_path); - return 0; - } - free(lock_path); - - for (i = 0; !found_selected && i < TASK__COUNT; i++) - found_selected = tasks[i].selected_order >= 0; - - if (found_selected) - QSORT(tasks, TASK__COUNT, compare_tasks_by_selection); - - for (i = 0; i < TASK__COUNT; i++) { - if (found_selected && tasks[i].selected_order < 0) - continue; - - if (!found_selected && !tasks[i].enabled) - continue; - - if (opts->auto_flag && - (!tasks[i].auto_condition || - !tasks[i].auto_condition())) - continue; - - trace2_region_enter("maintenance", tasks[i].name, r); - if (tasks[i].fn(opts)) { - error(_("task '%s' failed"), tasks[i].name); - result = 1; - } - trace2_region_leave("maintenance", tasks[i].name, r); - } - - rollback_lock_file(&lk); - return result; -} - -static void initialize_task_config(void) -{ - int i; - struct strbuf config_name = STRBUF_INIT; - gc_config(); - - for (i = 0; i < TASK__COUNT; i++) { - int config_value; - - strbuf_setlen(&config_name, 0); - strbuf_addf(&config_name, "maintenance.%s.enabled", - tasks[i].name); - - if (!git_config_get_bool(config_name.buf, &config_value)) - tasks[i].enabled = config_value; - } - - strbuf_release(&config_name); -} - -static int task_option_parse(const struct option *opt, - const char *arg, int unset) -{ - int i, num_selected = 0; - struct maintenance_task *task = NULL; - - BUG_ON_OPT_NEG(unset); - - for (i = 0; i < TASK__COUNT; i++) { - if (tasks[i].selected_order >= 0) - num_selected++; - if (!strcasecmp(tasks[i].name, arg)) { - task = &tasks[i]; - } - } - - if (!task) { - error(_("'%s' is not a valid task"), arg); - return 1; - } - - if (task->selected_order >= 0) { - error(_("task '%s' cannot be selected multiple times"), arg); - return 1; - } - - task->selected_order = num_selected + 1; - - return 0; -} - -static int maintenance_run(int argc, const char **argv, const char *prefix) -{ - int i; - struct maintenance_run_opts opts; - struct option builtin_maintenance_run_options[] = { - OPT_BOOL(0, "auto", &opts.auto_flag, - N_("run tasks based on the state of the repository")), - OPT_BOOL(0, "quiet", &opts.quiet, - N_("do not report progress or other information over stderr")), - OPT_CALLBACK_F(0, "task", NULL, N_("task"), - N_("run a specific task"), - PARSE_OPT_NONEG, task_option_parse), - OPT_END() - }; - memset(&opts, 0, sizeof(opts)); - - opts.quiet = !isatty(2); - initialize_task_config(); - - for (i = 0; i < TASK__COUNT; i++) - tasks[i].selected_order = -1; - - argc = parse_options(argc, argv, prefix, - builtin_maintenance_run_options, - builtin_maintenance_run_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - - if (argc != 0) - usage_with_options(builtin_maintenance_run_usage, - builtin_maintenance_run_options); - return maintenance_run_tasks(&opts); -} - -static const char builtin_maintenance_usage[] = N_("git maintenance run [<options>]"); - -int cmd_maintenance(int argc, const char **argv, const char *prefix) -{ - if (argc < 2 || - (argc == 2 && !strcmp(argv[1], "-h"))) - usage(builtin_maintenance_usage); - - if (!strcmp(argv[1], "run")) - return maintenance_run(argc - 1, argv + 1, prefix); - - die(_("invalid subcommand: %s"), argv[1]); -} diff --git a/third_party/git/builtin/get-tar-commit-id.c b/third_party/git/builtin/get-tar-commit-id.c deleted file mode 100644 index 491af9202dc9..000000000000 --- a/third_party/git/builtin/get-tar-commit-id.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2005, 2006 Rene Scharfe - */ -#include "cache.h" -#include "commit.h" -#include "tar.h" -#include "builtin.h" -#include "quote.h" - -static const char builtin_get_tar_commit_id_usage[] = -"git get-tar-commit-id"; - -/* ustar header + extended global header content */ -#define RECORDSIZE (512) -#define HEADERSIZE (2 * RECORDSIZE) - -int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix) -{ - char buffer[HEADERSIZE]; - struct ustar_header *header = (struct ustar_header *)buffer; - char *content = buffer + RECORDSIZE; - const char *comment; - ssize_t n; - long len; - char *end; - - if (argc != 1) - usage(builtin_get_tar_commit_id_usage); - - n = read_in_full(0, buffer, HEADERSIZE); - if (n < 0) - die_errno("git get-tar-commit-id: read error"); - if (n != HEADERSIZE) - die_errno("git get-tar-commit-id: EOF before reading tar header"); - if (header->typeflag[0] != 'g') - return 1; - - len = strtol(content, &end, 10); - if (errno == ERANGE || end == content || len < 0) - return 1; - if (!skip_prefix(end, " comment=", &comment)) - return 1; - len -= comment - content; - if (len < 1 || !(len % 2) || - hash_algo_by_length((len - 1) / 2) == GIT_HASH_UNKNOWN) - return 1; - - if (write_in_full(1, comment, len) < 0) - die_errno("git get-tar-commit-id: write error"); - - return 0; -} diff --git a/third_party/git/builtin/grep.c b/third_party/git/builtin/grep.c deleted file mode 100644 index c8037388c6e7..000000000000 --- a/third_party/git/builtin/grep.c +++ /dev/null @@ -1,1175 +0,0 @@ -/* - * Builtin "git grep" - * - * Copyright (c) 2006 Junio C Hamano - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "repository.h" -#include "config.h" -#include "blob.h" -#include "tree.h" -#include "commit.h" -#include "tag.h" -#include "tree-walk.h" -#include "builtin.h" -#include "parse-options.h" -#include "string-list.h" -#include "run-command.h" -#include "userdiff.h" -#include "grep.h" -#include "quote.h" -#include "dir.h" -#include "pathspec.h" -#include "submodule.h" -#include "submodule-config.h" -#include "object-store.h" -#include "packfile.h" - -static char const * const grep_usage[] = { - N_("git grep [<options>] [-e] <pattern> [<rev>...] [[--] <path>...]"), - NULL -}; - -static int recurse_submodules; - -static int num_threads; - -static pthread_t *threads; - -/* We use one producer thread and THREADS consumer - * threads. The producer adds struct work_items to 'todo' and the - * consumers pick work items from the same array. - */ -struct work_item { - struct grep_source source; - char done; - struct strbuf out; -}; - -/* In the range [todo_done, todo_start) in 'todo' we have work_items - * that have been or are processed by a consumer thread. We haven't - * written the result for these to stdout yet. - * - * The work_items in [todo_start, todo_end) are waiting to be picked - * up by a consumer thread. - * - * The ranges are modulo TODO_SIZE. - */ -#define TODO_SIZE 128 -static struct work_item todo[TODO_SIZE]; -static int todo_start; -static int todo_end; -static int todo_done; - -/* Has all work items been added? */ -static int all_work_added; - -/* This lock protects all the variables above. */ -static pthread_mutex_t grep_mutex; - -static inline void grep_lock(void) -{ - pthread_mutex_lock(&grep_mutex); -} - -static inline void grep_unlock(void) -{ - pthread_mutex_unlock(&grep_mutex); -} - -/* Signalled when a new work_item is added to todo. */ -static pthread_cond_t cond_add; - -/* Signalled when the result from one work_item is written to - * stdout. - */ -static pthread_cond_t cond_write; - -/* Signalled when we are finished with everything. */ -static pthread_cond_t cond_result; - -static int skip_first_line; - -static void add_work(struct grep_opt *opt, struct grep_source *gs) -{ - if (opt->binary != GREP_BINARY_TEXT) - grep_source_load_driver(gs, opt->repo->index); - - grep_lock(); - - while ((todo_end+1) % ARRAY_SIZE(todo) == todo_done) { - pthread_cond_wait(&cond_write, &grep_mutex); - } - - todo[todo_end].source = *gs; - todo[todo_end].done = 0; - strbuf_reset(&todo[todo_end].out); - todo_end = (todo_end + 1) % ARRAY_SIZE(todo); - - pthread_cond_signal(&cond_add); - grep_unlock(); -} - -static struct work_item *get_work(void) -{ - struct work_item *ret; - - grep_lock(); - while (todo_start == todo_end && !all_work_added) { - pthread_cond_wait(&cond_add, &grep_mutex); - } - - if (todo_start == todo_end && all_work_added) { - ret = NULL; - } else { - ret = &todo[todo_start]; - todo_start = (todo_start + 1) % ARRAY_SIZE(todo); - } - grep_unlock(); - return ret; -} - -static void work_done(struct work_item *w) -{ - int old_done; - - grep_lock(); - w->done = 1; - old_done = todo_done; - for(; todo[todo_done].done && todo_done != todo_start; - todo_done = (todo_done+1) % ARRAY_SIZE(todo)) { - w = &todo[todo_done]; - if (w->out.len) { - const char *p = w->out.buf; - size_t len = w->out.len; - - /* Skip the leading hunk mark of the first file. */ - if (skip_first_line) { - while (len) { - len--; - if (*p++ == '\n') - break; - } - skip_first_line = 0; - } - - write_or_die(1, p, len); - } - grep_source_clear(&w->source); - } - - if (old_done != todo_done) - pthread_cond_signal(&cond_write); - - if (all_work_added && todo_done == todo_end) - pthread_cond_signal(&cond_result); - - grep_unlock(); -} - -static void *run(void *arg) -{ - int hit = 0; - struct grep_opt *opt = arg; - - while (1) { - struct work_item *w = get_work(); - if (!w) - break; - - opt->output_priv = w; - hit |= grep_source(opt, &w->source); - grep_source_clear_data(&w->source); - work_done(w); - } - free_grep_patterns(arg); - free(arg); - - return (void*) (intptr_t) hit; -} - -static void strbuf_out(struct grep_opt *opt, const void *buf, size_t size) -{ - struct work_item *w = opt->output_priv; - strbuf_add(&w->out, buf, size); -} - -static void start_threads(struct grep_opt *opt) -{ - int i; - - pthread_mutex_init(&grep_mutex, NULL); - pthread_mutex_init(&grep_attr_mutex, NULL); - pthread_cond_init(&cond_add, NULL); - pthread_cond_init(&cond_write, NULL); - pthread_cond_init(&cond_result, NULL); - grep_use_locks = 1; - enable_obj_read_lock(); - - for (i = 0; i < ARRAY_SIZE(todo); i++) { - strbuf_init(&todo[i].out, 0); - } - - threads = xcalloc(num_threads, sizeof(*threads)); - for (i = 0; i < num_threads; i++) { - int err; - struct grep_opt *o = grep_opt_dup(opt); - o->output = strbuf_out; - if (i) - o->debug = 0; - compile_grep_patterns(o); - err = pthread_create(&threads[i], NULL, run, o); - - if (err) - die(_("grep: failed to create thread: %s"), - strerror(err)); - } -} - -static int wait_all(void) -{ - int hit = 0; - int i; - - if (!HAVE_THREADS) - BUG("Never call this function unless you have started threads"); - - grep_lock(); - all_work_added = 1; - - /* Wait until all work is done. */ - while (todo_done != todo_end) - pthread_cond_wait(&cond_result, &grep_mutex); - - /* Wake up all the consumer threads so they can see that there - * is no more work to do. - */ - pthread_cond_broadcast(&cond_add); - grep_unlock(); - - for (i = 0; i < num_threads; i++) { - void *h; - pthread_join(threads[i], &h); - hit |= (int) (intptr_t) h; - } - - free(threads); - - pthread_mutex_destroy(&grep_mutex); - pthread_mutex_destroy(&grep_attr_mutex); - pthread_cond_destroy(&cond_add); - pthread_cond_destroy(&cond_write); - pthread_cond_destroy(&cond_result); - grep_use_locks = 0; - disable_obj_read_lock(); - - return hit; -} - -static int grep_cmd_config(const char *var, const char *value, void *cb) -{ - int st = grep_config(var, value, cb); - if (git_color_default_config(var, value, cb) < 0) - st = -1; - - if (!strcmp(var, "grep.threads")) { - num_threads = git_config_int(var, value); - if (num_threads < 0) - die(_("invalid number of threads specified (%d) for %s"), - num_threads, var); - else if (!HAVE_THREADS && num_threads > 1) { - /* - * TRANSLATORS: %s is the configuration - * variable for tweaking threads, currently - * grep.threads - */ - warning(_("no threads support, ignoring %s"), var); - num_threads = 1; - } - } - - if (!strcmp(var, "submodule.recurse")) - recurse_submodules = git_config_bool(var, value); - - return st; -} - -static void grep_source_name(struct grep_opt *opt, const char *filename, - int tree_name_len, struct strbuf *out) -{ - strbuf_reset(out); - - if (opt->null_following_name) { - if (opt->relative && opt->prefix_length) { - struct strbuf rel_buf = STRBUF_INIT; - const char *rel_name = - relative_path(filename + tree_name_len, - opt->prefix, &rel_buf); - - if (tree_name_len) - strbuf_add(out, filename, tree_name_len); - - strbuf_addstr(out, rel_name); - strbuf_release(&rel_buf); - } else { - strbuf_addstr(out, filename); - } - return; - } - - if (opt->relative && opt->prefix_length) - quote_path(filename + tree_name_len, opt->prefix, out, 0); - else - quote_c_style(filename + tree_name_len, out, NULL, 0); - - if (tree_name_len) - strbuf_insert(out, 0, filename, tree_name_len); -} - -static int grep_oid(struct grep_opt *opt, const struct object_id *oid, - const char *filename, int tree_name_len, - const char *path) -{ - struct strbuf pathbuf = STRBUF_INIT; - struct grep_source gs; - - grep_source_name(opt, filename, tree_name_len, &pathbuf); - grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid); - strbuf_release(&pathbuf); - - if (num_threads > 1) { - /* - * add_work() copies gs and thus assumes ownership of - * its fields, so do not call grep_source_clear() - */ - add_work(opt, &gs); - return 0; - } else { - int hit; - - hit = grep_source(opt, &gs); - - grep_source_clear(&gs); - return hit; - } -} - -static int grep_file(struct grep_opt *opt, const char *filename) -{ - struct strbuf buf = STRBUF_INIT; - struct grep_source gs; - - grep_source_name(opt, filename, 0, &buf); - grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename); - strbuf_release(&buf); - - if (num_threads > 1) { - /* - * add_work() copies gs and thus assumes ownership of - * its fields, so do not call grep_source_clear() - */ - add_work(opt, &gs); - return 0; - } else { - int hit; - - hit = grep_source(opt, &gs); - - grep_source_clear(&gs); - return hit; - } -} - -static void append_path(struct grep_opt *opt, const void *data, size_t len) -{ - struct string_list *path_list = opt->output_priv; - - if (len == 1 && *(const char *)data == '\0') - return; - string_list_append(path_list, xstrndup(data, len)); -} - -static void run_pager(struct grep_opt *opt, const char *prefix) -{ - struct string_list *path_list = opt->output_priv; - struct child_process child = CHILD_PROCESS_INIT; - int i, status; - - for (i = 0; i < path_list->nr; i++) - strvec_push(&child.args, path_list->items[i].string); - child.dir = prefix; - child.use_shell = 1; - - status = run_command(&child); - if (status) - exit(status); -} - -static int grep_cache(struct grep_opt *opt, - const struct pathspec *pathspec, int cached); -static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, - struct tree_desc *tree, struct strbuf *base, int tn_len, - int check_attr); - -static int grep_submodule(struct grep_opt *opt, - const struct pathspec *pathspec, - const struct object_id *oid, - const char *filename, const char *path, int cached) -{ - struct repository subrepo; - struct repository *superproject = opt->repo; - const struct submodule *sub; - struct grep_opt subopt; - int hit; - - sub = submodule_from_path(superproject, &null_oid, path); - - if (!is_submodule_active(superproject, path)) - return 0; - - if (repo_submodule_init(&subrepo, superproject, sub)) - return 0; - - /* - * NEEDSWORK: repo_read_gitmodules() might call - * add_to_alternates_memory() via config_from_gitmodules(). This - * operation causes a race condition with concurrent object readings - * performed by the worker threads. That's why we need obj_read_lock() - * here. It should be removed once it's no longer necessary to add the - * subrepo's odbs to the in-memory alternates list. - */ - obj_read_lock(); - repo_read_gitmodules(&subrepo, 0); - - /* - * NEEDSWORK: This adds the submodule's object directory to the list of - * alternates for the single in-memory object store. This has some bad - * consequences for memory (processed objects will never be freed) and - * performance (this increases the number of pack files git has to pay - * attention to, to the sum of the number of pack files in all the - * repositories processed so far). This can be removed once the object - * store is no longer global and instead is a member of the repository - * object. - */ - add_to_alternates_memory(subrepo.objects->odb->path); - obj_read_unlock(); - - memcpy(&subopt, opt, sizeof(subopt)); - subopt.repo = &subrepo; - - if (oid) { - struct object *object; - struct tree_desc tree; - void *data; - unsigned long size; - struct strbuf base = STRBUF_INIT; - - obj_read_lock(); - object = parse_object_or_die(oid, NULL); - obj_read_unlock(); - data = read_object_with_reference(&subrepo, - &object->oid, tree_type, - &size, NULL); - if (!data) - die(_("unable to read tree (%s)"), oid_to_hex(&object->oid)); - - strbuf_addstr(&base, filename); - strbuf_addch(&base, '/'); - - init_tree_desc(&tree, data, size); - hit = grep_tree(&subopt, pathspec, &tree, &base, base.len, - object->type == OBJ_COMMIT); - strbuf_release(&base); - free(data); - } else { - hit = grep_cache(&subopt, pathspec, cached); - } - - repo_clear(&subrepo); - return hit; -} - -static int grep_cache(struct grep_opt *opt, - const struct pathspec *pathspec, int cached) -{ - struct repository *repo = opt->repo; - int hit = 0; - int nr; - struct strbuf name = STRBUF_INIT; - int name_base_len = 0; - if (repo->submodule_prefix) { - name_base_len = strlen(repo->submodule_prefix); - strbuf_addstr(&name, repo->submodule_prefix); - } - - if (repo_read_index(repo) < 0) - die(_("index file corrupt")); - - for (nr = 0; nr < repo->index->cache_nr; nr++) { - const struct cache_entry *ce = repo->index->cache[nr]; - strbuf_setlen(&name, name_base_len); - strbuf_addstr(&name, ce->name); - - if (S_ISREG(ce->ce_mode) && - match_pathspec(repo->index, pathspec, name.buf, name.len, 0, NULL, - S_ISDIR(ce->ce_mode) || - S_ISGITLINK(ce->ce_mode))) { - /* - * If CE_VALID is on, we assume worktree file and its - * cache entry are identical, even if worktree file has - * been modified, so use cache version instead - */ - if (cached || (ce->ce_flags & CE_VALID) || - ce_skip_worktree(ce)) { - if (ce_stage(ce) || ce_intent_to_add(ce)) - continue; - hit |= grep_oid(opt, &ce->oid, name.buf, - 0, name.buf); - } else { - hit |= grep_file(opt, name.buf); - } - } else if (recurse_submodules && S_ISGITLINK(ce->ce_mode) && - submodule_path_match(repo->index, pathspec, name.buf, NULL)) { - hit |= grep_submodule(opt, pathspec, NULL, ce->name, - ce->name, cached); - } else { - continue; - } - - if (ce_stage(ce)) { - do { - nr++; - } while (nr < repo->index->cache_nr && - !strcmp(ce->name, repo->index->cache[nr]->name)); - nr--; /* compensate for loop control */ - } - if (hit && opt->status_only) - break; - } - - strbuf_release(&name); - return hit; -} - -static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, - struct tree_desc *tree, struct strbuf *base, int tn_len, - int check_attr) -{ - struct repository *repo = opt->repo; - int hit = 0; - enum interesting match = entry_not_interesting; - struct name_entry entry; - int old_baselen = base->len; - struct strbuf name = STRBUF_INIT; - int name_base_len = 0; - if (repo->submodule_prefix) { - strbuf_addstr(&name, repo->submodule_prefix); - name_base_len = name.len; - } - - while (tree_entry(tree, &entry)) { - int te_len = tree_entry_len(&entry); - - if (match != all_entries_interesting) { - strbuf_addstr(&name, base->buf + tn_len); - match = tree_entry_interesting(repo->index, - &entry, &name, - 0, pathspec); - strbuf_setlen(&name, name_base_len); - - if (match == all_entries_not_interesting) - break; - if (match == entry_not_interesting) - continue; - } - - strbuf_add(base, entry.path, te_len); - - if (S_ISREG(entry.mode)) { - hit |= grep_oid(opt, &entry.oid, base->buf, tn_len, - check_attr ? base->buf + tn_len : NULL); - } else if (S_ISDIR(entry.mode)) { - enum object_type type; - struct tree_desc sub; - void *data; - unsigned long size; - - data = read_object_file(&entry.oid, &type, &size); - if (!data) - die(_("unable to read tree (%s)"), - oid_to_hex(&entry.oid)); - - strbuf_addch(base, '/'); - init_tree_desc(&sub, data, size); - hit |= grep_tree(opt, pathspec, &sub, base, tn_len, - check_attr); - free(data); - } else if (recurse_submodules && S_ISGITLINK(entry.mode)) { - hit |= grep_submodule(opt, pathspec, &entry.oid, - base->buf, base->buf + tn_len, - 1); /* ignored */ - } - - strbuf_setlen(base, old_baselen); - - if (hit && opt->status_only) - break; - } - - strbuf_release(&name); - return hit; -} - -static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, - struct object *obj, const char *name, const char *path) -{ - if (obj->type == OBJ_BLOB) - return grep_oid(opt, &obj->oid, name, 0, path); - if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) { - struct tree_desc tree; - void *data; - unsigned long size; - struct strbuf base; - int hit, len; - - data = read_object_with_reference(opt->repo, - &obj->oid, tree_type, - &size, NULL); - if (!data) - die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid)); - - len = name ? strlen(name) : 0; - strbuf_init(&base, PATH_MAX + len + 1); - if (len) { - strbuf_add(&base, name, len); - strbuf_addch(&base, ':'); - } - init_tree_desc(&tree, data, size); - hit = grep_tree(opt, pathspec, &tree, &base, base.len, - obj->type == OBJ_COMMIT); - strbuf_release(&base); - free(data); - return hit; - } - die(_("unable to grep from object of type %s"), type_name(obj->type)); -} - -static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, - const struct object_array *list) -{ - unsigned int i; - int hit = 0; - const unsigned int nr = list->nr; - - for (i = 0; i < nr; i++) { - struct object *real_obj; - - obj_read_lock(); - real_obj = deref_tag(opt->repo, list->objects[i].item, - NULL, 0); - obj_read_unlock(); - - /* load the gitmodules file for this rev */ - if (recurse_submodules) { - submodule_free(opt->repo); - obj_read_lock(); - gitmodules_config_oid(&real_obj->oid); - obj_read_unlock(); - } - if (grep_object(opt, pathspec, real_obj, list->objects[i].name, - list->objects[i].path)) { - hit = 1; - if (opt->status_only) - break; - } - } - return hit; -} - -static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec, - int exc_std, int use_index) -{ - struct dir_struct dir; - int i, hit = 0; - - dir_init(&dir); - if (!use_index) - dir.flags |= DIR_NO_GITLINKS; - if (exc_std) - setup_standard_excludes(&dir); - - fill_directory(&dir, opt->repo->index, pathspec); - for (i = 0; i < dir.nr; i++) { - hit |= grep_file(opt, dir.entries[i]->name); - if (hit && opt->status_only) - break; - } - dir_clear(&dir); - return hit; -} - -static int context_callback(const struct option *opt, const char *arg, - int unset) -{ - struct grep_opt *grep_opt = opt->value; - int value; - const char *endp; - - if (unset) { - grep_opt->pre_context = grep_opt->post_context = 0; - return 0; - } - value = strtol(arg, (char **)&endp, 10); - if (*endp) { - return error(_("switch `%c' expects a numerical value"), - opt->short_name); - } - grep_opt->pre_context = grep_opt->post_context = value; - return 0; -} - -static int file_callback(const struct option *opt, const char *arg, int unset) -{ - struct grep_opt *grep_opt = opt->value; - int from_stdin; - FILE *patterns; - int lno = 0; - struct strbuf sb = STRBUF_INIT; - - BUG_ON_OPT_NEG(unset); - - from_stdin = !strcmp(arg, "-"); - patterns = from_stdin ? stdin : fopen(arg, "r"); - if (!patterns) - die_errno(_("cannot open '%s'"), arg); - while (strbuf_getline(&sb, patterns) == 0) { - /* ignore empty line like grep does */ - if (sb.len == 0) - continue; - - append_grep_pat(grep_opt, sb.buf, sb.len, arg, ++lno, - GREP_PATTERN); - } - if (!from_stdin) - fclose(patterns); - strbuf_release(&sb); - return 0; -} - -static int not_callback(const struct option *opt, const char *arg, int unset) -{ - struct grep_opt *grep_opt = opt->value; - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - append_grep_pattern(grep_opt, "--not", "command line", 0, GREP_NOT); - return 0; -} - -static int and_callback(const struct option *opt, const char *arg, int unset) -{ - struct grep_opt *grep_opt = opt->value; - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - append_grep_pattern(grep_opt, "--and", "command line", 0, GREP_AND); - return 0; -} - -static int open_callback(const struct option *opt, const char *arg, int unset) -{ - struct grep_opt *grep_opt = opt->value; - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - append_grep_pattern(grep_opt, "(", "command line", 0, GREP_OPEN_PAREN); - return 0; -} - -static int close_callback(const struct option *opt, const char *arg, int unset) -{ - struct grep_opt *grep_opt = opt->value; - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - append_grep_pattern(grep_opt, ")", "command line", 0, GREP_CLOSE_PAREN); - return 0; -} - -static int pattern_callback(const struct option *opt, const char *arg, - int unset) -{ - struct grep_opt *grep_opt = opt->value; - BUG_ON_OPT_NEG(unset); - append_grep_pattern(grep_opt, arg, "-e option", 0, GREP_PATTERN); - return 0; -} - -int cmd_grep(int argc, const char **argv, const char *prefix) -{ - int hit = 0; - int cached = 0, untracked = 0, opt_exclude = -1; - int seen_dashdash = 0; - int external_grep_allowed__ignored; - const char *show_in_pager = NULL, *default_pager = "dummy"; - struct grep_opt opt; - struct object_array list = OBJECT_ARRAY_INIT; - struct pathspec pathspec; - struct string_list path_list = STRING_LIST_INIT_NODUP; - int i; - int dummy; - int use_index = 1; - int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED; - int allow_revs; - - struct option options[] = { - OPT_BOOL(0, "cached", &cached, - N_("search in index instead of in the work tree")), - OPT_NEGBIT(0, "no-index", &use_index, - N_("find in contents not managed by git"), 1), - OPT_BOOL(0, "untracked", &untracked, - N_("search in both tracked and untracked files")), - OPT_SET_INT(0, "exclude-standard", &opt_exclude, - N_("ignore files specified via '.gitignore'"), 1), - OPT_BOOL(0, "recurse-submodules", &recurse_submodules, - N_("recursively search in each submodule")), - OPT_GROUP(""), - OPT_BOOL('v', "invert-match", &opt.invert, - N_("show non-matching lines")), - OPT_BOOL('i', "ignore-case", &opt.ignore_case, - N_("case insensitive matching")), - OPT_BOOL('w', "word-regexp", &opt.word_regexp, - N_("match patterns only at word boundaries")), - OPT_SET_INT('a', "text", &opt.binary, - N_("process binary files as text"), GREP_BINARY_TEXT), - OPT_SET_INT('I', NULL, &opt.binary, - N_("don't match patterns in binary files"), - GREP_BINARY_NOMATCH), - OPT_BOOL(0, "textconv", &opt.allow_textconv, - N_("process binary files with textconv filters")), - OPT_SET_INT('r', "recursive", &opt.max_depth, - N_("search in subdirectories (default)"), -1), - { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"), - N_("descend at most <depth> levels"), PARSE_OPT_NONEG, - NULL, 1 }, - OPT_GROUP(""), - OPT_SET_INT('E', "extended-regexp", &pattern_type_arg, - N_("use extended POSIX regular expressions"), - GREP_PATTERN_TYPE_ERE), - OPT_SET_INT('G', "basic-regexp", &pattern_type_arg, - N_("use basic POSIX regular expressions (default)"), - GREP_PATTERN_TYPE_BRE), - OPT_SET_INT('F', "fixed-strings", &pattern_type_arg, - N_("interpret patterns as fixed strings"), - GREP_PATTERN_TYPE_FIXED), - OPT_SET_INT('P', "perl-regexp", &pattern_type_arg, - N_("use Perl-compatible regular expressions"), - GREP_PATTERN_TYPE_PCRE), - OPT_GROUP(""), - OPT_BOOL('n', "line-number", &opt.linenum, N_("show line numbers")), - OPT_BOOL(0, "column", &opt.columnnum, N_("show column number of first match")), - OPT_NEGBIT('h', NULL, &opt.pathname, N_("don't show filenames"), 1), - OPT_BIT('H', NULL, &opt.pathname, N_("show filenames"), 1), - OPT_NEGBIT(0, "full-name", &opt.relative, - N_("show filenames relative to top directory"), 1), - OPT_BOOL('l', "files-with-matches", &opt.name_only, - N_("show only filenames instead of matching lines")), - OPT_BOOL(0, "name-only", &opt.name_only, - N_("synonym for --files-with-matches")), - OPT_BOOL('L', "files-without-match", - &opt.unmatch_name_only, - N_("show only the names of files without match")), - OPT_BOOL_F('z', "null", &opt.null_following_name, - N_("print NUL after filenames"), - PARSE_OPT_NOCOMPLETE), - OPT_BOOL('o', "only-matching", &opt.only_matching, - N_("show only matching parts of a line")), - OPT_BOOL('c', "count", &opt.count, - N_("show the number of matches instead of matching lines")), - OPT__COLOR(&opt.color, N_("highlight matches")), - OPT_BOOL(0, "break", &opt.file_break, - N_("print empty line between matches from different files")), - OPT_BOOL(0, "heading", &opt.heading, - N_("show filename only once above matches from same file")), - OPT_GROUP(""), - OPT_CALLBACK('C', "context", &opt, N_("n"), - N_("show <n> context lines before and after matches"), - context_callback), - OPT_INTEGER('B', "before-context", &opt.pre_context, - N_("show <n> context lines before matches")), - OPT_INTEGER('A', "after-context", &opt.post_context, - N_("show <n> context lines after matches")), - OPT_INTEGER(0, "threads", &num_threads, - N_("use <n> worker threads")), - OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"), - context_callback), - OPT_BOOL('p', "show-function", &opt.funcname, - N_("show a line with the function name before matches")), - OPT_BOOL('W', "function-context", &opt.funcbody, - N_("show the surrounding function")), - OPT_GROUP(""), - OPT_CALLBACK('f', NULL, &opt, N_("file"), - N_("read patterns from file"), file_callback), - OPT_CALLBACK_F('e', NULL, &opt, N_("pattern"), - N_("match <pattern>"), PARSE_OPT_NONEG, pattern_callback), - OPT_CALLBACK_F(0, "and", &opt, NULL, - N_("combine patterns specified with -e"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback), - OPT_BOOL(0, "or", &dummy, ""), - OPT_CALLBACK_F(0, "not", &opt, NULL, "", - PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback), - OPT_CALLBACK_F('(', NULL, &opt, NULL, "", - PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, - open_callback), - OPT_CALLBACK_F(')', NULL, &opt, NULL, "", - PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, - close_callback), - OPT__QUIET(&opt.status_only, - N_("indicate hit with exit status without output")), - OPT_BOOL(0, "all-match", &opt.all_match, - N_("show only matches from files that match all patterns")), - OPT_SET_INT_F(0, "debug", &opt.debug, - N_("show parse tree for grep expression"), - 1, PARSE_OPT_HIDDEN), - OPT_GROUP(""), - { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager, - N_("pager"), N_("show matching files in the pager"), - PARSE_OPT_OPTARG | PARSE_OPT_NOCOMPLETE, - NULL, (intptr_t)default_pager }, - OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored, - N_("allow calling of grep(1) (ignored by this build)"), - PARSE_OPT_NOCOMPLETE), - OPT_END() - }; - - init_grep_defaults(the_repository); - git_config(grep_cmd_config, NULL); - grep_init(&opt, the_repository, prefix); - - /* - * If there is no -- then the paths must exist in the working - * tree. If there is no explicit pattern specified with -e or - * -f, we take the first unrecognized non option to be the - * pattern, but then what follows it must be zero or more - * valid refs up to the -- (if exists), and then existing - * paths. If there is an explicit pattern, then the first - * unrecognized non option is the beginning of the refs list - * that continues up to the -- (if exists), and then paths. - */ - argc = parse_options(argc, argv, prefix, options, grep_usage, - PARSE_OPT_KEEP_DASHDASH | - PARSE_OPT_STOP_AT_NON_OPTION); - grep_commit_pattern_type(pattern_type_arg, &opt); - - if (use_index && !startup_info->have_repository) { - int fallback = 0; - git_config_get_bool("grep.fallbacktonoindex", &fallback); - if (fallback) - use_index = 0; - else - /* die the same way as if we did it at the beginning */ - setup_git_directory(); - } - /* Ignore --recurse-submodules if --no-index is given or implied */ - if (!use_index) - recurse_submodules = 0; - - /* - * skip a -- separator; we know it cannot be - * separating revisions from pathnames if - * we haven't even had any patterns yet - */ - if (argc > 0 && !opt.pattern_list && !strcmp(argv[0], "--")) { - argv++; - argc--; - } - - /* First unrecognized non-option token */ - if (argc > 0 && !opt.pattern_list) { - append_grep_pattern(&opt, argv[0], "command line", 0, - GREP_PATTERN); - argv++; - argc--; - } - - if (show_in_pager == default_pager) - show_in_pager = git_pager(1); - if (show_in_pager) { - opt.color = 0; - opt.name_only = 1; - opt.null_following_name = 1; - opt.output_priv = &path_list; - opt.output = append_path; - string_list_append(&path_list, show_in_pager); - } - - if (!opt.pattern_list) - die(_("no pattern given")); - - /* --only-matching has no effect with --invert. */ - if (opt.invert) - opt.only_matching = 0; - - /* - * We have to find "--" in a separate pass, because its presence - * influences how we will parse arguments that come before it. - */ - for (i = 0; i < argc; i++) { - if (!strcmp(argv[i], "--")) { - seen_dashdash = 1; - break; - } - } - - /* - * Resolve any rev arguments. If we have a dashdash, then everything up - * to it must resolve as a rev. If not, then we stop at the first - * non-rev and assume everything else is a path. - */ - allow_revs = use_index && !untracked; - for (i = 0; i < argc; i++) { - const char *arg = argv[i]; - struct object_id oid; - struct object_context oc; - struct object *object; - - if (!strcmp(arg, "--")) { - i++; - break; - } - - if (!allow_revs) { - if (seen_dashdash) - die(_("--no-index or --untracked cannot be used with revs")); - break; - } - - if (get_oid_with_context(the_repository, arg, - GET_OID_RECORD_PATH, - &oid, &oc)) { - if (seen_dashdash) - die(_("unable to resolve revision: %s"), arg); - break; - } - - object = parse_object_or_die(&oid, arg); - if (!seen_dashdash) - verify_non_filename(prefix, arg); - add_object_array_with_path(object, arg, &list, oc.mode, oc.path); - free(oc.path); - } - - /* - * Anything left over is presumed to be a path. But in the non-dashdash - * "do what I mean" case, we verify and complain when that isn't true. - */ - if (!seen_dashdash) { - int j; - for (j = i; j < argc; j++) - verify_filename(prefix, argv[j], j == i && allow_revs); - } - - parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_CWD | - (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0), - prefix, argv + i); - pathspec.max_depth = opt.max_depth; - pathspec.recursive = 1; - pathspec.recurse_submodules = !!recurse_submodules; - - if (recurse_submodules && untracked) - die(_("--untracked not supported with --recurse-submodules")); - - if (show_in_pager) { - if (num_threads > 1) - warning(_("invalid option combination, ignoring --threads")); - num_threads = 1; - } else if (!HAVE_THREADS && num_threads > 1) { - warning(_("no threads support, ignoring --threads")); - num_threads = 1; - } else if (num_threads < 0) - die(_("invalid number of threads specified (%d)"), num_threads); - else if (num_threads == 0) - num_threads = HAVE_THREADS ? online_cpus() : 1; - - if (num_threads > 1) { - if (!HAVE_THREADS) - BUG("Somebody got num_threads calculation wrong!"); - if (!(opt.name_only || opt.unmatch_name_only || opt.count) - && (opt.pre_context || opt.post_context || - opt.file_break || opt.funcbody)) - skip_first_line = 1; - - /* - * Pre-read gitmodules (if not read already) and force eager - * initialization of packed_git to prevent racy lazy - * reading/initialization once worker threads are started. - */ - if (recurse_submodules) - repo_read_gitmodules(the_repository, 1); - if (startup_info->have_repository) - (void)get_packed_git(the_repository); - - start_threads(&opt); - } else { - /* - * The compiled patterns on the main path are only - * used when not using threading. Otherwise - * start_threads() above calls compile_grep_patterns() - * for each thread. - */ - compile_grep_patterns(&opt); - } - - if (show_in_pager && (cached || list.nr)) - die(_("--open-files-in-pager only works on the worktree")); - - if (show_in_pager && opt.pattern_list && !opt.pattern_list->next) { - const char *pager = path_list.items[0].string; - int len = strlen(pager); - - if (len > 4 && is_dir_sep(pager[len - 5])) - pager += len - 4; - - if (opt.ignore_case && !strcmp("less", pager)) - string_list_append(&path_list, "-I"); - - if (!strcmp("less", pager) || !strcmp("vi", pager)) { - struct strbuf buf = STRBUF_INIT; - strbuf_addf(&buf, "+/%s%s", - strcmp("less", pager) ? "" : "*", - opt.pattern_list->pattern); - string_list_append(&path_list, - strbuf_detach(&buf, NULL)); - } - } - - if (!show_in_pager && !opt.status_only) - setup_pager(); - - if (!use_index && (untracked || cached)) - die(_("--cached or --untracked cannot be used with --no-index")); - - if (!use_index || untracked) { - int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude; - hit = grep_directory(&opt, &pathspec, use_exclude, use_index); - } else if (0 <= opt_exclude) { - die(_("--[no-]exclude-standard cannot be used for tracked contents")); - } else if (!list.nr) { - if (!cached) - setup_work_tree(); - - hit = grep_cache(&opt, &pathspec, cached); - } else { - if (cached) - die(_("both --cached and trees are given")); - - hit = grep_objects(&opt, &pathspec, &list); - } - - if (num_threads > 1) - hit |= wait_all(); - if (hit && show_in_pager) - run_pager(&opt, prefix); - clear_pathspec(&pathspec); - free_grep_patterns(&opt); - grep_destroy(); - return !hit; -} diff --git a/third_party/git/builtin/hash-object.c b/third_party/git/builtin/hash-object.c deleted file mode 100644 index 640ef4ded595..000000000000 --- a/third_party/git/builtin/hash-object.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * GIT - The information manager from hell - * - * Copyright (C) Linus Torvalds, 2005 - * Copyright (C) Junio C Hamano, 2005 - */ -#include "builtin.h" -#include "config.h" -#include "object-store.h" -#include "blob.h" -#include "quote.h" -#include "parse-options.h" -#include "exec-cmd.h" - -/* - * This is to create corrupt objects for debugging and as such it - * needs to bypass the data conversion performed by, and the type - * limitation imposed by, index_fd() and its callees. - */ -static int hash_literally(struct object_id *oid, int fd, const char *type, unsigned flags) -{ - struct strbuf buf = STRBUF_INIT; - int ret; - - if (strbuf_read(&buf, fd, 4096) < 0) - ret = -1; - else - ret = hash_object_file_literally(buf.buf, buf.len, type, oid, - flags); - strbuf_release(&buf); - return ret; -} - -static void hash_fd(int fd, const char *type, const char *path, unsigned flags, - int literally) -{ - struct stat st; - struct object_id oid; - - if (fstat(fd, &st) < 0 || - (literally - ? hash_literally(&oid, fd, type, flags) - : index_fd(the_repository->index, &oid, fd, &st, - type_from_string(type), path, flags))) - die((flags & HASH_WRITE_OBJECT) - ? "Unable to add %s to database" - : "Unable to hash %s", path); - printf("%s\n", oid_to_hex(&oid)); - maybe_flush_or_die(stdout, "hash to stdout"); -} - -static void hash_object(const char *path, const char *type, const char *vpath, - unsigned flags, int literally) -{ - int fd; - fd = open(path, O_RDONLY); - if (fd < 0) - die_errno("Cannot open '%s'", path); - hash_fd(fd, type, vpath, flags, literally); -} - -static void hash_stdin_paths(const char *type, int no_filters, unsigned flags, - int literally) -{ - struct strbuf buf = STRBUF_INIT; - struct strbuf unquoted = STRBUF_INIT; - - while (strbuf_getline(&buf, stdin) != EOF) { - if (buf.buf[0] == '"') { - strbuf_reset(&unquoted); - if (unquote_c_style(&unquoted, buf.buf, NULL)) - die("line is badly quoted"); - strbuf_swap(&buf, &unquoted); - } - hash_object(buf.buf, type, no_filters ? NULL : buf.buf, flags, - literally); - } - strbuf_release(&buf); - strbuf_release(&unquoted); -} - -int cmd_hash_object(int argc, const char **argv, const char *prefix) -{ - static const char * const hash_object_usage[] = { - N_("git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>..."), - N_("git hash-object --stdin-paths"), - NULL - }; - const char *type = blob_type; - int hashstdin = 0; - int stdin_paths = 0; - int no_filters = 0; - int literally = 0; - int nongit = 0; - unsigned flags = HASH_FORMAT_CHECK; - const char *vpath = NULL; - const struct option hash_object_options[] = { - OPT_STRING('t', NULL, &type, N_("type"), N_("object type")), - OPT_BIT('w', NULL, &flags, N_("write the object into the object database"), - HASH_WRITE_OBJECT), - OPT_COUNTUP( 0 , "stdin", &hashstdin, N_("read the object from stdin")), - OPT_BOOL( 0 , "stdin-paths", &stdin_paths, N_("read file names from stdin")), - OPT_BOOL( 0 , "no-filters", &no_filters, N_("store file as is without filters")), - OPT_BOOL( 0, "literally", &literally, N_("just hash any random garbage to create corrupt objects for debugging Git")), - OPT_STRING( 0 , "path", &vpath, N_("file"), N_("process file as it were from this path")), - OPT_END() - }; - int i; - const char *errstr = NULL; - - argc = parse_options(argc, argv, prefix, hash_object_options, - hash_object_usage, 0); - - if (flags & HASH_WRITE_OBJECT) - prefix = setup_git_directory(); - else - prefix = setup_git_directory_gently(&nongit); - - if (vpath && prefix) - vpath = xstrdup(prefix_filename(prefix, vpath)); - - git_config(git_default_config, NULL); - - if (stdin_paths) { - if (hashstdin) - errstr = "Can't use --stdin-paths with --stdin"; - else if (argc) - errstr = "Can't specify files with --stdin-paths"; - else if (vpath) - errstr = "Can't use --stdin-paths with --path"; - } - else { - if (hashstdin > 1) - errstr = "Multiple --stdin arguments are not supported"; - if (vpath && no_filters) - errstr = "Can't use --path with --no-filters"; - } - - if (errstr) { - error("%s", errstr); - usage_with_options(hash_object_usage, hash_object_options); - } - - if (hashstdin) - hash_fd(0, type, vpath, flags, literally); - - for (i = 0 ; i < argc; i++) { - const char *arg = argv[i]; - char *to_free = NULL; - - if (prefix) - arg = to_free = prefix_filename(prefix, arg); - hash_object(arg, type, no_filters ? NULL : vpath ? vpath : arg, - flags, literally); - free(to_free); - } - - if (stdin_paths) - hash_stdin_paths(type, no_filters, flags, literally); - - return 0; -} diff --git a/third_party/git/builtin/help.c b/third_party/git/builtin/help.c deleted file mode 100644 index bb339f0fc802..000000000000 --- a/third_party/git/builtin/help.c +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Builtin help command - */ -#include "cache.h" -#include "config.h" -#include "builtin.h" -#include "exec-cmd.h" -#include "parse-options.h" -#include "run-command.h" -#include "column.h" -#include "config-list.h" -#include "help.h" -#include "alias.h" - -#ifndef DEFAULT_HELP_FORMAT -#define DEFAULT_HELP_FORMAT "man" -#endif - -static struct man_viewer_list { - struct man_viewer_list *next; - char name[FLEX_ARRAY]; -} *man_viewer_list; - -static struct man_viewer_info_list { - struct man_viewer_info_list *next; - const char *info; - char name[FLEX_ARRAY]; -} *man_viewer_info_list; - -enum help_format { - HELP_FORMAT_NONE, - HELP_FORMAT_MAN, - HELP_FORMAT_INFO, - HELP_FORMAT_WEB -}; - -static const char *html_path; - -static int show_all = 0; -static int show_guides = 0; -static int show_config; -static int verbose = 1; -static unsigned int colopts; -static enum help_format help_format = HELP_FORMAT_NONE; -static int exclude_guides; -static struct option builtin_help_options[] = { - OPT_BOOL('a', "all", &show_all, N_("print all available commands")), - OPT_HIDDEN_BOOL(0, "exclude-guides", &exclude_guides, N_("exclude guides")), - OPT_BOOL('g', "guides", &show_guides, N_("print list of useful guides")), - OPT_BOOL('c', "config", &show_config, N_("print all configuration variable names")), - OPT_SET_INT_F(0, "config-for-completion", &show_config, "", 2, PARSE_OPT_HIDDEN), - OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN), - OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"), - HELP_FORMAT_WEB), - OPT_SET_INT('i', "info", &help_format, N_("show info page"), - HELP_FORMAT_INFO), - OPT__VERBOSE(&verbose, N_("print command description")), - OPT_END(), -}; - -static const char * const builtin_help_usage[] = { - N_("git help [--all] [--guides] [--man | --web | --info] [<command>]"), - NULL -}; - -struct slot_expansion { - const char *prefix; - const char *placeholder; - void (*fn)(struct string_list *list, const char *prefix); - int found; -}; - -static void list_config_help(int for_human) -{ - struct slot_expansion slot_expansions[] = { - { "advice", "*", list_config_advices }, - { "color.branch", "<slot>", list_config_color_branch_slots }, - { "color.decorate", "<slot>", list_config_color_decorate_slots }, - { "color.diff", "<slot>", list_config_color_diff_slots }, - { "color.grep", "<slot>", list_config_color_grep_slots }, - { "color.interactive", "<slot>", list_config_color_interactive_slots }, - { "color.remote", "<slot>", list_config_color_sideband_slots }, - { "color.status", "<slot>", list_config_color_status_slots }, - { "fsck", "<msg-id>", list_config_fsck_msg_ids }, - { "receive.fsck", "<msg-id>", list_config_fsck_msg_ids }, - { NULL, NULL, NULL } - }; - const char **p; - struct slot_expansion *e; - struct string_list keys = STRING_LIST_INIT_DUP; - int i; - - for (p = config_name_list; *p; p++) { - const char *var = *p; - struct strbuf sb = STRBUF_INIT; - - for (e = slot_expansions; e->prefix; e++) { - - strbuf_reset(&sb); - strbuf_addf(&sb, "%s.%s", e->prefix, e->placeholder); - if (!strcasecmp(var, sb.buf)) { - e->fn(&keys, e->prefix); - e->found++; - break; - } - } - strbuf_release(&sb); - if (!e->prefix) - string_list_append(&keys, var); - } - - for (e = slot_expansions; e->prefix; e++) - if (!e->found) - BUG("slot_expansion %s.%s is not used", - e->prefix, e->placeholder); - - string_list_sort(&keys); - for (i = 0; i < keys.nr; i++) { - const char *var = keys.items[i].string; - const char *wildcard, *tag, *cut; - - if (for_human) { - puts(var); - continue; - } - - wildcard = strchr(var, '*'); - tag = strchr(var, '<'); - - if (!wildcard && !tag) { - puts(var); - continue; - } - - if (wildcard && !tag) - cut = wildcard; - else if (!wildcard && tag) - cut = tag; - else - cut = wildcard < tag ? wildcard : tag; - - /* - * We may produce duplicates, but that's up to - * git-completion.bash to handle - */ - printf("%.*s\n", (int)(cut - var), var); - } - string_list_clear(&keys, 0); -} - -static enum help_format parse_help_format(const char *format) -{ - if (!strcmp(format, "man")) - return HELP_FORMAT_MAN; - if (!strcmp(format, "info")) - return HELP_FORMAT_INFO; - if (!strcmp(format, "web") || !strcmp(format, "html")) - return HELP_FORMAT_WEB; - /* - * Please update _git_config() in git-completion.bash when you - * add new help formats. - */ - die(_("unrecognized help format '%s'"), format); -} - -static const char *get_man_viewer_info(const char *name) -{ - struct man_viewer_info_list *viewer; - - for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) - { - if (!strcasecmp(name, viewer->name)) - return viewer->info; - } - return NULL; -} - -static int check_emacsclient_version(void) -{ - struct strbuf buffer = STRBUF_INIT; - struct child_process ec_process = CHILD_PROCESS_INIT; - const char *argv_ec[] = { "emacsclient", "--version", NULL }; - int version; - - /* emacsclient prints its version number on stderr */ - ec_process.argv = argv_ec; - ec_process.err = -1; - ec_process.stdout_to_stderr = 1; - if (start_command(&ec_process)) - return error(_("Failed to start emacsclient.")); - - strbuf_read(&buffer, ec_process.err, 20); - close(ec_process.err); - - /* - * Don't bother checking return value, because "emacsclient --version" - * seems to always exits with code 1. - */ - finish_command(&ec_process); - - if (!starts_with(buffer.buf, "emacsclient")) { - strbuf_release(&buffer); - return error(_("Failed to parse emacsclient version.")); - } - - strbuf_remove(&buffer, 0, strlen("emacsclient")); - version = atoi(buffer.buf); - - if (version < 22) { - strbuf_release(&buffer); - return error(_("emacsclient version '%d' too old (< 22)."), - version); - } - - strbuf_release(&buffer); - return 0; -} - -static void exec_woman_emacs(const char *path, const char *page) -{ - if (!check_emacsclient_version()) { - /* This works only with emacsclient version >= 22. */ - struct strbuf man_page = STRBUF_INIT; - - if (!path) - path = "emacsclient"; - strbuf_addf(&man_page, "(woman \"%s\")", page); - execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL); - warning_errno(_("failed to exec '%s'"), path); - strbuf_release(&man_page); - } -} - -static void exec_man_konqueror(const char *path, const char *page) -{ - const char *display = getenv("DISPLAY"); - if (display && *display) { - struct strbuf man_page = STRBUF_INIT; - const char *filename = "kfmclient"; - - /* It's simpler to launch konqueror using kfmclient. */ - if (path) { - size_t len; - if (strip_suffix(path, "/konqueror", &len)) - path = xstrfmt("%.*s/kfmclient", (int)len, path); - filename = basename((char *)path); - } else - path = "kfmclient"; - strbuf_addf(&man_page, "man:%s(1)", page); - execlp(path, filename, "newTab", man_page.buf, (char *)NULL); - warning_errno(_("failed to exec '%s'"), path); - strbuf_release(&man_page); - } -} - -static void exec_man_man(const char *path, const char *page) -{ - if (!path) - path = "man"; - execlp(path, "man", page, (char *)NULL); - warning_errno(_("failed to exec '%s'"), path); -} - -static void exec_man_cmd(const char *cmd, const char *page) -{ - struct strbuf shell_cmd = STRBUF_INIT; - strbuf_addf(&shell_cmd, "%s %s", cmd, page); - execl(SHELL_PATH, SHELL_PATH, "-c", shell_cmd.buf, (char *)NULL); - warning(_("failed to exec '%s'"), cmd); - strbuf_release(&shell_cmd); -} - -static void add_man_viewer(const char *name) -{ - struct man_viewer_list **p = &man_viewer_list; - - while (*p) - p = &((*p)->next); - FLEX_ALLOC_STR(*p, name, name); -} - -static int supported_man_viewer(const char *name, size_t len) -{ - return (!strncasecmp("man", name, len) || - !strncasecmp("woman", name, len) || - !strncasecmp("konqueror", name, len)); -} - -static void do_add_man_viewer_info(const char *name, - size_t len, - const char *value) -{ - struct man_viewer_info_list *new_man_viewer; - FLEX_ALLOC_MEM(new_man_viewer, name, name, len); - new_man_viewer->info = xstrdup(value); - new_man_viewer->next = man_viewer_info_list; - man_viewer_info_list = new_man_viewer; -} - -static int add_man_viewer_path(const char *name, - size_t len, - const char *value) -{ - if (supported_man_viewer(name, len)) - do_add_man_viewer_info(name, len, value); - else - warning(_("'%s': path for unsupported man viewer.\n" - "Please consider using 'man.<tool>.cmd' instead."), - name); - - return 0; -} - -static int add_man_viewer_cmd(const char *name, - size_t len, - const char *value) -{ - if (supported_man_viewer(name, len)) - warning(_("'%s': cmd for supported man viewer.\n" - "Please consider using 'man.<tool>.path' instead."), - name); - else - do_add_man_viewer_info(name, len, value); - - return 0; -} - -static int add_man_viewer_info(const char *var, const char *value) -{ - const char *name, *subkey; - size_t namelen; - - if (parse_config_key(var, "man", &name, &namelen, &subkey) < 0 || !name) - return 0; - - if (!strcmp(subkey, "path")) { - if (!value) - return config_error_nonbool(var); - return add_man_viewer_path(name, namelen, value); - } - if (!strcmp(subkey, "cmd")) { - if (!value) - return config_error_nonbool(var); - return add_man_viewer_cmd(name, namelen, value); - } - - return 0; -} - -static int git_help_config(const char *var, const char *value, void *cb) -{ - if (starts_with(var, "column.")) - return git_column_config(var, value, "help", &colopts); - if (!strcmp(var, "help.format")) { - if (!value) - return config_error_nonbool(var); - help_format = parse_help_format(value); - return 0; - } - if (!strcmp(var, "help.htmlpath")) { - if (!value) - return config_error_nonbool(var); - html_path = xstrdup(value); - return 0; - } - if (!strcmp(var, "man.viewer")) { - if (!value) - return config_error_nonbool(var); - add_man_viewer(value); - return 0; - } - if (starts_with(var, "man.")) - return add_man_viewer_info(var, value); - - return git_default_config(var, value, cb); -} - -static struct cmdnames main_cmds, other_cmds; - -static int is_git_command(const char *s) -{ - if (is_builtin(s)) - return 1; - - load_command_list("git-", &main_cmds, &other_cmds); - return is_in_cmdlist(&main_cmds, s) || - is_in_cmdlist(&other_cmds, s); -} - -static const char *cmd_to_page(const char *git_cmd) -{ - if (!git_cmd) - return "git"; - else if (starts_with(git_cmd, "git")) - return git_cmd; - else if (is_git_command(git_cmd)) - return xstrfmt("git-%s", git_cmd); - else - return xstrfmt("git%s", git_cmd); -} - -static void setup_man_path(void) -{ - struct strbuf new_path = STRBUF_INIT; - const char *old_path = getenv("MANPATH"); - char *git_man_path = system_path(GIT_MAN_PATH); - - /* We should always put ':' after our path. If there is no - * old_path, the ':' at the end will let 'man' to try - * system-wide paths after ours to find the manual page. If - * there is old_path, we need ':' as delimiter. */ - strbuf_addstr(&new_path, git_man_path); - strbuf_addch(&new_path, ':'); - if (old_path) - strbuf_addstr(&new_path, old_path); - - free(git_man_path); - setenv("MANPATH", new_path.buf, 1); - - strbuf_release(&new_path); -} - -static void exec_viewer(const char *name, const char *page) -{ - const char *info = get_man_viewer_info(name); - - if (!strcasecmp(name, "man")) - exec_man_man(info, page); - else if (!strcasecmp(name, "woman")) - exec_woman_emacs(info, page); - else if (!strcasecmp(name, "konqueror")) - exec_man_konqueror(info, page); - else if (info) - exec_man_cmd(info, page); - else - warning(_("'%s': unknown man viewer."), name); -} - -static void show_man_page(const char *git_cmd) -{ - struct man_viewer_list *viewer; - const char *page = cmd_to_page(git_cmd); - const char *fallback = getenv("GIT_MAN_VIEWER"); - - setup_man_path(); - for (viewer = man_viewer_list; viewer; viewer = viewer->next) - { - exec_viewer(viewer->name, page); /* will return when unable */ - } - if (fallback) - exec_viewer(fallback, page); - exec_viewer("man", page); - die(_("no man viewer handled the request")); -} - -static void show_info_page(const char *git_cmd) -{ - const char *page = cmd_to_page(git_cmd); - setenv("INFOPATH", system_path(GIT_INFO_PATH), 1); - execlp("info", "info", "gitman", page, (char *)NULL); - die(_("no info viewer handled the request")); -} - -static void get_html_page_path(struct strbuf *page_path, const char *page) -{ - struct stat st; - char *to_free = NULL; - - if (!html_path) - html_path = to_free = system_path(GIT_HTML_PATH); - - /* Check that we have a git documentation directory. */ - if (!strstr(html_path, "://")) { - if (stat(mkpath("%s/git.html", html_path), &st) - || !S_ISREG(st.st_mode)) - die("'%s': not a documentation directory.", html_path); - } - - strbuf_init(page_path, 0); - strbuf_addf(page_path, "%s/%s.html", html_path, page); - free(to_free); -} - -static void open_html(const char *path) -{ - execl_git_cmd("web--browse", "-c", "help.browser", path, (char *)NULL); -} - -static void show_html_page(const char *git_cmd) -{ - const char *page = cmd_to_page(git_cmd); - struct strbuf page_path; /* it leaks but we exec bellow */ - - get_html_page_path(&page_path, page); - - open_html(page_path.buf); -} - -static const char *check_git_cmd(const char* cmd) -{ - char *alias; - - if (is_git_command(cmd)) - return cmd; - - alias = alias_lookup(cmd); - if (alias) { - const char **argv; - int count; - - /* - * handle_builtin() in git.c rewrites "git cmd --help" - * to "git help --exclude-guides cmd", so we can use - * exclude_guides to distinguish "git cmd --help" from - * "git help cmd". In the latter case, or if cmd is an - * alias for a shell command, just print the alias - * definition. - */ - if (!exclude_guides || alias[0] == '!') { - printf_ln(_("'%s' is aliased to '%s'"), cmd, alias); - free(alias); - exit(0); - } - /* - * Otherwise, we pretend that the command was "git - * word0 --help". We use split_cmdline() to get the - * first word of the alias, to ensure that we use the - * same rules as when the alias is actually - * used. split_cmdline() modifies alias in-place. - */ - fprintf_ln(stderr, _("'%s' is aliased to '%s'"), cmd, alias); - count = split_cmdline(alias, &argv); - if (count < 0) - die(_("bad alias.%s string: %s"), cmd, - split_cmdline_strerror(count)); - free(argv); - UNLEAK(alias); - return alias; - } - - if (exclude_guides) - return help_unknown_cmd(cmd); - - return cmd; -} - -int cmd_help(int argc, const char **argv, const char *prefix) -{ - int nongit; - enum help_format parsed_help_format; - - argc = parse_options(argc, argv, prefix, builtin_help_options, - builtin_help_usage, 0); - parsed_help_format = help_format; - - if (show_all) { - git_config(git_help_config, NULL); - if (verbose) { - setup_pager(); - list_all_cmds_help(); - return 0; - } - printf(_("usage: %s%s"), _(git_usage_string), "\n\n"); - load_command_list("git-", &main_cmds, &other_cmds); - list_commands(colopts, &main_cmds, &other_cmds); - } - - if (show_config) { - int for_human = show_config == 1; - - if (!for_human) { - list_config_help(for_human); - return 0; - } - setup_pager(); - list_config_help(for_human); - printf("\n%s\n", _("'git help config' for more information")); - return 0; - } - - if (show_guides) - list_guides_help(); - - if (show_all || show_guides) { - printf("%s\n", _(git_more_info_string)); - /* - * We're done. Ignore any remaining args - */ - return 0; - } - - if (!argv[0]) { - printf(_("usage: %s%s"), _(git_usage_string), "\n\n"); - list_common_cmds_help(); - printf("\n%s\n", _(git_more_info_string)); - return 0; - } - - setup_git_directory_gently(&nongit); - git_config(git_help_config, NULL); - - if (parsed_help_format != HELP_FORMAT_NONE) - help_format = parsed_help_format; - if (help_format == HELP_FORMAT_NONE) - help_format = parse_help_format(DEFAULT_HELP_FORMAT); - - argv[0] = check_git_cmd(argv[0]); - - switch (help_format) { - case HELP_FORMAT_NONE: - case HELP_FORMAT_MAN: - show_man_page(argv[0]); - break; - case HELP_FORMAT_INFO: - show_info_page(argv[0]); - break; - case HELP_FORMAT_WEB: - show_html_page(argv[0]); - break; - } - - return 0; -} diff --git a/third_party/git/builtin/index-pack.c b/third_party/git/builtin/index-pack.c deleted file mode 100644 index 0d03cb442df3..000000000000 --- a/third_party/git/builtin/index-pack.c +++ /dev/null @@ -1,1908 +0,0 @@ -#include "builtin.h" -#include "config.h" -#include "delta.h" -#include "pack.h" -#include "csum-file.h" -#include "blob.h" -#include "commit.h" -#include "tag.h" -#include "tree.h" -#include "progress.h" -#include "fsck.h" -#include "exec-cmd.h" -#include "streaming.h" -#include "thread-utils.h" -#include "packfile.h" -#include "object-store.h" -#include "promisor-remote.h" - -static const char index_pack_usage[] = -"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])"; - -struct object_entry { - struct pack_idx_entry idx; - unsigned long size; - unsigned char hdr_size; - signed char type; - signed char real_type; -}; - -struct object_stat { - unsigned delta_depth; - int base_object_no; -}; - -struct base_data { - /* Initialized by make_base(). */ - struct base_data *base; - struct object_entry *obj; - int ref_first, ref_last; - int ofs_first, ofs_last; - /* - * Threads should increment retain_data if they are about to call - * patch_delta() using this struct's data as a base, and decrement this - * when they are done. While retain_data is nonzero, this struct's data - * will not be freed even if the delta base cache limit is exceeded. - */ - int retain_data; - /* - * The number of direct children that have not been fully processed - * (entered work_head, entered done_head, left done_head). When this - * number reaches zero, this struct base_data can be freed. - */ - int children_remaining; - - /* Not initialized by make_base(). */ - struct list_head list; - void *data; - unsigned long size; -}; - -/* - * Stack of struct base_data that have unprocessed children. - * threaded_second_pass() uses this as a source of work (the other being the - * objects array). - * - * Guarded by work_mutex. - */ -static LIST_HEAD(work_head); - -/* - * Stack of struct base_data that have children, all of whom have been - * processed or are being processed, and at least one child is being processed. - * These struct base_data must be kept around until the last child is - * processed. - * - * Guarded by work_mutex. - */ -static LIST_HEAD(done_head); - -/* - * All threads share one delta base cache. - * - * base_cache_used is guarded by work_mutex, and base_cache_limit is read-only - * in a thread. - */ -static size_t base_cache_used; -static size_t base_cache_limit; - -struct thread_local { - pthread_t thread; - int pack_fd; -}; - -/* Remember to update object flag allocation in object.h */ -#define FLAG_LINK (1u<<20) -#define FLAG_CHECKED (1u<<21) - -struct ofs_delta_entry { - off_t offset; - int obj_no; -}; - -struct ref_delta_entry { - struct object_id oid; - int obj_no; -}; - -static struct object_entry *objects; -static struct object_stat *obj_stat; -static struct ofs_delta_entry *ofs_deltas; -static struct ref_delta_entry *ref_deltas; -static struct thread_local nothread_data; -static int nr_objects; -static int nr_ofs_deltas; -static int nr_ref_deltas; -static int ref_deltas_alloc; -static int nr_resolved_deltas; -static int nr_threads; - -static int from_stdin; -static int strict; -static int do_fsck_object; -static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT; -static int verbose; -static int show_resolving_progress; -static int show_stat; -static int check_self_contained_and_connected; - -static struct progress *progress; - -/* We always read in 4kB chunks. */ -static unsigned char input_buffer[4096]; -static unsigned int input_offset, input_len; -static off_t consumed_bytes; -static off_t max_input_size; -static unsigned deepest_delta; -static git_hash_ctx input_ctx; -static uint32_t input_crc32; -static int input_fd, output_fd; -static const char *curr_pack; - -static struct thread_local *thread_data; -static int nr_dispatched; -static int threads_active; - -static pthread_mutex_t read_mutex; -#define read_lock() lock_mutex(&read_mutex) -#define read_unlock() unlock_mutex(&read_mutex) - -static pthread_mutex_t counter_mutex; -#define counter_lock() lock_mutex(&counter_mutex) -#define counter_unlock() unlock_mutex(&counter_mutex) - -static pthread_mutex_t work_mutex; -#define work_lock() lock_mutex(&work_mutex) -#define work_unlock() unlock_mutex(&work_mutex) - -static pthread_mutex_t deepest_delta_mutex; -#define deepest_delta_lock() lock_mutex(&deepest_delta_mutex) -#define deepest_delta_unlock() unlock_mutex(&deepest_delta_mutex) - -static pthread_key_t key; - -static inline void lock_mutex(pthread_mutex_t *mutex) -{ - if (threads_active) - pthread_mutex_lock(mutex); -} - -static inline void unlock_mutex(pthread_mutex_t *mutex) -{ - if (threads_active) - pthread_mutex_unlock(mutex); -} - -/* - * Mutex and conditional variable can't be statically-initialized on Windows. - */ -static void init_thread(void) -{ - int i; - init_recursive_mutex(&read_mutex); - pthread_mutex_init(&counter_mutex, NULL); - pthread_mutex_init(&work_mutex, NULL); - if (show_stat) - pthread_mutex_init(&deepest_delta_mutex, NULL); - pthread_key_create(&key, NULL); - thread_data = xcalloc(nr_threads, sizeof(*thread_data)); - for (i = 0; i < nr_threads; i++) { - thread_data[i].pack_fd = open(curr_pack, O_RDONLY); - if (thread_data[i].pack_fd == -1) - die_errno(_("unable to open %s"), curr_pack); - } - - threads_active = 1; -} - -static void cleanup_thread(void) -{ - int i; - if (!threads_active) - return; - threads_active = 0; - pthread_mutex_destroy(&read_mutex); - pthread_mutex_destroy(&counter_mutex); - pthread_mutex_destroy(&work_mutex); - if (show_stat) - pthread_mutex_destroy(&deepest_delta_mutex); - for (i = 0; i < nr_threads; i++) - close(thread_data[i].pack_fd); - pthread_key_delete(key); - free(thread_data); -} - -static int mark_link(struct object *obj, int type, void *data, struct fsck_options *options) -{ - if (!obj) - return -1; - - if (type != OBJ_ANY && obj->type != type) - die(_("object type mismatch at %s"), oid_to_hex(&obj->oid)); - - obj->flags |= FLAG_LINK; - return 0; -} - -/* The content of each linked object must have been checked - or it must be already present in the object database */ -static unsigned check_object(struct object *obj) -{ - if (!obj) - return 0; - - if (!(obj->flags & FLAG_LINK)) - return 0; - - if (!(obj->flags & FLAG_CHECKED)) { - unsigned long size; - int type = oid_object_info(the_repository, &obj->oid, &size); - if (type <= 0) - die(_("did not receive expected object %s"), - oid_to_hex(&obj->oid)); - if (type != obj->type) - die(_("object %s: expected type %s, found %s"), - oid_to_hex(&obj->oid), - type_name(obj->type), type_name(type)); - obj->flags |= FLAG_CHECKED; - return 1; - } - - return 0; -} - -static unsigned check_objects(void) -{ - unsigned i, max, foreign_nr = 0; - - max = get_max_object_index(); - - if (verbose) - progress = start_delayed_progress(_("Checking objects"), max); - - for (i = 0; i < max; i++) { - foreign_nr += check_object(get_indexed_object(i)); - display_progress(progress, i + 1); - } - - stop_progress(&progress); - return foreign_nr; -} - - -/* Discard current buffer used content. */ -static void flush(void) -{ - if (input_offset) { - if (output_fd >= 0) - write_or_die(output_fd, input_buffer, input_offset); - the_hash_algo->update_fn(&input_ctx, input_buffer, input_offset); - memmove(input_buffer, input_buffer + input_offset, input_len); - input_offset = 0; - } -} - -/* - * Make sure at least "min" bytes are available in the buffer, and - * return the pointer to the buffer. - */ -static void *fill(int min) -{ - if (min <= input_len) - return input_buffer + input_offset; - if (min > sizeof(input_buffer)) - die(Q_("cannot fill %d byte", - "cannot fill %d bytes", - min), - min); - flush(); - do { - ssize_t ret = xread(input_fd, input_buffer + input_len, - sizeof(input_buffer) - input_len); - if (ret <= 0) { - if (!ret) - die(_("early EOF")); - die_errno(_("read error on input")); - } - input_len += ret; - if (from_stdin) - display_throughput(progress, consumed_bytes + input_len); - } while (input_len < min); - return input_buffer; -} - -static void use(int bytes) -{ - if (bytes > input_len) - die(_("used more bytes than were available")); - input_crc32 = crc32(input_crc32, input_buffer + input_offset, bytes); - input_len -= bytes; - input_offset += bytes; - - /* make sure off_t is sufficiently large not to wrap */ - if (signed_add_overflows(consumed_bytes, bytes)) - die(_("pack too large for current definition of off_t")); - consumed_bytes += bytes; - if (max_input_size && consumed_bytes > max_input_size) - die(_("pack exceeds maximum allowed size")); -} - -static const char *open_pack_file(const char *pack_name) -{ - if (from_stdin) { - input_fd = 0; - if (!pack_name) { - struct strbuf tmp_file = STRBUF_INIT; - output_fd = odb_mkstemp(&tmp_file, - "pack/tmp_pack_XXXXXX"); - pack_name = strbuf_detach(&tmp_file, NULL); - } else { - output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600); - if (output_fd < 0) - die_errno(_("unable to create '%s'"), pack_name); - } - nothread_data.pack_fd = output_fd; - } else { - input_fd = open(pack_name, O_RDONLY); - if (input_fd < 0) - die_errno(_("cannot open packfile '%s'"), pack_name); - output_fd = -1; - nothread_data.pack_fd = input_fd; - } - the_hash_algo->init_fn(&input_ctx); - return pack_name; -} - -static void parse_pack_header(void) -{ - struct pack_header *hdr = fill(sizeof(struct pack_header)); - - /* Header consistency check */ - if (hdr->hdr_signature != htonl(PACK_SIGNATURE)) - die(_("pack signature mismatch")); - if (!pack_version_ok(hdr->hdr_version)) - die(_("pack version %"PRIu32" unsupported"), - ntohl(hdr->hdr_version)); - - nr_objects = ntohl(hdr->hdr_entries); - use(sizeof(struct pack_header)); -} - -static NORETURN void bad_object(off_t offset, const char *format, - ...) __attribute__((format (printf, 2, 3))); - -static NORETURN void bad_object(off_t offset, const char *format, ...) -{ - va_list params; - char buf[1024]; - - va_start(params, format); - vsnprintf(buf, sizeof(buf), format, params); - va_end(params); - die(_("pack has bad object at offset %"PRIuMAX": %s"), - (uintmax_t)offset, buf); -} - -static inline struct thread_local *get_thread_data(void) -{ - if (HAVE_THREADS) { - if (threads_active) - return pthread_getspecific(key); - assert(!threads_active && - "This should only be reached when all threads are gone"); - } - return ¬hread_data; -} - -static void set_thread_data(struct thread_local *data) -{ - if (threads_active) - pthread_setspecific(key, data); -} - -static void free_base_data(struct base_data *c) -{ - if (c->data) { - FREE_AND_NULL(c->data); - base_cache_used -= c->size; - } -} - -static void prune_base_data(struct base_data *retain) -{ - struct list_head *pos; - - if (base_cache_used <= base_cache_limit) - return; - - list_for_each_prev(pos, &done_head) { - struct base_data *b = list_entry(pos, struct base_data, list); - if (b->retain_data || b == retain) - continue; - if (b->data) { - free_base_data(b); - if (base_cache_used <= base_cache_limit) - return; - } - } - - list_for_each_prev(pos, &work_head) { - struct base_data *b = list_entry(pos, struct base_data, list); - if (b->retain_data || b == retain) - continue; - if (b->data) { - free_base_data(b); - if (base_cache_used <= base_cache_limit) - return; - } - } -} - -static int is_delta_type(enum object_type type) -{ - return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA); -} - -static void *unpack_entry_data(off_t offset, unsigned long size, - enum object_type type, struct object_id *oid) -{ - static char fixed_buf[8192]; - int status; - git_zstream stream; - void *buf; - git_hash_ctx c; - char hdr[32]; - int hdrlen; - - if (!is_delta_type(type)) { - hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %"PRIuMAX, - type_name(type),(uintmax_t)size) + 1; - the_hash_algo->init_fn(&c); - the_hash_algo->update_fn(&c, hdr, hdrlen); - } else - oid = NULL; - if (type == OBJ_BLOB && size > big_file_threshold) - buf = fixed_buf; - else - buf = xmallocz(size); - - memset(&stream, 0, sizeof(stream)); - git_inflate_init(&stream); - stream.next_out = buf; - stream.avail_out = buf == fixed_buf ? sizeof(fixed_buf) : size; - - do { - unsigned char *last_out = stream.next_out; - stream.next_in = fill(1); - stream.avail_in = input_len; - status = git_inflate(&stream, 0); - use(input_len - stream.avail_in); - if (oid) - the_hash_algo->update_fn(&c, last_out, stream.next_out - last_out); - if (buf == fixed_buf) { - stream.next_out = buf; - stream.avail_out = sizeof(fixed_buf); - } - } while (status == Z_OK); - if (stream.total_out != size || status != Z_STREAM_END) - bad_object(offset, _("inflate returned %d"), status); - git_inflate_end(&stream); - if (oid) - the_hash_algo->final_fn(oid->hash, &c); - return buf == fixed_buf ? NULL : buf; -} - -static void *unpack_raw_entry(struct object_entry *obj, - off_t *ofs_offset, - struct object_id *ref_oid, - struct object_id *oid) -{ - unsigned char *p; - unsigned long size, c; - off_t base_offset; - unsigned shift; - void *data; - - obj->idx.offset = consumed_bytes; - input_crc32 = crc32(0, NULL, 0); - - p = fill(1); - c = *p; - use(1); - obj->type = (c >> 4) & 7; - size = (c & 15); - shift = 4; - while (c & 0x80) { - p = fill(1); - c = *p; - use(1); - size += (c & 0x7f) << shift; - shift += 7; - } - obj->size = size; - - switch (obj->type) { - case OBJ_REF_DELTA: - hashcpy(ref_oid->hash, fill(the_hash_algo->rawsz)); - use(the_hash_algo->rawsz); - break; - case OBJ_OFS_DELTA: - p = fill(1); - c = *p; - use(1); - base_offset = c & 127; - while (c & 128) { - base_offset += 1; - if (!base_offset || MSB(base_offset, 7)) - bad_object(obj->idx.offset, _("offset value overflow for delta base object")); - p = fill(1); - c = *p; - use(1); - base_offset = (base_offset << 7) + (c & 127); - } - *ofs_offset = obj->idx.offset - base_offset; - if (*ofs_offset <= 0 || *ofs_offset >= obj->idx.offset) - bad_object(obj->idx.offset, _("delta base offset is out of bound")); - break; - case OBJ_COMMIT: - case OBJ_TREE: - case OBJ_BLOB: - case OBJ_TAG: - break; - default: - bad_object(obj->idx.offset, _("unknown object type %d"), obj->type); - } - obj->hdr_size = consumed_bytes - obj->idx.offset; - - data = unpack_entry_data(obj->idx.offset, obj->size, obj->type, oid); - obj->idx.crc32 = input_crc32; - return data; -} - -static void *unpack_data(struct object_entry *obj, - int (*consume)(const unsigned char *, unsigned long, void *), - void *cb_data) -{ - off_t from = obj[0].idx.offset + obj[0].hdr_size; - off_t len = obj[1].idx.offset - from; - unsigned char *data, *inbuf; - git_zstream stream; - int status; - - data = xmallocz(consume ? 64*1024 : obj->size); - inbuf = xmalloc((len < 64*1024) ? (int)len : 64*1024); - - memset(&stream, 0, sizeof(stream)); - git_inflate_init(&stream); - stream.next_out = data; - stream.avail_out = consume ? 64*1024 : obj->size; - - do { - ssize_t n = (len < 64*1024) ? (ssize_t)len : 64*1024; - n = xpread(get_thread_data()->pack_fd, inbuf, n, from); - if (n < 0) - die_errno(_("cannot pread pack file")); - if (!n) - die(Q_("premature end of pack file, %"PRIuMAX" byte missing", - "premature end of pack file, %"PRIuMAX" bytes missing", - (unsigned int)len), - (uintmax_t)len); - from += n; - len -= n; - stream.next_in = inbuf; - stream.avail_in = n; - if (!consume) - status = git_inflate(&stream, 0); - else { - do { - status = git_inflate(&stream, 0); - if (consume(data, stream.next_out - data, cb_data)) { - free(inbuf); - free(data); - return NULL; - } - stream.next_out = data; - stream.avail_out = 64*1024; - } while (status == Z_OK && stream.avail_in); - } - } while (len && status == Z_OK && !stream.avail_in); - - /* This has been inflated OK when first encountered, so... */ - if (status != Z_STREAM_END || stream.total_out != obj->size) - die(_("serious inflate inconsistency")); - - git_inflate_end(&stream); - free(inbuf); - if (consume) { - FREE_AND_NULL(data); - } - return data; -} - -static void *get_data_from_pack(struct object_entry *obj) -{ - return unpack_data(obj, NULL, NULL); -} - -static int compare_ofs_delta_bases(off_t offset1, off_t offset2, - enum object_type type1, - enum object_type type2) -{ - int cmp = type1 - type2; - if (cmp) - return cmp; - return offset1 < offset2 ? -1 : - offset1 > offset2 ? 1 : - 0; -} - -static int find_ofs_delta(const off_t offset) -{ - int first = 0, last = nr_ofs_deltas; - - while (first < last) { - int next = first + (last - first) / 2; - struct ofs_delta_entry *delta = &ofs_deltas[next]; - int cmp; - - cmp = compare_ofs_delta_bases(offset, delta->offset, - OBJ_OFS_DELTA, - objects[delta->obj_no].type); - if (!cmp) - return next; - if (cmp < 0) { - last = next; - continue; - } - first = next+1; - } - return -first-1; -} - -static void find_ofs_delta_children(off_t offset, - int *first_index, int *last_index) -{ - int first = find_ofs_delta(offset); - int last = first; - int end = nr_ofs_deltas - 1; - - if (first < 0) { - *first_index = 0; - *last_index = -1; - return; - } - while (first > 0 && ofs_deltas[first - 1].offset == offset) - --first; - while (last < end && ofs_deltas[last + 1].offset == offset) - ++last; - *first_index = first; - *last_index = last; -} - -static int compare_ref_delta_bases(const struct object_id *oid1, - const struct object_id *oid2, - enum object_type type1, - enum object_type type2) -{ - int cmp = type1 - type2; - if (cmp) - return cmp; - return oidcmp(oid1, oid2); -} - -static int find_ref_delta(const struct object_id *oid) -{ - int first = 0, last = nr_ref_deltas; - - while (first < last) { - int next = first + (last - first) / 2; - struct ref_delta_entry *delta = &ref_deltas[next]; - int cmp; - - cmp = compare_ref_delta_bases(oid, &delta->oid, - OBJ_REF_DELTA, - objects[delta->obj_no].type); - if (!cmp) - return next; - if (cmp < 0) { - last = next; - continue; - } - first = next+1; - } - return -first-1; -} - -static void find_ref_delta_children(const struct object_id *oid, - int *first_index, int *last_index) -{ - int first = find_ref_delta(oid); - int last = first; - int end = nr_ref_deltas - 1; - - if (first < 0) { - *first_index = 0; - *last_index = -1; - return; - } - while (first > 0 && oideq(&ref_deltas[first - 1].oid, oid)) - --first; - while (last < end && oideq(&ref_deltas[last + 1].oid, oid)) - ++last; - *first_index = first; - *last_index = last; -} - -struct compare_data { - struct object_entry *entry; - struct git_istream *st; - unsigned char *buf; - unsigned long buf_size; -}; - -static int compare_objects(const unsigned char *buf, unsigned long size, - void *cb_data) -{ - struct compare_data *data = cb_data; - - if (data->buf_size < size) { - free(data->buf); - data->buf = xmalloc(size); - data->buf_size = size; - } - - while (size) { - ssize_t len = read_istream(data->st, data->buf, size); - if (len == 0) - die(_("SHA1 COLLISION FOUND WITH %s !"), - oid_to_hex(&data->entry->idx.oid)); - if (len < 0) - die(_("unable to read %s"), - oid_to_hex(&data->entry->idx.oid)); - if (memcmp(buf, data->buf, len)) - die(_("SHA1 COLLISION FOUND WITH %s !"), - oid_to_hex(&data->entry->idx.oid)); - size -= len; - buf += len; - } - return 0; -} - -static int check_collison(struct object_entry *entry) -{ - struct compare_data data; - enum object_type type; - unsigned long size; - - if (entry->size <= big_file_threshold || entry->type != OBJ_BLOB) - return -1; - - memset(&data, 0, sizeof(data)); - data.entry = entry; - data.st = open_istream(the_repository, &entry->idx.oid, &type, &size, - NULL); - if (!data.st) - return -1; - if (size != entry->size || type != entry->type) - die(_("SHA1 COLLISION FOUND WITH %s !"), - oid_to_hex(&entry->idx.oid)); - unpack_data(entry, compare_objects, &data); - close_istream(data.st); - free(data.buf); - return 0; -} - -static void sha1_object(const void *data, struct object_entry *obj_entry, - unsigned long size, enum object_type type, - const struct object_id *oid) -{ - void *new_data = NULL; - int collision_test_needed = 0; - - assert(data || obj_entry); - - if (startup_info->have_repository) { - read_lock(); - collision_test_needed = - has_object_file_with_flags(oid, OBJECT_INFO_QUICK); - read_unlock(); - } - - if (collision_test_needed && !data) { - read_lock(); - if (!check_collison(obj_entry)) - collision_test_needed = 0; - read_unlock(); - } - if (collision_test_needed) { - void *has_data; - enum object_type has_type; - unsigned long has_size; - read_lock(); - has_type = oid_object_info(the_repository, oid, &has_size); - if (has_type < 0) - die(_("cannot read existing object info %s"), oid_to_hex(oid)); - if (has_type != type || has_size != size) - die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid)); - has_data = read_object_file(oid, &has_type, &has_size); - read_unlock(); - if (!data) - data = new_data = get_data_from_pack(obj_entry); - if (!has_data) - die(_("cannot read existing object %s"), oid_to_hex(oid)); - if (size != has_size || type != has_type || - memcmp(data, has_data, size) != 0) - die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid)); - free(has_data); - } - - if (strict || do_fsck_object) { - read_lock(); - if (type == OBJ_BLOB) { - struct blob *blob = lookup_blob(the_repository, oid); - if (blob) - blob->object.flags |= FLAG_CHECKED; - else - die(_("invalid blob object %s"), oid_to_hex(oid)); - if (do_fsck_object && - fsck_object(&blob->object, (void *)data, size, &fsck_options)) - die(_("fsck error in packed object")); - } else { - struct object *obj; - int eaten; - void *buf = (void *) data; - - assert(data && "data can only be NULL for large _blobs_"); - - /* - * we do not need to free the memory here, as the - * buf is deleted by the caller. - */ - obj = parse_object_buffer(the_repository, oid, type, - size, buf, - &eaten); - if (!obj) - die(_("invalid %s"), type_name(type)); - if (do_fsck_object && - fsck_object(obj, buf, size, &fsck_options)) - die(_("fsck error in packed object")); - if (strict && fsck_walk(obj, NULL, &fsck_options)) - die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid)); - - if (obj->type == OBJ_TREE) { - struct tree *item = (struct tree *) obj; - item->buffer = NULL; - obj->parsed = 0; - } - if (obj->type == OBJ_COMMIT) { - struct commit *commit = (struct commit *) obj; - if (detach_commit_buffer(commit, NULL) != data) - BUG("parse_object_buffer transmogrified our buffer"); - } - obj->flags |= FLAG_CHECKED; - } - read_unlock(); - } - - free(new_data); -} - -/* - * Ensure that this node has been reconstructed and return its contents. - * - * In the typical and best case, this node would already be reconstructed - * (through the invocation to resolve_delta() in threaded_second_pass()) and it - * would not be pruned. However, if pruning of this node was necessary due to - * reaching delta_base_cache_limit, this function will find the closest - * ancestor with reconstructed data that has not been pruned (or if there is - * none, the ultimate base object), and reconstruct each node in the delta - * chain in order to generate the reconstructed data for this node. - */ -static void *get_base_data(struct base_data *c) -{ - if (!c->data) { - struct object_entry *obj = c->obj; - struct base_data **delta = NULL; - int delta_nr = 0, delta_alloc = 0; - - while (is_delta_type(c->obj->type) && !c->data) { - ALLOC_GROW(delta, delta_nr + 1, delta_alloc); - delta[delta_nr++] = c; - c = c->base; - } - if (!delta_nr) { - c->data = get_data_from_pack(obj); - c->size = obj->size; - base_cache_used += c->size; - prune_base_data(c); - } - for (; delta_nr > 0; delta_nr--) { - void *base, *raw; - c = delta[delta_nr - 1]; - obj = c->obj; - base = get_base_data(c->base); - raw = get_data_from_pack(obj); - c->data = patch_delta( - base, c->base->size, - raw, obj->size, - &c->size); - free(raw); - if (!c->data) - bad_object(obj->idx.offset, _("failed to apply delta")); - base_cache_used += c->size; - prune_base_data(c); - } - free(delta); - } - return c->data; -} - -static struct base_data *make_base(struct object_entry *obj, - struct base_data *parent) -{ - struct base_data *base = xcalloc(1, sizeof(struct base_data)); - base->base = parent; - base->obj = obj; - find_ref_delta_children(&obj->idx.oid, - &base->ref_first, &base->ref_last); - find_ofs_delta_children(obj->idx.offset, - &base->ofs_first, &base->ofs_last); - base->children_remaining = base->ref_last - base->ref_first + - base->ofs_last - base->ofs_first + 2; - return base; -} - -static struct base_data *resolve_delta(struct object_entry *delta_obj, - struct base_data *base) -{ - void *delta_data, *result_data; - struct base_data *result; - unsigned long result_size; - - if (show_stat) { - int i = delta_obj - objects; - int j = base->obj - objects; - obj_stat[i].delta_depth = obj_stat[j].delta_depth + 1; - deepest_delta_lock(); - if (deepest_delta < obj_stat[i].delta_depth) - deepest_delta = obj_stat[i].delta_depth; - deepest_delta_unlock(); - obj_stat[i].base_object_no = j; - } - delta_data = get_data_from_pack(delta_obj); - assert(base->data); - result_data = patch_delta(base->data, base->size, - delta_data, delta_obj->size, &result_size); - free(delta_data); - if (!result_data) - bad_object(delta_obj->idx.offset, _("failed to apply delta")); - hash_object_file(the_hash_algo, result_data, result_size, - type_name(delta_obj->real_type), &delta_obj->idx.oid); - sha1_object(result_data, NULL, result_size, delta_obj->real_type, - &delta_obj->idx.oid); - - result = make_base(delta_obj, base); - result->data = result_data; - result->size = result_size; - - counter_lock(); - nr_resolved_deltas++; - counter_unlock(); - - return result; -} - -static int compare_ofs_delta_entry(const void *a, const void *b) -{ - const struct ofs_delta_entry *delta_a = a; - const struct ofs_delta_entry *delta_b = b; - - return delta_a->offset < delta_b->offset ? -1 : - delta_a->offset > delta_b->offset ? 1 : - 0; -} - -static int compare_ref_delta_entry(const void *a, const void *b) -{ - const struct ref_delta_entry *delta_a = a; - const struct ref_delta_entry *delta_b = b; - - return oidcmp(&delta_a->oid, &delta_b->oid); -} - -static void *threaded_second_pass(void *data) -{ - if (data) - set_thread_data(data); - for (;;) { - struct base_data *parent = NULL; - struct object_entry *child_obj; - struct base_data *child; - - counter_lock(); - display_progress(progress, nr_resolved_deltas); - counter_unlock(); - - work_lock(); - if (list_empty(&work_head)) { - /* - * Take an object from the object array. - */ - while (nr_dispatched < nr_objects && - is_delta_type(objects[nr_dispatched].type)) - nr_dispatched++; - if (nr_dispatched >= nr_objects) { - work_unlock(); - break; - } - child_obj = &objects[nr_dispatched++]; - } else { - /* - * Peek at the top of the stack, and take a child from - * it. - */ - parent = list_first_entry(&work_head, struct base_data, - list); - - if (parent->ref_first <= parent->ref_last) { - int offset = ref_deltas[parent->ref_first++].obj_no; - child_obj = objects + offset; - if (child_obj->real_type != OBJ_REF_DELTA) - die("REF_DELTA at offset %"PRIuMAX" already resolved (duplicate base %s?)", - (uintmax_t) child_obj->idx.offset, - oid_to_hex(&parent->obj->idx.oid)); - child_obj->real_type = parent->obj->real_type; - } else { - child_obj = objects + - ofs_deltas[parent->ofs_first++].obj_no; - assert(child_obj->real_type == OBJ_OFS_DELTA); - child_obj->real_type = parent->obj->real_type; - } - - if (parent->ref_first > parent->ref_last && - parent->ofs_first > parent->ofs_last) { - /* - * This parent has run out of children, so move - * it to done_head. - */ - list_del(&parent->list); - list_add(&parent->list, &done_head); - } - - /* - * Ensure that the parent has data, since we will need - * it later. - * - * NEEDSWORK: If parent data needs to be reloaded, this - * prolongs the time that the current thread spends in - * the mutex. A mitigating factor is that parent data - * needs to be reloaded only if the delta base cache - * limit is exceeded, so in the typical case, this does - * not happen. - */ - get_base_data(parent); - parent->retain_data++; - } - work_unlock(); - - if (parent) { - child = resolve_delta(child_obj, parent); - if (!child->children_remaining) - FREE_AND_NULL(child->data); - } else { - child = make_base(child_obj, NULL); - if (child->children_remaining) { - /* - * Since this child has its own delta children, - * we will need this data in the future. - * Inflate now so that future iterations will - * have access to this object's data while - * outside the work mutex. - */ - child->data = get_data_from_pack(child_obj); - child->size = child_obj->size; - } - } - - work_lock(); - if (parent) - parent->retain_data--; - if (child->data) { - /* - * This child has its own children, so add it to - * work_head. - */ - list_add(&child->list, &work_head); - base_cache_used += child->size; - prune_base_data(NULL); - } else { - /* - * This child does not have its own children. It may be - * the last descendant of its ancestors; free those - * that we can. - */ - struct base_data *p = parent; - - while (p) { - struct base_data *next_p; - - p->children_remaining--; - if (p->children_remaining) - break; - - next_p = p->base; - free_base_data(p); - list_del(&p->list); - free(p); - - p = next_p; - } - } - work_unlock(); - } - return NULL; -} - -/* - * First pass: - * - find locations of all objects; - * - calculate SHA1 of all non-delta objects; - * - remember base (SHA1 or offset) for all deltas. - */ -static void parse_pack_objects(unsigned char *hash) -{ - int i, nr_delays = 0; - struct ofs_delta_entry *ofs_delta = ofs_deltas; - struct object_id ref_delta_oid; - struct stat st; - - if (verbose) - progress = start_progress( - from_stdin ? _("Receiving objects") : _("Indexing objects"), - nr_objects); - for (i = 0; i < nr_objects; i++) { - struct object_entry *obj = &objects[i]; - void *data = unpack_raw_entry(obj, &ofs_delta->offset, - &ref_delta_oid, - &obj->idx.oid); - obj->real_type = obj->type; - if (obj->type == OBJ_OFS_DELTA) { - nr_ofs_deltas++; - ofs_delta->obj_no = i; - ofs_delta++; - } else if (obj->type == OBJ_REF_DELTA) { - ALLOC_GROW(ref_deltas, nr_ref_deltas + 1, ref_deltas_alloc); - oidcpy(&ref_deltas[nr_ref_deltas].oid, &ref_delta_oid); - ref_deltas[nr_ref_deltas].obj_no = i; - nr_ref_deltas++; - } else if (!data) { - /* large blobs, check later */ - obj->real_type = OBJ_BAD; - nr_delays++; - } else - sha1_object(data, NULL, obj->size, obj->type, - &obj->idx.oid); - free(data); - display_progress(progress, i+1); - } - objects[i].idx.offset = consumed_bytes; - stop_progress(&progress); - - /* Check pack integrity */ - flush(); - the_hash_algo->final_fn(hash, &input_ctx); - if (!hasheq(fill(the_hash_algo->rawsz), hash)) - die(_("pack is corrupted (SHA1 mismatch)")); - use(the_hash_algo->rawsz); - - /* If input_fd is a file, we should have reached its end now. */ - if (fstat(input_fd, &st)) - die_errno(_("cannot fstat packfile")); - if (S_ISREG(st.st_mode) && - lseek(input_fd, 0, SEEK_CUR) - input_len != st.st_size) - die(_("pack has junk at the end")); - - for (i = 0; i < nr_objects; i++) { - struct object_entry *obj = &objects[i]; - if (obj->real_type != OBJ_BAD) - continue; - obj->real_type = obj->type; - sha1_object(NULL, obj, obj->size, obj->type, - &obj->idx.oid); - nr_delays--; - } - if (nr_delays) - die(_("confusion beyond insanity in parse_pack_objects()")); -} - -/* - * Second pass: - * - for all non-delta objects, look if it is used as a base for - * deltas; - * - if used as a base, uncompress the object and apply all deltas, - * recursively checking if the resulting object is used as a base - * for some more deltas. - */ -static void resolve_deltas(void) -{ - int i; - - if (!nr_ofs_deltas && !nr_ref_deltas) - return; - - /* Sort deltas by base SHA1/offset for fast searching */ - QSORT(ofs_deltas, nr_ofs_deltas, compare_ofs_delta_entry); - QSORT(ref_deltas, nr_ref_deltas, compare_ref_delta_entry); - - if (verbose || show_resolving_progress) - progress = start_progress(_("Resolving deltas"), - nr_ref_deltas + nr_ofs_deltas); - - nr_dispatched = 0; - base_cache_limit = delta_base_cache_limit * nr_threads; - if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) { - init_thread(); - for (i = 0; i < nr_threads; i++) { - int ret = pthread_create(&thread_data[i].thread, NULL, - threaded_second_pass, thread_data + i); - if (ret) - die(_("unable to create thread: %s"), - strerror(ret)); - } - for (i = 0; i < nr_threads; i++) - pthread_join(thread_data[i].thread, NULL); - cleanup_thread(); - return; - } - threaded_second_pass(¬hread_data); -} - -/* - * Third pass: - * - append objects to convert thin pack to full pack if required - * - write the final pack hash - */ -static void fix_unresolved_deltas(struct hashfile *f); -static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_hash) -{ - if (nr_ref_deltas + nr_ofs_deltas == nr_resolved_deltas) { - stop_progress(&progress); - /* Flush remaining pack final hash. */ - flush(); - return; - } - - if (fix_thin_pack) { - struct hashfile *f; - unsigned char read_hash[GIT_MAX_RAWSZ], tail_hash[GIT_MAX_RAWSZ]; - struct strbuf msg = STRBUF_INIT; - int nr_unresolved = nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas; - int nr_objects_initial = nr_objects; - if (nr_unresolved <= 0) - die(_("confusion beyond insanity")); - REALLOC_ARRAY(objects, nr_objects + nr_unresolved + 1); - memset(objects + nr_objects + 1, 0, - nr_unresolved * sizeof(*objects)); - f = hashfd(output_fd, curr_pack); - fix_unresolved_deltas(f); - strbuf_addf(&msg, Q_("completed with %d local object", - "completed with %d local objects", - nr_objects - nr_objects_initial), - nr_objects - nr_objects_initial); - stop_progress_msg(&progress, msg.buf); - strbuf_release(&msg); - finalize_hashfile(f, tail_hash, 0); - hashcpy(read_hash, pack_hash); - fixup_pack_header_footer(output_fd, pack_hash, - curr_pack, nr_objects, - read_hash, consumed_bytes-the_hash_algo->rawsz); - if (!hasheq(read_hash, tail_hash)) - die(_("Unexpected tail checksum for %s " - "(disk corruption?)"), curr_pack); - } - if (nr_ofs_deltas + nr_ref_deltas != nr_resolved_deltas) - die(Q_("pack has %d unresolved delta", - "pack has %d unresolved deltas", - nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas), - nr_ofs_deltas + nr_ref_deltas - nr_resolved_deltas); -} - -static int write_compressed(struct hashfile *f, void *in, unsigned int size) -{ - git_zstream stream; - int status; - unsigned char outbuf[4096]; - - git_deflate_init(&stream, zlib_compression_level); - stream.next_in = in; - stream.avail_in = size; - - do { - stream.next_out = outbuf; - stream.avail_out = sizeof(outbuf); - status = git_deflate(&stream, Z_FINISH); - hashwrite(f, outbuf, sizeof(outbuf) - stream.avail_out); - } while (status == Z_OK); - - if (status != Z_STREAM_END) - die(_("unable to deflate appended object (%d)"), status); - size = stream.total_out; - git_deflate_end(&stream); - return size; -} - -static struct object_entry *append_obj_to_pack(struct hashfile *f, - const unsigned char *sha1, void *buf, - unsigned long size, enum object_type type) -{ - struct object_entry *obj = &objects[nr_objects++]; - unsigned char header[10]; - unsigned long s = size; - int n = 0; - unsigned char c = (type << 4) | (s & 15); - s >>= 4; - while (s) { - header[n++] = c | 0x80; - c = s & 0x7f; - s >>= 7; - } - header[n++] = c; - crc32_begin(f); - hashwrite(f, header, n); - obj[0].size = size; - obj[0].hdr_size = n; - obj[0].type = type; - obj[0].real_type = type; - obj[1].idx.offset = obj[0].idx.offset + n; - obj[1].idx.offset += write_compressed(f, buf, size); - obj[0].idx.crc32 = crc32_end(f); - hashflush(f); - hashcpy(obj->idx.oid.hash, sha1); - return obj; -} - -static int delta_pos_compare(const void *_a, const void *_b) -{ - struct ref_delta_entry *a = *(struct ref_delta_entry **)_a; - struct ref_delta_entry *b = *(struct ref_delta_entry **)_b; - return a->obj_no - b->obj_no; -} - -static void fix_unresolved_deltas(struct hashfile *f) -{ - struct ref_delta_entry **sorted_by_pos; - int i; - - /* - * Since many unresolved deltas may well be themselves base objects - * for more unresolved deltas, we really want to include the - * smallest number of base objects that would cover as much delta - * as possible by picking the - * trunc deltas first, allowing for other deltas to resolve without - * additional base objects. Since most base objects are to be found - * before deltas depending on them, a good heuristic is to start - * resolving deltas in the same order as their position in the pack. - */ - ALLOC_ARRAY(sorted_by_pos, nr_ref_deltas); - for (i = 0; i < nr_ref_deltas; i++) - sorted_by_pos[i] = &ref_deltas[i]; - QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare); - - if (has_promisor_remote()) { - /* - * Prefetch the delta bases. - */ - struct oid_array to_fetch = OID_ARRAY_INIT; - for (i = 0; i < nr_ref_deltas; i++) { - struct ref_delta_entry *d = sorted_by_pos[i]; - if (!oid_object_info_extended(the_repository, &d->oid, - NULL, - OBJECT_INFO_FOR_PREFETCH)) - continue; - oid_array_append(&to_fetch, &d->oid); - } - promisor_remote_get_direct(the_repository, - to_fetch.oid, to_fetch.nr); - oid_array_clear(&to_fetch); - } - - for (i = 0; i < nr_ref_deltas; i++) { - struct ref_delta_entry *d = sorted_by_pos[i]; - enum object_type type; - void *data; - unsigned long size; - - if (objects[d->obj_no].real_type != OBJ_REF_DELTA) - continue; - data = read_object_file(&d->oid, &type, &size); - if (!data) - continue; - - if (check_object_signature(the_repository, &d->oid, - data, size, - type_name(type))) - die(_("local object %s is corrupt"), oid_to_hex(&d->oid)); - - /* - * Add this as an object to the objects array and call - * threaded_second_pass() (which will pick up the added - * object). - */ - append_obj_to_pack(f, d->oid.hash, data, size, type); - threaded_second_pass(NULL); - - display_progress(progress, nr_resolved_deltas); - } - free(sorted_by_pos); -} - -static const char *derive_filename(const char *pack_name, const char *suffix, - struct strbuf *buf) -{ - size_t len; - if (!strip_suffix(pack_name, ".pack", &len)) - die(_("packfile name '%s' does not end with '.pack'"), - pack_name); - strbuf_add(buf, pack_name, len); - strbuf_addch(buf, '.'); - strbuf_addstr(buf, suffix); - return buf->buf; -} - -static void write_special_file(const char *suffix, const char *msg, - const char *pack_name, const unsigned char *hash, - const char **report) -{ - struct strbuf name_buf = STRBUF_INIT; - const char *filename; - int fd; - int msg_len = strlen(msg); - - if (pack_name) - filename = derive_filename(pack_name, suffix, &name_buf); - else - filename = odb_pack_name(&name_buf, hash, suffix); - - fd = odb_pack_keep(filename); - if (fd < 0) { - if (errno != EEXIST) - die_errno(_("cannot write %s file '%s'"), - suffix, filename); - } else { - if (msg_len > 0) { - write_or_die(fd, msg, msg_len); - write_or_die(fd, "\n", 1); - } - if (close(fd) != 0) - die_errno(_("cannot close written %s file '%s'"), - suffix, filename); - if (report) - *report = suffix; - } - strbuf_release(&name_buf); -} - -static void final(const char *final_pack_name, const char *curr_pack_name, - const char *final_index_name, const char *curr_index_name, - const char *keep_msg, const char *promisor_msg, - unsigned char *hash) -{ - const char *report = "pack"; - struct strbuf pack_name = STRBUF_INIT; - struct strbuf index_name = STRBUF_INIT; - int err; - - if (!from_stdin) { - close(input_fd); - } else { - fsync_or_die(output_fd, curr_pack_name); - err = close(output_fd); - if (err) - die_errno(_("error while closing pack file")); - } - - if (keep_msg) - write_special_file("keep", keep_msg, final_pack_name, hash, - &report); - if (promisor_msg) - write_special_file("promisor", promisor_msg, final_pack_name, - hash, NULL); - - if (final_pack_name != curr_pack_name) { - if (!final_pack_name) - final_pack_name = odb_pack_name(&pack_name, hash, "pack"); - if (finalize_object_file(curr_pack_name, final_pack_name)) - die(_("cannot store pack file")); - } else if (from_stdin) - chmod(final_pack_name, 0444); - - if (final_index_name != curr_index_name) { - if (!final_index_name) - final_index_name = odb_pack_name(&index_name, hash, "idx"); - if (finalize_object_file(curr_index_name, final_index_name)) - die(_("cannot store index file")); - } else - chmod(final_index_name, 0444); - - if (do_fsck_object) { - struct packed_git *p; - p = add_packed_git(final_index_name, strlen(final_index_name), 0); - if (p) - install_packed_git(the_repository, p); - } - - if (!from_stdin) { - printf("%s\n", hash_to_hex(hash)); - } else { - struct strbuf buf = STRBUF_INIT; - - strbuf_addf(&buf, "%s\t%s\n", report, hash_to_hex(hash)); - write_or_die(1, buf.buf, buf.len); - strbuf_release(&buf); - - /* - * Let's just mimic git-unpack-objects here and write - * the last part of the input buffer to stdout. - */ - while (input_len) { - err = xwrite(1, input_buffer + input_offset, input_len); - if (err <= 0) - break; - input_len -= err; - input_offset += err; - } - } - - strbuf_release(&index_name); - strbuf_release(&pack_name); -} - -static int git_index_pack_config(const char *k, const char *v, void *cb) -{ - struct pack_idx_option *opts = cb; - - if (!strcmp(k, "pack.indexversion")) { - opts->version = git_config_int(k, v); - if (opts->version > 2) - die(_("bad pack.indexversion=%"PRIu32), opts->version); - return 0; - } - if (!strcmp(k, "pack.threads")) { - nr_threads = git_config_int(k, v); - if (nr_threads < 0) - die(_("invalid number of threads specified (%d)"), - nr_threads); - if (!HAVE_THREADS && nr_threads != 1) { - warning(_("no threads support, ignoring %s"), k); - nr_threads = 1; - } - return 0; - } - return git_default_config(k, v, cb); -} - -static int cmp_uint32(const void *a_, const void *b_) -{ - uint32_t a = *((uint32_t *)a_); - uint32_t b = *((uint32_t *)b_); - - return (a < b) ? -1 : (a != b); -} - -static void read_v2_anomalous_offsets(struct packed_git *p, - struct pack_idx_option *opts) -{ - const uint32_t *idx1, *idx2; - uint32_t i; - - /* The address of the 4-byte offset table */ - idx1 = (((const uint32_t *)((const uint8_t *)p->index_data + p->crc_offset)) - + p->num_objects /* CRC32 table */ - ); - - /* The address of the 8-byte offset table */ - idx2 = idx1 + p->num_objects; - - for (i = 0; i < p->num_objects; i++) { - uint32_t off = ntohl(idx1[i]); - if (!(off & 0x80000000)) - continue; - off = off & 0x7fffffff; - check_pack_index_ptr(p, &idx2[off * 2]); - if (idx2[off * 2]) - continue; - /* - * The real offset is ntohl(idx2[off * 2]) in high 4 - * octets, and ntohl(idx2[off * 2 + 1]) in low 4 - * octets. But idx2[off * 2] is Zero!!! - */ - ALLOC_GROW(opts->anomaly, opts->anomaly_nr + 1, opts->anomaly_alloc); - opts->anomaly[opts->anomaly_nr++] = ntohl(idx2[off * 2 + 1]); - } - - QSORT(opts->anomaly, opts->anomaly_nr, cmp_uint32); -} - -static void read_idx_option(struct pack_idx_option *opts, const char *pack_name) -{ - struct packed_git *p = add_packed_git(pack_name, strlen(pack_name), 1); - - if (!p) - die(_("Cannot open existing pack file '%s'"), pack_name); - if (open_pack_index(p)) - die(_("Cannot open existing pack idx file for '%s'"), pack_name); - - /* Read the attributes from the existing idx file */ - opts->version = p->index_version; - - if (opts->version == 2) - read_v2_anomalous_offsets(p, opts); - - /* - * Get rid of the idx file as we do not need it anymore. - * NEEDSWORK: extract this bit from free_pack_by_name() in - * sha1-file.c, perhaps? It shouldn't matter very much as we - * know we haven't installed this pack (hence we never have - * read anything from it). - */ - close_pack_index(p); - free(p); -} - -static void show_pack_info(int stat_only) -{ - int i, baseobjects = nr_objects - nr_ref_deltas - nr_ofs_deltas; - unsigned long *chain_histogram = NULL; - - if (deepest_delta) - chain_histogram = xcalloc(deepest_delta, sizeof(unsigned long)); - - for (i = 0; i < nr_objects; i++) { - struct object_entry *obj = &objects[i]; - - if (is_delta_type(obj->type)) - chain_histogram[obj_stat[i].delta_depth - 1]++; - if (stat_only) - continue; - printf("%s %-6s %"PRIuMAX" %"PRIuMAX" %"PRIuMAX, - oid_to_hex(&obj->idx.oid), - type_name(obj->real_type), (uintmax_t)obj->size, - (uintmax_t)(obj[1].idx.offset - obj->idx.offset), - (uintmax_t)obj->idx.offset); - if (is_delta_type(obj->type)) { - struct object_entry *bobj = &objects[obj_stat[i].base_object_no]; - printf(" %u %s", obj_stat[i].delta_depth, - oid_to_hex(&bobj->idx.oid)); - } - putchar('\n'); - } - - if (baseobjects) - printf_ln(Q_("non delta: %d object", - "non delta: %d objects", - baseobjects), - baseobjects); - for (i = 0; i < deepest_delta; i++) { - if (!chain_histogram[i]) - continue; - printf_ln(Q_("chain length = %d: %lu object", - "chain length = %d: %lu objects", - chain_histogram[i]), - i + 1, - chain_histogram[i]); - } -} - -int cmd_index_pack(int argc, const char **argv, const char *prefix) -{ - int i, fix_thin_pack = 0, verify = 0, stat_only = 0; - const char *curr_index; - const char *index_name = NULL, *pack_name = NULL; - const char *keep_msg = NULL; - const char *promisor_msg = NULL; - struct strbuf index_name_buf = STRBUF_INIT; - struct pack_idx_entry **idx_objects; - struct pack_idx_option opts; - unsigned char pack_hash[GIT_MAX_RAWSZ]; - unsigned foreign_nr = 1; /* zero is a "good" value, assume bad */ - int report_end_of_input = 0; - int hash_algo = 0; - - /* - * index-pack never needs to fetch missing objects except when - * REF_DELTA bases are missing (which are explicitly handled). It only - * accesses the repo to do hash collision checks and to check which - * REF_DELTA bases need to be fetched. - */ - fetch_if_missing = 0; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage(index_pack_usage); - - read_replace_refs = 0; - fsck_options.walk = mark_link; - - reset_pack_idx_option(&opts); - git_config(git_index_pack_config, &opts); - if (prefix && chdir(prefix)) - die(_("Cannot come back to cwd")); - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (*arg == '-') { - if (!strcmp(arg, "--stdin")) { - from_stdin = 1; - } else if (!strcmp(arg, "--fix-thin")) { - fix_thin_pack = 1; - } else if (skip_to_optional_arg(arg, "--strict", &arg)) { - strict = 1; - do_fsck_object = 1; - fsck_set_msg_types(&fsck_options, arg); - } else if (!strcmp(arg, "--check-self-contained-and-connected")) { - strict = 1; - check_self_contained_and_connected = 1; - } else if (!strcmp(arg, "--fsck-objects")) { - do_fsck_object = 1; - } else if (!strcmp(arg, "--verify")) { - verify = 1; - } else if (!strcmp(arg, "--verify-stat")) { - verify = 1; - show_stat = 1; - } else if (!strcmp(arg, "--verify-stat-only")) { - verify = 1; - show_stat = 1; - stat_only = 1; - } else if (skip_to_optional_arg(arg, "--keep", &keep_msg)) { - ; /* nothing to do */ - } else if (skip_to_optional_arg(arg, "--promisor", &promisor_msg)) { - ; /* already parsed */ - } else if (starts_with(arg, "--threads=")) { - char *end; - nr_threads = strtoul(arg+10, &end, 0); - if (!arg[10] || *end || nr_threads < 0) - usage(index_pack_usage); - if (!HAVE_THREADS && nr_threads != 1) { - warning(_("no threads support, ignoring %s"), arg); - nr_threads = 1; - } - } else if (starts_with(arg, "--pack_header=")) { - struct pack_header *hdr; - char *c; - - hdr = (struct pack_header *)input_buffer; - hdr->hdr_signature = htonl(PACK_SIGNATURE); - hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10)); - if (*c != ',') - die(_("bad %s"), arg); - hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10)); - if (*c) - die(_("bad %s"), arg); - input_len = sizeof(*hdr); - } else if (!strcmp(arg, "-v")) { - verbose = 1; - } else if (!strcmp(arg, "--show-resolving-progress")) { - show_resolving_progress = 1; - } else if (!strcmp(arg, "--report-end-of-input")) { - report_end_of_input = 1; - } else if (!strcmp(arg, "-o")) { - if (index_name || (i+1) >= argc) - usage(index_pack_usage); - index_name = argv[++i]; - } else if (starts_with(arg, "--index-version=")) { - char *c; - opts.version = strtoul(arg + 16, &c, 10); - if (opts.version > 2) - die(_("bad %s"), arg); - if (*c == ',') - opts.off32_limit = strtoul(c+1, &c, 0); - if (*c || opts.off32_limit & 0x80000000) - die(_("bad %s"), arg); - } else if (skip_prefix(arg, "--max-input-size=", &arg)) { - max_input_size = strtoumax(arg, NULL, 10); - } else if (skip_prefix(arg, "--object-format=", &arg)) { - hash_algo = hash_algo_by_name(arg); - if (hash_algo == GIT_HASH_UNKNOWN) - die(_("unknown hash algorithm '%s'"), arg); - repo_set_hash_algo(the_repository, hash_algo); - } else - usage(index_pack_usage); - continue; - } - - if (pack_name) - usage(index_pack_usage); - pack_name = arg; - } - - if (!pack_name && !from_stdin) - usage(index_pack_usage); - if (fix_thin_pack && !from_stdin) - die(_("--fix-thin cannot be used without --stdin")); - if (from_stdin && !startup_info->have_repository) - die(_("--stdin requires a git repository")); - if (from_stdin && hash_algo) - die(_("--object-format cannot be used with --stdin")); - if (!index_name && pack_name) - index_name = derive_filename(pack_name, "idx", &index_name_buf); - - if (verify) { - if (!index_name) - die(_("--verify with no packfile name given")); - read_idx_option(&opts, index_name); - opts.flags |= WRITE_IDX_VERIFY | WRITE_IDX_STRICT; - } - if (strict) - opts.flags |= WRITE_IDX_STRICT; - - if (HAVE_THREADS && !nr_threads) { - nr_threads = online_cpus(); - /* - * Experiments show that going above 20 threads doesn't help, - * no matter how many cores you have. Below that, we tend to - * max at half the number of online_cpus(), presumably because - * half of those are hyperthreads rather than full cores. We'll - * never reduce the level below "3", though, to match a - * historical value that nobody complained about. - */ - if (nr_threads < 4) - ; /* too few cores to consider capping */ - else if (nr_threads < 6) - nr_threads = 3; /* historic cap */ - else if (nr_threads < 40) - nr_threads /= 2; - else - nr_threads = 20; /* hard cap */ - } - - curr_pack = open_pack_file(pack_name); - parse_pack_header(); - objects = xcalloc(st_add(nr_objects, 1), sizeof(struct object_entry)); - if (show_stat) - obj_stat = xcalloc(st_add(nr_objects, 1), sizeof(struct object_stat)); - ofs_deltas = xcalloc(nr_objects, sizeof(struct ofs_delta_entry)); - parse_pack_objects(pack_hash); - if (report_end_of_input) - write_in_full(2, "\0", 1); - resolve_deltas(); - conclude_pack(fix_thin_pack, curr_pack, pack_hash); - free(ofs_deltas); - free(ref_deltas); - if (strict) - foreign_nr = check_objects(); - - if (show_stat) - show_pack_info(stat_only); - - ALLOC_ARRAY(idx_objects, nr_objects); - for (i = 0; i < nr_objects; i++) - idx_objects[i] = &objects[i].idx; - curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_hash); - free(idx_objects); - - if (!verify) - final(pack_name, curr_pack, - index_name, curr_index, - keep_msg, promisor_msg, - pack_hash); - else - close(input_fd); - - if (do_fsck_object && fsck_finish(&fsck_options)) - die(_("fsck error in pack objects")); - - free(objects); - strbuf_release(&index_name_buf); - if (pack_name == NULL) - free((void *) curr_pack); - if (index_name == NULL) - free((void *) curr_index); - - /* - * Let the caller know this pack is not self contained - */ - if (check_self_contained_and_connected && foreign_nr) - return 1; - - return 0; -} diff --git a/third_party/git/builtin/init-db.c b/third_party/git/builtin/init-db.c deleted file mode 100644 index 01bc648d416f..000000000000 --- a/third_party/git/builtin/init-db.c +++ /dev/null @@ -1,705 +0,0 @@ -/* - * GIT - The information manager from hell - * - * Copyright (C) Linus Torvalds, 2005 - */ -#include "cache.h" -#include "config.h" -#include "refs.h" -#include "builtin.h" -#include "exec-cmd.h" -#include "parse-options.h" -#include "worktree.h" - -#ifndef DEFAULT_GIT_TEMPLATE_DIR -#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates" -#endif - -#ifdef NO_TRUSTABLE_FILEMODE -#define TEST_FILEMODE 0 -#else -#define TEST_FILEMODE 1 -#endif - -#define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH" - -static int init_is_bare_repository = 0; -static int init_shared_repository = -1; -static const char *init_db_template_dir; - -static void copy_templates_1(struct strbuf *path, struct strbuf *template_path, - DIR *dir) -{ - size_t path_baselen = path->len; - size_t template_baselen = template_path->len; - struct dirent *de; - - /* Note: if ".git/hooks" file exists in the repository being - * re-initialized, /etc/core-git/templates/hooks/update would - * cause "git init" to fail here. I think this is sane but - * it means that the set of templates we ship by default, along - * with the way the namespace under .git/ is organized, should - * be really carefully chosen. - */ - safe_create_dir(path->buf, 1); - while ((de = readdir(dir)) != NULL) { - struct stat st_git, st_template; - int exists = 0; - - strbuf_setlen(path, path_baselen); - strbuf_setlen(template_path, template_baselen); - - if (de->d_name[0] == '.') - continue; - strbuf_addstr(path, de->d_name); - strbuf_addstr(template_path, de->d_name); - if (lstat(path->buf, &st_git)) { - if (errno != ENOENT) - die_errno(_("cannot stat '%s'"), path->buf); - } - else - exists = 1; - - if (lstat(template_path->buf, &st_template)) - die_errno(_("cannot stat template '%s'"), template_path->buf); - - if (S_ISDIR(st_template.st_mode)) { - DIR *subdir = opendir(template_path->buf); - if (!subdir) - die_errno(_("cannot opendir '%s'"), template_path->buf); - strbuf_addch(path, '/'); - strbuf_addch(template_path, '/'); - copy_templates_1(path, template_path, subdir); - closedir(subdir); - } - else if (exists) - continue; - else if (S_ISLNK(st_template.st_mode)) { - struct strbuf lnk = STRBUF_INIT; - if (strbuf_readlink(&lnk, template_path->buf, - st_template.st_size) < 0) - die_errno(_("cannot readlink '%s'"), template_path->buf); - if (symlink(lnk.buf, path->buf)) - die_errno(_("cannot symlink '%s' '%s'"), - lnk.buf, path->buf); - strbuf_release(&lnk); - } - else if (S_ISREG(st_template.st_mode)) { - if (copy_file(path->buf, template_path->buf, st_template.st_mode)) - die_errno(_("cannot copy '%s' to '%s'"), - template_path->buf, path->buf); - } - else - error(_("ignoring template %s"), template_path->buf); - } -} - -static void copy_templates(const char *template_dir) -{ - struct strbuf path = STRBUF_INIT; - struct strbuf template_path = STRBUF_INIT; - size_t template_len; - struct repository_format template_format = REPOSITORY_FORMAT_INIT; - struct strbuf err = STRBUF_INIT; - DIR *dir; - char *to_free = NULL; - - if (!template_dir) - template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT); - if (!template_dir) - template_dir = init_db_template_dir; - if (!template_dir) - template_dir = to_free = system_path(DEFAULT_GIT_TEMPLATE_DIR); - if (!template_dir[0]) { - free(to_free); - return; - } - - strbuf_addstr(&template_path, template_dir); - strbuf_complete(&template_path, '/'); - template_len = template_path.len; - - dir = opendir(template_path.buf); - if (!dir) { - warning(_("templates not found in %s"), template_dir); - goto free_return; - } - - /* Make sure that template is from the correct vintage */ - strbuf_addstr(&template_path, "config"); - read_repository_format(&template_format, template_path.buf); - strbuf_setlen(&template_path, template_len); - - /* - * No mention of version at all is OK, but anything else should be - * verified. - */ - if (template_format.version >= 0 && - verify_repository_format(&template_format, &err) < 0) { - warning(_("not copying templates from '%s': %s"), - template_dir, err.buf); - strbuf_release(&err); - goto close_free_return; - } - - strbuf_addstr(&path, get_git_common_dir()); - strbuf_complete(&path, '/'); - copy_templates_1(&path, &template_path, dir); -close_free_return: - closedir(dir); -free_return: - free(to_free); - strbuf_release(&path); - strbuf_release(&template_path); - clear_repository_format(&template_format); -} - -static int git_init_db_config(const char *k, const char *v, void *cb) -{ - if (!strcmp(k, "init.templatedir")) - return git_config_pathname(&init_db_template_dir, k, v); - - if (starts_with(k, "core.")) - return platform_core_config(k, v, cb); - - return 0; -} - -/* - * If the git_dir is not directly inside the working tree, then git will not - * find it by default, and we need to set the worktree explicitly. - */ -static int needs_work_tree_config(const char *git_dir, const char *work_tree) -{ - if (!strcmp(work_tree, "/") && !strcmp(git_dir, "/.git")) - return 0; - if (skip_prefix(git_dir, work_tree, &git_dir) && - !strcmp(git_dir, "/.git")) - return 0; - return 1; -} - -void initialize_repository_version(int hash_algo, int reinit) -{ - char repo_version_string[10]; - int repo_version = GIT_REPO_VERSION; - - if (hash_algo != GIT_HASH_SHA1) - repo_version = GIT_REPO_VERSION_READ; - - /* This forces creation of new config file */ - xsnprintf(repo_version_string, sizeof(repo_version_string), - "%d", repo_version); - git_config_set("core.repositoryformatversion", repo_version_string); - - if (hash_algo != GIT_HASH_SHA1) - git_config_set("extensions.objectformat", - hash_algos[hash_algo].name); - else if (reinit) - git_config_set_gently("extensions.objectformat", NULL); -} - -static int create_default_files(const char *template_path, - const char *original_git_dir, - const char *initial_branch, - const struct repository_format *fmt) -{ - struct stat st1; - struct strbuf buf = STRBUF_INIT; - char *path; - char junk[2]; - int reinit; - int filemode; - struct strbuf err = STRBUF_INIT; - - /* Just look for `init.templatedir` */ - init_db_template_dir = NULL; /* re-set in case it was set before */ - git_config(git_init_db_config, NULL); - - /* - * First copy the templates -- we might have the default - * config file there, in which case we would want to read - * from it after installing. - * - * Before reading that config, we also need to clear out any cached - * values (since we've just potentially changed what's available on - * disk). - */ - copy_templates(template_path); - git_config_clear(); - reset_shared_repository(); - git_config(git_default_config, NULL); - - /* - * We must make sure command-line options continue to override any - * values we might have just re-read from the config. - */ - is_bare_repository_cfg = init_is_bare_repository; - if (init_shared_repository != -1) - set_shared_repository(init_shared_repository); - - /* - * We would have created the above under user's umask -- under - * shared-repository settings, we would need to fix them up. - */ - if (get_shared_repository()) { - adjust_shared_perm(get_git_dir()); - } - - /* - * We need to create a "refs" dir in any case so that older - * versions of git can tell that this is a repository. - */ - safe_create_dir(git_path("refs"), 1); - adjust_shared_perm(git_path("refs")); - - if (refs_init_db(&err)) - die("failed to set up refs db: %s", err.buf); - - /* - * Point the HEAD symref to the initial branch with if HEAD does - * not yet exist. - */ - path = git_path_buf(&buf, "HEAD"); - reinit = (!access(path, R_OK) - || readlink(path, junk, sizeof(junk)-1) != -1); - if (!reinit) { - char *ref; - - if (!initial_branch) - initial_branch = git_default_branch_name(); - - ref = xstrfmt("refs/heads/%s", initial_branch); - if (check_refname_format(ref, 0) < 0) - die(_("invalid initial branch name: '%s'"), - initial_branch); - - if (create_symref("HEAD", ref, NULL) < 0) - exit(1); - free(ref); - } - - initialize_repository_version(fmt->hash_algo, 0); - - /* Check filemode trustability */ - path = git_path_buf(&buf, "config"); - filemode = TEST_FILEMODE; - if (TEST_FILEMODE && !lstat(path, &st1)) { - struct stat st2; - filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) && - !lstat(path, &st2) && - st1.st_mode != st2.st_mode && - !chmod(path, st1.st_mode)); - if (filemode && !reinit && (st1.st_mode & S_IXUSR)) - filemode = 0; - } - git_config_set("core.filemode", filemode ? "true" : "false"); - - if (is_bare_repository()) - git_config_set("core.bare", "true"); - else { - const char *work_tree = get_git_work_tree(); - git_config_set("core.bare", "false"); - /* allow template config file to override the default */ - if (log_all_ref_updates == LOG_REFS_UNSET) - git_config_set("core.logallrefupdates", "true"); - if (needs_work_tree_config(original_git_dir, work_tree)) - git_config_set("core.worktree", work_tree); - } - - if (!reinit) { - /* Check if symlink is supported in the work tree */ - path = git_path_buf(&buf, "tXXXXXX"); - if (!close(xmkstemp(path)) && - !unlink(path) && - !symlink("testing", path) && - !lstat(path, &st1) && - S_ISLNK(st1.st_mode)) - unlink(path); /* good */ - else - git_config_set("core.symlinks", "false"); - - /* Check if the filesystem is case-insensitive */ - path = git_path_buf(&buf, "CoNfIg"); - if (!access(path, F_OK)) - git_config_set("core.ignorecase", "true"); - probe_utf8_pathname_composition(); - } - - strbuf_release(&buf); - return reinit; -} - -static void create_object_directory(void) -{ - struct strbuf path = STRBUF_INIT; - size_t baselen; - - strbuf_addstr(&path, get_object_directory()); - baselen = path.len; - - safe_create_dir(path.buf, 1); - - strbuf_setlen(&path, baselen); - strbuf_addstr(&path, "/pack"); - safe_create_dir(path.buf, 1); - - strbuf_setlen(&path, baselen); - strbuf_addstr(&path, "/info"); - safe_create_dir(path.buf, 1); - - strbuf_release(&path); -} - -static void separate_git_dir(const char *git_dir, const char *git_link) -{ - struct stat st; - - if (!stat(git_link, &st)) { - const char *src; - - if (S_ISREG(st.st_mode)) - src = read_gitfile(git_link); - else if (S_ISDIR(st.st_mode)) - src = git_link; - else - die(_("unable to handle file type %d"), (int)st.st_mode); - - if (rename(src, git_dir)) - die_errno(_("unable to move %s to %s"), src, git_dir); - repair_worktrees(NULL, NULL); - } - - write_file(git_link, "gitdir: %s", git_dir); -} - -static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash) -{ - const char *env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT); - /* - * If we already have an initialized repo, don't allow the user to - * specify a different algorithm, as that could cause corruption. - * Otherwise, if the user has specified one on the command line, use it. - */ - if (repo_fmt->version >= 0 && hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo) - die(_("attempt to reinitialize repository with different hash")); - else if (hash != GIT_HASH_UNKNOWN) - repo_fmt->hash_algo = hash; - else if (env) { - int env_algo = hash_algo_by_name(env); - if (env_algo == GIT_HASH_UNKNOWN) - die(_("unknown hash algorithm '%s'"), env); - repo_fmt->hash_algo = env_algo; - } -} - -int init_db(const char *git_dir, const char *real_git_dir, - const char *template_dir, int hash, const char *initial_branch, - unsigned int flags) -{ - int reinit; - int exist_ok = flags & INIT_DB_EXIST_OK; - char *original_git_dir = real_pathdup(git_dir, 1); - struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT; - - if (real_git_dir) { - struct stat st; - - if (!exist_ok && !stat(git_dir, &st)) - die(_("%s already exists"), git_dir); - - if (!exist_ok && !stat(real_git_dir, &st)) - die(_("%s already exists"), real_git_dir); - - set_git_dir(real_git_dir, 1); - git_dir = get_git_dir(); - separate_git_dir(git_dir, original_git_dir); - } - else { - set_git_dir(git_dir, 1); - git_dir = get_git_dir(); - } - startup_info->have_repository = 1; - - /* Just look for `core.hidedotfiles` */ - git_config(git_init_db_config, NULL); - - safe_create_dir(git_dir, 0); - - init_is_bare_repository = is_bare_repository(); - - /* Check to see if the repository version is right. - * Note that a newly created repository does not have - * config file, so this will not fail. What we are catching - * is an attempt to reinitialize new repository with an old tool. - */ - check_repository_format(&repo_fmt); - - validate_hash_algorithm(&repo_fmt, hash); - - reinit = create_default_files(template_dir, original_git_dir, - initial_branch, &repo_fmt); - if (reinit && initial_branch) - warning(_("re-init: ignored --initial-branch=%s"), - initial_branch); - - create_object_directory(); - - if (get_shared_repository()) { - char buf[10]; - /* We do not spell "group" and such, so that - * the configuration can be read by older version - * of git. Note, we use octal numbers for new share modes, - * and compatibility values for PERM_GROUP and - * PERM_EVERYBODY. - */ - if (get_shared_repository() < 0) - /* force to the mode value */ - xsnprintf(buf, sizeof(buf), "0%o", -get_shared_repository()); - else if (get_shared_repository() == PERM_GROUP) - xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_GROUP); - else if (get_shared_repository() == PERM_EVERYBODY) - xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY); - else - BUG("invalid value for shared_repository"); - git_config_set("core.sharedrepository", buf); - git_config_set("receive.denyNonFastforwards", "true"); - } - - if (!(flags & INIT_DB_QUIET)) { - int len = strlen(git_dir); - - if (reinit) - printf(get_shared_repository() - ? _("Reinitialized existing shared Git repository in %s%s\n") - : _("Reinitialized existing Git repository in %s%s\n"), - git_dir, len && git_dir[len-1] != '/' ? "/" : ""); - else - printf(get_shared_repository() - ? _("Initialized empty shared Git repository in %s%s\n") - : _("Initialized empty Git repository in %s%s\n"), - git_dir, len && git_dir[len-1] != '/' ? "/" : ""); - } - - free(original_git_dir); - return 0; -} - -static int guess_repository_type(const char *git_dir) -{ - const char *slash; - char *cwd; - int cwd_is_git_dir; - - /* - * "GIT_DIR=. git init" is always bare. - * "GIT_DIR=`pwd` git init" too. - */ - if (!strcmp(".", git_dir)) - return 1; - cwd = xgetcwd(); - cwd_is_git_dir = !strcmp(git_dir, cwd); - free(cwd); - if (cwd_is_git_dir) - return 1; - /* - * "GIT_DIR=.git or GIT_DIR=something/.git is usually not. - */ - if (!strcmp(git_dir, ".git")) - return 0; - slash = strrchr(git_dir, '/'); - if (slash && !strcmp(slash, "/.git")) - return 0; - - /* - * Otherwise it is often bare. At this point - * we are just guessing. - */ - return 1; -} - -static int shared_callback(const struct option *opt, const char *arg, int unset) -{ - BUG_ON_OPT_NEG(unset); - *((int *) opt->value) = (arg) ? git_config_perm("arg", arg) : PERM_GROUP; - return 0; -} - -static const char *const init_db_usage[] = { - N_("git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [<directory>]"), - NULL -}; - -/* - * If you want to, you can share the DB area with any number of branches. - * That has advantages: you can save space by sharing all the SHA1 objects. - * On the other hand, it might just make lookup slower and messier. You - * be the judge. The default case is to have one DB per managed directory. - */ -int cmd_init_db(int argc, const char **argv, const char *prefix) -{ - const char *git_dir; - const char *real_git_dir = NULL; - const char *work_tree; - const char *template_dir = NULL; - unsigned int flags = 0; - const char *object_format = NULL; - const char *initial_branch = NULL; - int hash_algo = GIT_HASH_UNKNOWN; - const struct option init_db_options[] = { - OPT_STRING(0, "template", &template_dir, N_("template-directory"), - N_("directory from which templates will be used")), - OPT_SET_INT(0, "bare", &is_bare_repository_cfg, - N_("create a bare repository"), 1), - { OPTION_CALLBACK, 0, "shared", &init_shared_repository, - N_("permissions"), - N_("specify that the git repository is to be shared amongst several users"), - PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0}, - OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET), - OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), - N_("separate git dir from working tree")), - OPT_STRING('b', "initial-branch", &initial_branch, N_("name"), - N_("override the name of the initial branch")), - OPT_STRING(0, "object-format", &object_format, N_("hash"), - N_("specify the hash algorithm to use")), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0); - - if (real_git_dir && is_bare_repository_cfg == 1) - die(_("--separate-git-dir and --bare are mutually exclusive")); - - if (real_git_dir && !is_absolute_path(real_git_dir)) - real_git_dir = real_pathdup(real_git_dir, 1); - - if (template_dir && *template_dir && !is_absolute_path(template_dir)) - template_dir = absolute_pathdup(template_dir); - - if (argc == 1) { - int mkdir_tried = 0; - retry: - if (chdir(argv[0]) < 0) { - if (!mkdir_tried) { - int saved; - /* - * At this point we haven't read any configuration, - * and we know shared_repository should always be 0; - * but just in case we play safe. - */ - saved = get_shared_repository(); - set_shared_repository(0); - switch (safe_create_leading_directories_const(argv[0])) { - case SCLD_OK: - case SCLD_PERMS: - break; - case SCLD_EXISTS: - errno = EEXIST; - /* fallthru */ - default: - die_errno(_("cannot mkdir %s"), argv[0]); - break; - } - set_shared_repository(saved); - if (mkdir(argv[0], 0777) < 0) - die_errno(_("cannot mkdir %s"), argv[0]); - mkdir_tried = 1; - goto retry; - } - die_errno(_("cannot chdir to %s"), argv[0]); - } - } else if (0 < argc) { - usage(init_db_usage[0]); - } - if (is_bare_repository_cfg == 1) { - char *cwd = xgetcwd(); - setenv(GIT_DIR_ENVIRONMENT, cwd, argc > 0); - free(cwd); - } - - if (object_format) { - hash_algo = hash_algo_by_name(object_format); - if (hash_algo == GIT_HASH_UNKNOWN) - die(_("unknown hash algorithm '%s'"), object_format); - } - - if (init_shared_repository != -1) - set_shared_repository(init_shared_repository); - - /* - * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR - * without --bare. Catch the error early. - */ - git_dir = xstrdup_or_null(getenv(GIT_DIR_ENVIRONMENT)); - work_tree = xstrdup_or_null(getenv(GIT_WORK_TREE_ENVIRONMENT)); - if ((!git_dir || is_bare_repository_cfg == 1) && work_tree) - die(_("%s (or --work-tree=<directory>) not allowed without " - "specifying %s (or --git-dir=<directory>)"), - GIT_WORK_TREE_ENVIRONMENT, - GIT_DIR_ENVIRONMENT); - - /* - * Set up the default .git directory contents - */ - if (!git_dir) - git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; - - /* - * When --separate-git-dir is used inside a linked worktree, take - * care to ensure that the common .git/ directory is relocated, not - * the worktree-specific .git/worktrees/<id>/ directory. - */ - if (real_git_dir) { - int err; - const char *p; - struct strbuf sb = STRBUF_INIT; - - p = read_gitfile_gently(git_dir, &err); - if (p && get_common_dir(&sb, p)) { - struct strbuf mainwt = STRBUF_INIT; - - strbuf_addbuf(&mainwt, &sb); - strbuf_strip_suffix(&mainwt, "/.git"); - if (chdir(mainwt.buf) < 0) - die_errno(_("cannot chdir to %s"), mainwt.buf); - strbuf_release(&mainwt); - git_dir = strbuf_detach(&sb, NULL); - } - strbuf_release(&sb); - } - - if (is_bare_repository_cfg < 0) - is_bare_repository_cfg = guess_repository_type(git_dir); - - if (!is_bare_repository_cfg) { - const char *git_dir_parent = strrchr(git_dir, '/'); - if (git_dir_parent) { - char *rel = xstrndup(git_dir, git_dir_parent - git_dir); - git_work_tree_cfg = real_pathdup(rel, 1); - free(rel); - } - if (!git_work_tree_cfg) - git_work_tree_cfg = xgetcwd(); - if (work_tree) - set_git_work_tree(work_tree); - else - set_git_work_tree(git_work_tree_cfg); - if (access(get_git_work_tree(), X_OK)) - die_errno (_("Cannot access work tree '%s'"), - get_git_work_tree()); - } - else { - if (real_git_dir) - die(_("--separate-git-dir incompatible with bare repository")); - if (work_tree) - set_git_work_tree(work_tree); - } - - UNLEAK(real_git_dir); - UNLEAK(git_dir); - UNLEAK(work_tree); - - flags |= INIT_DB_EXIST_OK; - return init_db(git_dir, real_git_dir, template_dir, hash_algo, - initial_branch, flags); -} diff --git a/third_party/git/builtin/interpret-trailers.c b/third_party/git/builtin/interpret-trailers.c deleted file mode 100644 index 84748eafc01b..000000000000 --- a/third_party/git/builtin/interpret-trailers.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Builtin "git interpret-trailers" - * - * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org> - * - */ - -#include "cache.h" -#include "builtin.h" -#include "parse-options.h" -#include "string-list.h" -#include "trailer.h" -#include "config.h" - -static const char * const git_interpret_trailers_usage[] = { - N_("git interpret-trailers [--in-place] [--trim-empty] [(--trailer <token>[(=|:)<value>])...] [<file>...]"), - NULL -}; - -static enum trailer_where where; -static enum trailer_if_exists if_exists; -static enum trailer_if_missing if_missing; - -static int option_parse_where(const struct option *opt, - const char *arg, int unset) -{ - return trailer_set_where(&where, arg); -} - -static int option_parse_if_exists(const struct option *opt, - const char *arg, int unset) -{ - return trailer_set_if_exists(&if_exists, arg); -} - -static int option_parse_if_missing(const struct option *opt, - const char *arg, int unset) -{ - return trailer_set_if_missing(&if_missing, arg); -} - -static void new_trailers_clear(struct list_head *trailers) -{ - struct list_head *pos, *tmp; - struct new_trailer_item *item; - - list_for_each_safe(pos, tmp, trailers) { - item = list_entry(pos, struct new_trailer_item, list); - list_del(pos); - free(item); - } -} - -static int option_parse_trailer(const struct option *opt, - const char *arg, int unset) -{ - struct list_head *trailers = opt->value; - struct new_trailer_item *item; - - if (unset) { - new_trailers_clear(trailers); - return 0; - } - - if (!arg) - return -1; - - item = xmalloc(sizeof(*item)); - item->text = arg; - item->where = where; - item->if_exists = if_exists; - item->if_missing = if_missing; - list_add_tail(&item->list, trailers); - return 0; -} - -static int parse_opt_parse(const struct option *opt, const char *arg, - int unset) -{ - struct process_trailer_options *v = opt->value; - v->only_trailers = 1; - v->only_input = 1; - v->unfold = 1; - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - return 0; -} - -int cmd_interpret_trailers(int argc, const char **argv, const char *prefix) -{ - struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT; - LIST_HEAD(trailers); - - struct option options[] = { - OPT_BOOL(0, "in-place", &opts.in_place, N_("edit files in place")), - OPT_BOOL(0, "trim-empty", &opts.trim_empty, N_("trim empty trailers")), - - OPT_CALLBACK(0, "where", NULL, N_("action"), - N_("where to place the new trailer"), option_parse_where), - OPT_CALLBACK(0, "if-exists", NULL, N_("action"), - N_("action if trailer already exists"), option_parse_if_exists), - OPT_CALLBACK(0, "if-missing", NULL, N_("action"), - N_("action if trailer is missing"), option_parse_if_missing), - - OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")), - OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply config rules")), - OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")), - OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("set parsing options"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse), - OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat --- specially")), - OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"), - N_("trailer(s) to add"), option_parse_trailer), - OPT_END() - }; - - git_config(git_default_config, NULL); - - argc = parse_options(argc, argv, prefix, options, - git_interpret_trailers_usage, 0); - - if (opts.only_input && !list_empty(&trailers)) - usage_msg_opt( - _("--trailer with --only-input does not make sense"), - git_interpret_trailers_usage, - options); - - if (argc) { - int i; - for (i = 0; i < argc; i++) - process_trailers(argv[i], &opts, &trailers); - } else { - if (opts.in_place) - die(_("no input file given for in-place editing")); - process_trailers(NULL, &opts, &trailers); - } - - new_trailers_clear(&trailers); - - return 0; -} diff --git a/third_party/git/builtin/log.c b/third_party/git/builtin/log.c deleted file mode 100644 index 0a7ed4bef92b..000000000000 --- a/third_party/git/builtin/log.c +++ /dev/null @@ -1,2336 +0,0 @@ -/* - * Builtin "git log" and related commands (show, whatchanged) - * - * (C) Copyright 2006 Linus Torvalds - * 2006 Junio Hamano - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "refs.h" -#include "object-store.h" -#include "color.h" -#include "commit.h" -#include "diff.h" -#include "revision.h" -#include "log-tree.h" -#include "builtin.h" -#include "tag.h" -#include "reflog-walk.h" -#include "patch-ids.h" -#include "run-command.h" -#include "shortlog.h" -#include "remote.h" -#include "string-list.h" -#include "parse-options.h" -#include "line-log.h" -#include "branch.h" -#include "streaming.h" -#include "version.h" -#include "mailmap.h" -#include "gpg-interface.h" -#include "progress.h" -#include "commit-slab.h" -#include "repository.h" -#include "commit-reach.h" -#include "range-diff.h" - -#define MAIL_DEFAULT_WRAP 72 -#define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100 - -/* Set a default date-time format for git log ("log.date" config variable) */ -static const char *default_date_mode = NULL; - -static int default_abbrev_commit; -static int default_show_root = 1; -static int default_follow; -static int default_show_signature; -static int default_encode_email_headers = 1; -static int decoration_style; -static int decoration_given; -static int use_mailmap_config = 1; -static const char *fmt_patch_subject_prefix = "PATCH"; -static const char *fmt_pretty; - -static const char * const builtin_log_usage[] = { - N_("git log [<options>] [<revision-range>] [[--] <path>...]"), - N_("git show [<options>] <object>..."), - NULL -}; - -struct line_opt_callback_data { - struct rev_info *rev; - const char *prefix; - struct string_list args; -}; - -static int session_is_interactive(void) -{ - return isatty(1) || pager_in_use(); -} - -static int auto_decoration_style(void) -{ - return session_is_interactive() ? DECORATE_SHORT_REFS : 0; -} - -static int parse_decoration_style(const char *value) -{ - switch (git_parse_maybe_bool(value)) { - case 1: - return DECORATE_SHORT_REFS; - case 0: - return 0; - default: - break; - } - if (!strcmp(value, "full")) - return DECORATE_FULL_REFS; - else if (!strcmp(value, "short")) - return DECORATE_SHORT_REFS; - else if (!strcmp(value, "auto")) - return auto_decoration_style(); - /* - * Please update _git_log() in git-completion.bash when you - * add new decoration styles. - */ - return -1; -} - -static int decorate_callback(const struct option *opt, const char *arg, int unset) -{ - if (unset) - decoration_style = 0; - else if (arg) - decoration_style = parse_decoration_style(arg); - else - decoration_style = DECORATE_SHORT_REFS; - - if (decoration_style < 0) - die(_("invalid --decorate option: %s"), arg); - - decoration_given = 1; - - return 0; -} - -static int log_line_range_callback(const struct option *option, const char *arg, int unset) -{ - struct line_opt_callback_data *data = option->value; - - BUG_ON_OPT_NEG(unset); - - if (!arg) - return -1; - - data->rev->line_level_traverse = 1; - string_list_append(&data->args, arg); - - return 0; -} - -static void init_log_defaults(void) -{ - init_grep_defaults(the_repository); - init_diff_ui_defaults(); - - decoration_style = auto_decoration_style(); -} - -static void cmd_log_init_defaults(struct rev_info *rev) -{ - if (fmt_pretty) - get_commit_format(fmt_pretty, rev); - if (default_follow) - rev->diffopt.flags.default_follow_renames = 1; - rev->verbose_header = 1; - rev->diffopt.flags.recursive = 1; - rev->diffopt.stat_width = -1; /* use full terminal width */ - rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */ - rev->abbrev_commit = default_abbrev_commit; - rev->show_root_diff = default_show_root; - rev->subject_prefix = fmt_patch_subject_prefix; - rev->show_signature = default_show_signature; - rev->encode_email_headers = default_encode_email_headers; - rev->diffopt.flags.allow_textconv = 1; - - if (default_date_mode) - parse_date_format(default_date_mode, &rev->date_mode); -} - -static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, - struct rev_info *rev, struct setup_revision_opt *opt) -{ - struct userformat_want w; - int quiet = 0, source = 0, mailmap; - static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP}; - static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP; - static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP; - static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP; - struct decoration_filter decoration_filter = {&decorate_refs_include, - &decorate_refs_exclude, - &decorate_refs_exclude_config}; - static struct revision_sources revision_sources; - - const struct option builtin_log_options[] = { - OPT__QUIET(&quiet, N_("suppress diff output")), - OPT_BOOL(0, "source", &source, N_("show source")), - OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")), - OPT_ALIAS(0, "mailmap", "use-mailmap"), - OPT_STRING_LIST(0, "decorate-refs", &decorate_refs_include, - N_("pattern"), N_("only decorate refs that match <pattern>")), - OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude, - N_("pattern"), N_("do not decorate refs that match <pattern>")), - OPT_CALLBACK_F(0, "decorate", NULL, NULL, N_("decorate options"), - PARSE_OPT_OPTARG, decorate_callback), - OPT_CALLBACK('L', NULL, &line_cb, "n,m:file", - N_("Process line range n,m in file, counting from 1"), - log_line_range_callback), - OPT_END() - }; - - line_cb.rev = rev; - line_cb.prefix = prefix; - - mailmap = use_mailmap_config; - argc = parse_options(argc, argv, prefix, - builtin_log_options, builtin_log_usage, - PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | - PARSE_OPT_KEEP_DASHDASH); - - if (quiet) - rev->diffopt.output_format |= DIFF_FORMAT_NO_OUTPUT; - argc = setup_revisions(argc, argv, rev, opt); - - /* Any arguments at this point are not recognized */ - if (argc > 1) - die(_("unrecognized argument: %s"), argv[1]); - - memset(&w, 0, sizeof(w)); - userformat_find_requirements(NULL, &w); - - if (!rev->show_notes_given && (!rev->pretty_given || w.notes)) - rev->show_notes = 1; - if (rev->show_notes) - load_display_notes(&rev->notes_opt); - - if ((rev->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) || - rev->diffopt.filter || rev->diffopt.flags.follow_renames) - rev->always_show_header = 0; - - if (source || w.source) { - init_revision_sources(&revision_sources); - rev->sources = &revision_sources; - } - - if (mailmap) { - rev->mailmap = xcalloc(1, sizeof(struct string_list)); - read_mailmap(rev->mailmap, NULL); - } - - if (rev->pretty_given && rev->commit_format == CMIT_FMT_RAW) { - /* - * "log --pretty=raw" is special; ignore UI oriented - * configuration variables such as decoration. - */ - if (!decoration_given) - decoration_style = 0; - if (!rev->abbrev_commit_given) - rev->abbrev_commit = 0; - } - - if (decoration_style) { - const struct string_list *config_exclude = - repo_config_get_value_multi(the_repository, - "log.excludeDecoration"); - - if (config_exclude) { - struct string_list_item *item; - for_each_string_list_item(item, config_exclude) - string_list_append(&decorate_refs_exclude_config, - item->string); - } - - rev->show_decorations = 1; - - load_ref_decorations(&decoration_filter, decoration_style); - } - - if (rev->line_level_traverse) - line_log_init(rev, line_cb.prefix, &line_cb.args); - - setup_pager(); -} - -static void cmd_log_init(int argc, const char **argv, const char *prefix, - struct rev_info *rev, struct setup_revision_opt *opt) -{ - cmd_log_init_defaults(rev); - cmd_log_init_finish(argc, argv, prefix, rev, opt); -} - -/* - * This gives a rough estimate for how many commits we - * will print out in the list. - */ -static int estimate_commit_count(struct commit_list *list) -{ - int n = 0; - - while (list) { - struct commit *commit = list->item; - unsigned int flags = commit->object.flags; - list = list->next; - if (!(flags & (TREESAME | UNINTERESTING))) - n++; - } - return n; -} - -static void show_early_header(struct rev_info *rev, const char *stage, int nr) -{ - if (rev->shown_one) { - rev->shown_one = 0; - if (rev->commit_format != CMIT_FMT_ONELINE) - putchar(rev->diffopt.line_termination); - } - fprintf(rev->diffopt.file, _("Final output: %d %s\n"), nr, stage); -} - -static struct itimerval early_output_timer; - -static void log_show_early(struct rev_info *revs, struct commit_list *list) -{ - int i = revs->early_output, close_file = revs->diffopt.close_file; - int show_header = 1; - - revs->diffopt.close_file = 0; - sort_in_topological_order(&list, revs->sort_order); - while (list && i) { - struct commit *commit = list->item; - switch (simplify_commit(revs, commit)) { - case commit_show: - if (show_header) { - int n = estimate_commit_count(list); - show_early_header(revs, "incomplete", n); - show_header = 0; - } - log_tree_commit(revs, commit); - i--; - break; - case commit_ignore: - break; - case commit_error: - if (close_file) - fclose(revs->diffopt.file); - return; - } - list = list->next; - } - - /* Did we already get enough commits for the early output? */ - if (!i) { - if (close_file) - fclose(revs->diffopt.file); - return; - } - - /* - * ..if no, then repeat it twice a second until we - * do. - * - * NOTE! We don't use "it_interval", because if the - * reader isn't listening, we want our output to be - * throttled by the writing, and not have the timer - * trigger every second even if we're blocked on a - * reader! - */ - early_output_timer.it_value.tv_sec = 0; - early_output_timer.it_value.tv_usec = 500000; - setitimer(ITIMER_REAL, &early_output_timer, NULL); -} - -static void early_output(int signal) -{ - show_early_output = log_show_early; -} - -static void setup_early_output(void) -{ - struct sigaction sa; - - /* - * Set up the signal handler, minimally intrusively: - * we only set a single volatile integer word (not - * using sigatomic_t - trying to avoid unnecessary - * system dependencies and headers), and using - * SA_RESTART. - */ - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = early_output; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - sigaction(SIGALRM, &sa, NULL); - - /* - * If we can get the whole output in less than a - * tenth of a second, don't even bother doing the - * early-output thing.. - * - * This is a one-time-only trigger. - */ - early_output_timer.it_value.tv_sec = 0; - early_output_timer.it_value.tv_usec = 100000; - setitimer(ITIMER_REAL, &early_output_timer, NULL); -} - -static void finish_early_output(struct rev_info *rev) -{ - int n = estimate_commit_count(rev->commits); - signal(SIGALRM, SIG_IGN); - show_early_header(rev, "done", n); -} - -static int cmd_log_walk(struct rev_info *rev) -{ - struct commit *commit; - int saved_nrl = 0; - int saved_dcctc = 0, close_file = rev->diffopt.close_file; - - if (rev->early_output) - setup_early_output(); - - if (prepare_revision_walk(rev)) - die(_("revision walk setup failed")); - - if (rev->early_output) - finish_early_output(rev); - - /* - * For --check and --exit-code, the exit code is based on CHECK_FAILED - * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to - * retain that state information if replacing rev->diffopt in this loop - */ - rev->diffopt.close_file = 0; - while ((commit = get_revision(rev)) != NULL) { - if (!log_tree_commit(rev, commit) && rev->max_count >= 0) - /* - * We decremented max_count in get_revision, - * but we didn't actually show the commit. - */ - rev->max_count++; - if (!rev->reflog_info) { - /* - * We may show a given commit multiple times when - * walking the reflogs. - */ - free_commit_buffer(the_repository->parsed_objects, - commit); - free_commit_list(commit->parents); - commit->parents = NULL; - } - if (saved_nrl < rev->diffopt.needed_rename_limit) - saved_nrl = rev->diffopt.needed_rename_limit; - if (rev->diffopt.degraded_cc_to_c) - saved_dcctc = 1; - } - rev->diffopt.degraded_cc_to_c = saved_dcctc; - rev->diffopt.needed_rename_limit = saved_nrl; - if (close_file) - fclose(rev->diffopt.file); - - if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF && - rev->diffopt.flags.check_failed) { - return 02; - } - return diff_result_code(&rev->diffopt, 0); -} - -static int git_log_config(const char *var, const char *value, void *cb) -{ - const char *slot_name; - - if (!strcmp(var, "format.pretty")) - return git_config_string(&fmt_pretty, var, value); - if (!strcmp(var, "format.subjectprefix")) - return git_config_string(&fmt_patch_subject_prefix, var, value); - if (!strcmp(var, "format.encodeemailheaders")) { - default_encode_email_headers = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "log.abbrevcommit")) { - default_abbrev_commit = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "log.date")) - return git_config_string(&default_date_mode, var, value); - if (!strcmp(var, "log.decorate")) { - decoration_style = parse_decoration_style(value); - if (decoration_style < 0) - decoration_style = 0; /* maybe warn? */ - return 0; - } - if (!strcmp(var, "log.showroot")) { - default_show_root = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "log.follow")) { - default_follow = git_config_bool(var, value); - return 0; - } - if (skip_prefix(var, "color.decorate.", &slot_name)) - return parse_decorate_color_config(var, slot_name, value); - if (!strcmp(var, "log.mailmap")) { - use_mailmap_config = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "log.showsignature")) { - default_show_signature = git_config_bool(var, value); - return 0; - } - - if (grep_config(var, value, cb) < 0) - return -1; - if (git_gpg_config(var, value, cb) < 0) - return -1; - return git_diff_ui_config(var, value, cb); -} - -int cmd_whatchanged(int argc, const char **argv, const char *prefix) -{ - struct rev_info rev; - struct setup_revision_opt opt; - - init_log_defaults(); - git_config(git_log_config, NULL); - - repo_init_revisions(the_repository, &rev, prefix); - rev.diff = 1; - rev.simplify_history = 0; - memset(&opt, 0, sizeof(opt)); - opt.def = "HEAD"; - opt.revarg_opt = REVARG_COMMITTISH; - cmd_log_init(argc, argv, prefix, &rev, &opt); - if (!rev.diffopt.output_format) - rev.diffopt.output_format = DIFF_FORMAT_RAW; - return cmd_log_walk(&rev); -} - -static void show_tagger(const char *buf, struct rev_info *rev) -{ - struct strbuf out = STRBUF_INIT; - struct pretty_print_context pp = {0}; - - pp.fmt = rev->commit_format; - pp.date_mode = rev->date_mode; - pp_user_info(&pp, "Tagger", &out, buf, get_log_output_encoding()); - fprintf(rev->diffopt.file, "%s", out.buf); - strbuf_release(&out); -} - -static int show_blob_object(const struct object_id *oid, struct rev_info *rev, const char *obj_name) -{ - struct object_id oidc; - struct object_context obj_context; - char *buf; - unsigned long size; - - fflush(rev->diffopt.file); - if (!rev->diffopt.flags.textconv_set_via_cmdline || - !rev->diffopt.flags.allow_textconv) - return stream_blob_to_fd(1, oid, NULL, 0); - - if (get_oid_with_context(the_repository, obj_name, - GET_OID_RECORD_PATH, - &oidc, &obj_context)) - die(_("not a valid object name %s"), obj_name); - if (!obj_context.path || - !textconv_object(the_repository, obj_context.path, - obj_context.mode, &oidc, 1, &buf, &size)) { - free(obj_context.path); - return stream_blob_to_fd(1, oid, NULL, 0); - } - - if (!buf) - die(_("git show %s: bad file"), obj_name); - - write_or_die(1, buf, size); - free(obj_context.path); - return 0; -} - -static int show_tag_object(const struct object_id *oid, struct rev_info *rev) -{ - unsigned long size; - enum object_type type; - char *buf = read_object_file(oid, &type, &size); - int offset = 0; - - if (!buf) - return error(_("could not read object %s"), oid_to_hex(oid)); - - assert(type == OBJ_TAG); - while (offset < size && buf[offset] != '\n') { - int new_offset = offset + 1; - const char *ident; - while (new_offset < size && buf[new_offset++] != '\n') - ; /* do nothing */ - if (skip_prefix(buf + offset, "tagger ", &ident)) - show_tagger(ident, rev); - offset = new_offset; - } - - if (offset < size) - fwrite(buf + offset, size - offset, 1, rev->diffopt.file); - free(buf); - return 0; -} - -static int show_tree_object(const struct object_id *oid, - struct strbuf *base, - const char *pathname, unsigned mode, int stage, void *context) -{ - FILE *file = context; - fprintf(file, "%s%s\n", pathname, S_ISDIR(mode) ? "/" : ""); - return 0; -} - -static void show_setup_revisions_tweak(struct rev_info *rev, - struct setup_revision_opt *opt) -{ - if (rev->ignore_merges < 0) { - /* There was no "-m" variant on the command line */ - rev->ignore_merges = 0; - if (!rev->first_parent_only && !rev->combine_merges) { - /* No "--first-parent", "-c", or "--cc" */ - rev->combine_merges = 1; - rev->dense_combined_merges = 1; - } - } - if (!rev->diffopt.output_format) - rev->diffopt.output_format = DIFF_FORMAT_PATCH; -} - -int cmd_show(int argc, const char **argv, const char *prefix) -{ - struct rev_info rev; - struct object_array_entry *objects; - struct setup_revision_opt opt; - struct pathspec match_all; - int i, count, ret = 0; - - init_log_defaults(); - git_config(git_log_config, NULL); - - memset(&match_all, 0, sizeof(match_all)); - repo_init_revisions(the_repository, &rev, prefix); - rev.diff = 1; - rev.always_show_header = 1; - rev.no_walk = REVISION_WALK_NO_WALK_SORTED; - rev.diffopt.stat_width = -1; /* Scale to real terminal size */ - - memset(&opt, 0, sizeof(opt)); - opt.def = "HEAD"; - opt.tweak = show_setup_revisions_tweak; - cmd_log_init(argc, argv, prefix, &rev, &opt); - - if (!rev.no_walk) - return cmd_log_walk(&rev); - - count = rev.pending.nr; - objects = rev.pending.objects; - for (i = 0; i < count && !ret; i++) { - struct object *o = objects[i].item; - const char *name = objects[i].name; - switch (o->type) { - case OBJ_BLOB: - ret = show_blob_object(&o->oid, &rev, name); - break; - case OBJ_TAG: { - struct tag *t = (struct tag *)o; - struct object_id *oid = get_tagged_oid(t); - - if (rev.shown_one) - putchar('\n'); - fprintf(rev.diffopt.file, "%stag %s%s\n", - diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), - t->tag, - diff_get_color_opt(&rev.diffopt, DIFF_RESET)); - ret = show_tag_object(&o->oid, &rev); - rev.shown_one = 1; - if (ret) - break; - o = parse_object(the_repository, oid); - if (!o) - ret = error(_("could not read object %s"), - oid_to_hex(oid)); - objects[i].item = o; - i--; - break; - } - case OBJ_TREE: - if (rev.shown_one) - putchar('\n'); - fprintf(rev.diffopt.file, "%stree %s%s\n\n", - diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), - name, - diff_get_color_opt(&rev.diffopt, DIFF_RESET)); - read_tree_recursive(the_repository, (struct tree *)o, "", - 0, 0, &match_all, show_tree_object, - rev.diffopt.file); - rev.shown_one = 1; - break; - case OBJ_COMMIT: - rev.pending.nr = rev.pending.alloc = 0; - rev.pending.objects = NULL; - add_object_array(o, name, &rev.pending); - ret = cmd_log_walk(&rev); - break; - default: - ret = error(_("unknown type: %d"), o->type); - } - } - free(objects); - return ret; -} - -/* - * This is equivalent to "git log -g --abbrev-commit --pretty=oneline" - */ -int cmd_log_reflog(int argc, const char **argv, const char *prefix) -{ - struct rev_info rev; - struct setup_revision_opt opt; - - init_log_defaults(); - git_config(git_log_config, NULL); - - repo_init_revisions(the_repository, &rev, prefix); - init_reflog_walk(&rev.reflog_info); - rev.verbose_header = 1; - memset(&opt, 0, sizeof(opt)); - opt.def = "HEAD"; - cmd_log_init_defaults(&rev); - rev.abbrev_commit = 1; - rev.commit_format = CMIT_FMT_ONELINE; - rev.use_terminator = 1; - rev.always_show_header = 1; - cmd_log_init_finish(argc, argv, prefix, &rev, &opt); - - return cmd_log_walk(&rev); -} - -static void log_setup_revisions_tweak(struct rev_info *rev, - struct setup_revision_opt *opt) -{ - if (rev->diffopt.flags.default_follow_renames && - rev->prune_data.nr == 1) - rev->diffopt.flags.follow_renames = 1; - - /* Turn --cc/-c into -p --cc/-c when -p was not given */ - if (!rev->diffopt.output_format && rev->combine_merges) - rev->diffopt.output_format = DIFF_FORMAT_PATCH; - - if (rev->first_parent_only && rev->ignore_merges < 0) - rev->ignore_merges = 0; -} - -int cmd_log(int argc, const char **argv, const char *prefix) -{ - struct rev_info rev; - struct setup_revision_opt opt; - - init_log_defaults(); - git_config(git_log_config, NULL); - - repo_init_revisions(the_repository, &rev, prefix); - rev.always_show_header = 1; - memset(&opt, 0, sizeof(opt)); - opt.def = "HEAD"; - opt.revarg_opt = REVARG_COMMITTISH; - opt.tweak = log_setup_revisions_tweak; - cmd_log_init(argc, argv, prefix, &rev, &opt); - return cmd_log_walk(&rev); -} - -/* format-patch */ - -static const char *fmt_patch_suffix = ".patch"; -static int numbered = 0; -static int auto_number = 1; - -static char *default_attach = NULL; - -static struct string_list extra_hdr = STRING_LIST_INIT_NODUP; -static struct string_list extra_to = STRING_LIST_INIT_NODUP; -static struct string_list extra_cc = STRING_LIST_INIT_NODUP; - -static void add_header(const char *value) -{ - struct string_list_item *item; - int len = strlen(value); - while (len && value[len - 1] == '\n') - len--; - - if (!strncasecmp(value, "to: ", 4)) { - item = string_list_append(&extra_to, value + 4); - len -= 4; - } else if (!strncasecmp(value, "cc: ", 4)) { - item = string_list_append(&extra_cc, value + 4); - len -= 4; - } else { - item = string_list_append(&extra_hdr, value); - } - - item->string[len] = '\0'; -} - -enum cover_setting { - COVER_UNSET, - COVER_OFF, - COVER_ON, - COVER_AUTO -}; - -enum thread_level { - THREAD_UNSET, - THREAD_SHALLOW, - THREAD_DEEP -}; - -enum cover_from_description { - COVER_FROM_NONE, - COVER_FROM_MESSAGE, - COVER_FROM_SUBJECT, - COVER_FROM_AUTO -}; - -enum auto_base_setting { - AUTO_BASE_NEVER, - AUTO_BASE_ALWAYS, - AUTO_BASE_WHEN_ABLE -}; - -static enum thread_level thread; -static int do_signoff; -static enum auto_base_setting auto_base; -static char *from; -static const char *signature = git_version_string; -static const char *signature_file; -static enum cover_setting config_cover_letter; -static const char *config_output_directory; -static enum cover_from_description cover_from_description_mode = COVER_FROM_MESSAGE; -static int show_notes; -static struct display_notes_opt notes_opt; - -static enum cover_from_description parse_cover_from_description(const char *arg) -{ - if (!arg || !strcmp(arg, "default")) - return COVER_FROM_MESSAGE; - else if (!strcmp(arg, "none")) - return COVER_FROM_NONE; - else if (!strcmp(arg, "message")) - return COVER_FROM_MESSAGE; - else if (!strcmp(arg, "subject")) - return COVER_FROM_SUBJECT; - else if (!strcmp(arg, "auto")) - return COVER_FROM_AUTO; - else - die(_("%s: invalid cover from description mode"), arg); -} - -static int git_format_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "format.headers")) { - if (!value) - die(_("format.headers without value")); - add_header(value); - return 0; - } - if (!strcmp(var, "format.suffix")) - return git_config_string(&fmt_patch_suffix, var, value); - if (!strcmp(var, "format.to")) { - if (!value) - return config_error_nonbool(var); - string_list_append(&extra_to, value); - return 0; - } - if (!strcmp(var, "format.cc")) { - if (!value) - return config_error_nonbool(var); - string_list_append(&extra_cc, value); - return 0; - } - if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff") || - !strcmp(var, "color.ui") || !strcmp(var, "diff.submodule")) { - return 0; - } - if (!strcmp(var, "format.numbered")) { - if (value && !strcasecmp(value, "auto")) { - auto_number = 1; - return 0; - } - numbered = git_config_bool(var, value); - auto_number = auto_number && numbered; - return 0; - } - if (!strcmp(var, "format.attach")) { - if (value && *value) - default_attach = xstrdup(value); - else - default_attach = xstrdup(git_version_string); - return 0; - } - if (!strcmp(var, "format.thread")) { - if (value && !strcasecmp(value, "deep")) { - thread = THREAD_DEEP; - return 0; - } - if (value && !strcasecmp(value, "shallow")) { - thread = THREAD_SHALLOW; - return 0; - } - thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET; - return 0; - } - if (!strcmp(var, "format.signoff")) { - do_signoff = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "format.signature")) - return git_config_string(&signature, var, value); - if (!strcmp(var, "format.signaturefile")) - return git_config_pathname(&signature_file, var, value); - if (!strcmp(var, "format.coverletter")) { - if (value && !strcasecmp(value, "auto")) { - config_cover_letter = COVER_AUTO; - return 0; - } - config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF; - return 0; - } - if (!strcmp(var, "format.outputdirectory")) - return git_config_string(&config_output_directory, var, value); - if (!strcmp(var, "format.useautobase")) { - if (value && !strcasecmp(value, "whenAble")) { - auto_base = AUTO_BASE_WHEN_ABLE; - return 0; - } - auto_base = git_config_bool(var, value) ? AUTO_BASE_ALWAYS : AUTO_BASE_NEVER; - return 0; - } - if (!strcmp(var, "format.from")) { - int b = git_parse_maybe_bool(value); - free(from); - if (b < 0) - from = xstrdup(value); - else if (b) - from = xstrdup(git_committer_info(IDENT_NO_DATE)); - else - from = NULL; - return 0; - } - if (!strcmp(var, "format.notes")) { - int b = git_parse_maybe_bool(value); - if (b < 0) - enable_ref_display_notes(¬es_opt, &show_notes, value); - else if (b) - enable_default_display_notes(¬es_opt, &show_notes); - else - disable_display_notes(¬es_opt, &show_notes); - return 0; - } - if (!strcmp(var, "format.coverfromdescription")) { - cover_from_description_mode = parse_cover_from_description(value); - return 0; - } - - return git_log_config(var, value, cb); -} - -static const char *output_directory = NULL; -static int outdir_offset; - -static int open_next_file(struct commit *commit, const char *subject, - struct rev_info *rev, int quiet) -{ - struct strbuf filename = STRBUF_INIT; - int suffix_len = strlen(rev->patch_suffix) + 1; - - if (output_directory) { - strbuf_addstr(&filename, output_directory); - if (filename.len >= - PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len) { - strbuf_release(&filename); - return error(_("name of output directory is too long")); - } - strbuf_complete(&filename, '/'); - } - - if (rev->numbered_files) - strbuf_addf(&filename, "%d", rev->nr); - else if (commit) - fmt_output_commit(&filename, commit, rev); - else - fmt_output_subject(&filename, subject, rev); - - if (!quiet) - printf("%s\n", filename.buf + outdir_offset); - - if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL) { - error_errno(_("cannot open patch file %s"), filename.buf); - strbuf_release(&filename); - return -1; - } - - strbuf_release(&filename); - return 0; -} - -static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids) -{ - struct rev_info check_rev; - struct commit *commit, *c1, *c2; - struct object *o1, *o2; - unsigned flags1, flags2; - - if (rev->pending.nr != 2) - die(_("need exactly one range")); - - o1 = rev->pending.objects[0].item; - o2 = rev->pending.objects[1].item; - flags1 = o1->flags; - flags2 = o2->flags; - c1 = lookup_commit_reference(the_repository, &o1->oid); - c2 = lookup_commit_reference(the_repository, &o2->oid); - - if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING)) - die(_("not a range")); - - init_patch_ids(the_repository, ids); - - /* given a range a..b get all patch ids for b..a */ - repo_init_revisions(the_repository, &check_rev, rev->prefix); - check_rev.max_parents = 1; - o1->flags ^= UNINTERESTING; - o2->flags ^= UNINTERESTING; - add_pending_object(&check_rev, o1, "o1"); - add_pending_object(&check_rev, o2, "o2"); - if (prepare_revision_walk(&check_rev)) - die(_("revision walk setup failed")); - - while ((commit = get_revision(&check_rev)) != NULL) { - add_commit_patch_id(commit, ids); - } - - /* reset for next revision walk */ - clear_commit_marks(c1, SEEN | UNINTERESTING | SHOWN | ADDED); - clear_commit_marks(c2, SEEN | UNINTERESTING | SHOWN | ADDED); - o1->flags = flags1; - o2->flags = flags2; -} - -static void gen_message_id(struct rev_info *info, char *base) -{ - struct strbuf buf = STRBUF_INIT; - strbuf_addf(&buf, "%s.%"PRItime".git.%s", base, - (timestamp_t) time(NULL), - git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT)); - info->message_id = strbuf_detach(&buf, NULL); -} - -static void print_signature(FILE *file) -{ - if (!signature || !*signature) - return; - - fprintf(file, "-- \n%s", signature); - if (signature[strlen(signature)-1] != '\n') - putc('\n', file); - putc('\n', file); -} - -static char *find_branch_name(struct rev_info *rev) -{ - int i, positive = -1; - struct object_id branch_oid; - const struct object_id *tip_oid; - const char *ref, *v; - char *full_ref, *branch = NULL; - - for (i = 0; i < rev->cmdline.nr; i++) { - if (rev->cmdline.rev[i].flags & UNINTERESTING) - continue; - if (positive < 0) - positive = i; - else - return NULL; - } - if (positive < 0) - return NULL; - ref = rev->cmdline.rev[positive].name; - tip_oid = &rev->cmdline.rev[positive].item->oid; - if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref, 0) && - skip_prefix(full_ref, "refs/heads/", &v) && - oideq(tip_oid, &branch_oid)) - branch = xstrdup(v); - free(full_ref); - return branch; -} - -static void show_diffstat(struct rev_info *rev, - struct commit *origin, struct commit *head) -{ - struct diff_options opts; - - memcpy(&opts, &rev->diffopt, sizeof(opts)); - opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; - diff_setup_done(&opts); - - diff_tree_oid(get_commit_tree_oid(origin), - get_commit_tree_oid(head), - "", &opts); - diffcore_std(&opts); - diff_flush(&opts); - - fprintf(rev->diffopt.file, "\n"); -} - -static void prepare_cover_text(struct pretty_print_context *pp, - const char *branch_name, - struct strbuf *sb, - const char *encoding, - int need_8bit_cte) -{ - const char *subject = "*** SUBJECT HERE ***"; - const char *body = "*** BLURB HERE ***"; - struct strbuf description_sb = STRBUF_INIT; - struct strbuf subject_sb = STRBUF_INIT; - - if (cover_from_description_mode == COVER_FROM_NONE) - goto do_pp; - - if (branch_name && *branch_name) - read_branch_desc(&description_sb, branch_name); - if (!description_sb.len) - goto do_pp; - - if (cover_from_description_mode == COVER_FROM_SUBJECT || - cover_from_description_mode == COVER_FROM_AUTO) - body = format_subject(&subject_sb, description_sb.buf, " "); - - if (cover_from_description_mode == COVER_FROM_MESSAGE || - (cover_from_description_mode == COVER_FROM_AUTO && - subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN)) - body = description_sb.buf; - else - subject = subject_sb.buf; - -do_pp: - pp_title_line(pp, &subject, sb, encoding, need_8bit_cte); - pp_remainder(pp, &body, sb, 0); - - strbuf_release(&description_sb); - strbuf_release(&subject_sb); -} - -static int get_notes_refs(struct string_list_item *item, void *arg) -{ - strvec_pushf(arg, "--notes=%s", item->string); - return 0; -} - -static void get_notes_args(struct strvec *arg, struct rev_info *rev) -{ - if (!rev->show_notes) { - strvec_push(arg, "--no-notes"); - } else if (rev->notes_opt.use_default_notes > 0 || - (rev->notes_opt.use_default_notes == -1 && - !rev->notes_opt.extra_notes_refs.nr)) { - strvec_push(arg, "--notes"); - } else { - for_each_string_list(&rev->notes_opt.extra_notes_refs, get_notes_refs, arg); - } -} - -static void make_cover_letter(struct rev_info *rev, int use_stdout, - struct commit *origin, - int nr, struct commit **list, - const char *branch_name, - int quiet) -{ - const char *committer; - struct shortlog log; - struct strbuf sb = STRBUF_INIT; - int i; - const char *encoding = "UTF-8"; - int need_8bit_cte = 0; - struct pretty_print_context pp = {0}; - struct commit *head = list[0]; - - if (!cmit_fmt_is_mail(rev->commit_format)) - die(_("cover letter needs email format")); - - committer = git_committer_info(0); - - if (!use_stdout && - open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet)) - die(_("failed to create cover-letter file")); - - log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte, 0); - - for (i = 0; !need_8bit_cte && i < nr; i++) { - const char *buf = get_commit_buffer(list[i], NULL); - if (has_non_ascii(buf)) - need_8bit_cte = 1; - unuse_commit_buffer(list[i], buf); - } - - if (!branch_name) - branch_name = find_branch_name(rev); - - pp.fmt = CMIT_FMT_EMAIL; - pp.date_mode.type = DATE_RFC2822; - pp.rev = rev; - pp.print_email_subject = 1; - pp_user_info(&pp, NULL, &sb, committer, encoding); - prepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte); - fprintf(rev->diffopt.file, "%s\n", sb.buf); - - strbuf_release(&sb); - - shortlog_init(&log); - log.wrap_lines = 1; - log.wrap = MAIL_DEFAULT_WRAP; - log.in1 = 2; - log.in2 = 4; - log.file = rev->diffopt.file; - log.groups = SHORTLOG_GROUP_AUTHOR; - for (i = 0; i < nr; i++) - shortlog_add_commit(&log, list[i]); - - shortlog_output(&log); - - /* We can only do diffstat with a unique reference point */ - if (origin) - show_diffstat(rev, origin, head); - - if (rev->idiff_oid1) { - fprintf_ln(rev->diffopt.file, "%s", rev->idiff_title); - show_interdiff(rev->idiff_oid1, rev->idiff_oid2, 0, - &rev->diffopt); - } - - if (rev->rdiff1) { - /* - * Pass minimum required diff-options to range-diff; others - * can be added later if deemed desirable. - */ - struct diff_options opts; - struct strvec other_arg = STRVEC_INIT; - diff_setup(&opts); - opts.file = rev->diffopt.file; - opts.use_color = rev->diffopt.use_color; - diff_setup_done(&opts); - fprintf_ln(rev->diffopt.file, "%s", rev->rdiff_title); - get_notes_args(&other_arg, rev); - show_range_diff(rev->rdiff1, rev->rdiff2, - rev->creation_factor, 1, &opts, &other_arg); - strvec_clear(&other_arg); - } -} - -static const char *clean_message_id(const char *msg_id) -{ - char ch; - const char *a, *z, *m; - - m = msg_id; - while ((ch = *m) && (isspace(ch) || (ch == '<'))) - m++; - a = m; - z = NULL; - while ((ch = *m)) { - if (!isspace(ch) && (ch != '>')) - z = m; - m++; - } - if (!z) - die(_("insane in-reply-to: %s"), msg_id); - if (++z == m) - return a; - return xmemdupz(a, z - a); -} - -static const char *set_outdir(const char *prefix, const char *output_directory) -{ - if (output_directory && is_absolute_path(output_directory)) - return output_directory; - - if (!prefix || !*prefix) { - if (output_directory) - return output_directory; - /* The user did not explicitly ask for "./" */ - outdir_offset = 2; - return "./"; - } - - outdir_offset = strlen(prefix); - if (!output_directory) - return prefix; - - return prefix_filename(prefix, output_directory); -} - -static const char * const builtin_format_patch_usage[] = { - N_("git format-patch [<options>] [<since> | <revision-range>]"), - NULL -}; - -static int keep_subject = 0; - -static int keep_callback(const struct option *opt, const char *arg, int unset) -{ - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - ((struct rev_info *)opt->value)->total = -1; - keep_subject = 1; - return 0; -} - -static int subject_prefix = 0; - -static int subject_prefix_callback(const struct option *opt, const char *arg, - int unset) -{ - BUG_ON_OPT_NEG(unset); - subject_prefix = 1; - ((struct rev_info *)opt->value)->subject_prefix = arg; - return 0; -} - -static int rfc_callback(const struct option *opt, const char *arg, int unset) -{ - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - return subject_prefix_callback(opt, "RFC PATCH", unset); -} - -static int numbered_cmdline_opt = 0; - -static int numbered_callback(const struct option *opt, const char *arg, - int unset) -{ - BUG_ON_OPT_ARG(arg); - *(int *)opt->value = numbered_cmdline_opt = unset ? 0 : 1; - if (unset) - auto_number = 0; - return 0; -} - -static int no_numbered_callback(const struct option *opt, const char *arg, - int unset) -{ - BUG_ON_OPT_NEG(unset); - return numbered_callback(opt, arg, 1); -} - -static int output_directory_callback(const struct option *opt, const char *arg, - int unset) -{ - const char **dir = (const char **)opt->value; - BUG_ON_OPT_NEG(unset); - if (*dir) - die(_("two output directories?")); - *dir = arg; - return 0; -} - -static int thread_callback(const struct option *opt, const char *arg, int unset) -{ - enum thread_level *thread = (enum thread_level *)opt->value; - if (unset) - *thread = THREAD_UNSET; - else if (!arg || !strcmp(arg, "shallow")) - *thread = THREAD_SHALLOW; - else if (!strcmp(arg, "deep")) - *thread = THREAD_DEEP; - /* - * Please update _git_formatpatch() in git-completion.bash - * when you add new options. - */ - else - return 1; - return 0; -} - -static int attach_callback(const struct option *opt, const char *arg, int unset) -{ - struct rev_info *rev = (struct rev_info *)opt->value; - if (unset) - rev->mime_boundary = NULL; - else if (arg) - rev->mime_boundary = arg; - else - rev->mime_boundary = git_version_string; - rev->no_inline = unset ? 0 : 1; - return 0; -} - -static int inline_callback(const struct option *opt, const char *arg, int unset) -{ - struct rev_info *rev = (struct rev_info *)opt->value; - if (unset) - rev->mime_boundary = NULL; - else if (arg) - rev->mime_boundary = arg; - else - rev->mime_boundary = git_version_string; - rev->no_inline = 0; - return 0; -} - -static int header_callback(const struct option *opt, const char *arg, int unset) -{ - if (unset) { - string_list_clear(&extra_hdr, 0); - string_list_clear(&extra_to, 0); - string_list_clear(&extra_cc, 0); - } else { - add_header(arg); - } - return 0; -} - -static int to_callback(const struct option *opt, const char *arg, int unset) -{ - if (unset) - string_list_clear(&extra_to, 0); - else - string_list_append(&extra_to, arg); - return 0; -} - -static int cc_callback(const struct option *opt, const char *arg, int unset) -{ - if (unset) - string_list_clear(&extra_cc, 0); - else - string_list_append(&extra_cc, arg); - return 0; -} - -static int from_callback(const struct option *opt, const char *arg, int unset) -{ - char **from = opt->value; - - free(*from); - - if (unset) - *from = NULL; - else if (arg) - *from = xstrdup(arg); - else - *from = xstrdup(git_committer_info(IDENT_NO_DATE)); - return 0; -} - -static int base_callback(const struct option *opt, const char *arg, int unset) -{ - const char **base_commit = opt->value; - - if (unset) { - auto_base = AUTO_BASE_NEVER; - *base_commit = NULL; - } else if (!strcmp(arg, "auto")) { - auto_base = AUTO_BASE_ALWAYS; - *base_commit = NULL; - } else { - auto_base = AUTO_BASE_NEVER; - *base_commit = arg; - } - return 0; -} - -struct base_tree_info { - struct object_id base_commit; - int nr_patch_id, alloc_patch_id; - struct object_id *patch_id; -}; - -static struct commit *get_base_commit(const char *base_commit, - struct commit **list, - int total) -{ - struct commit *base = NULL; - struct commit **rev; - int i = 0, rev_nr = 0, auto_select, die_on_failure; - - switch (auto_base) { - case AUTO_BASE_NEVER: - if (base_commit) { - auto_select = 0; - die_on_failure = 1; - } else { - /* no base information is requested */ - return NULL; - } - break; - case AUTO_BASE_ALWAYS: - case AUTO_BASE_WHEN_ABLE: - if (base_commit) { - BUG("requested automatic base selection but a commit was provided"); - } else { - auto_select = 1; - die_on_failure = auto_base == AUTO_BASE_ALWAYS; - } - break; - default: - BUG("unexpected automatic base selection method"); - } - - if (!auto_select) { - base = lookup_commit_reference_by_name(base_commit); - if (!base) - die(_("unknown commit %s"), base_commit); - } else { - struct branch *curr_branch = branch_get(NULL); - const char *upstream = branch_get_upstream(curr_branch, NULL); - if (upstream) { - struct commit_list *base_list; - struct commit *commit; - struct object_id oid; - - if (get_oid(upstream, &oid)) { - if (die_on_failure) - die(_("failed to resolve '%s' as a valid ref"), upstream); - else - return NULL; - } - commit = lookup_commit_or_die(&oid, "upstream base"); - base_list = get_merge_bases_many(commit, total, list); - /* There should be one and only one merge base. */ - if (!base_list || base_list->next) { - if (die_on_failure) { - die(_("could not find exact merge base")); - } else { - free_commit_list(base_list); - return NULL; - } - } - base = base_list->item; - free_commit_list(base_list); - } else { - if (die_on_failure) - die(_("failed to get upstream, if you want to record base commit automatically,\n" - "please use git branch --set-upstream-to to track a remote branch.\n" - "Or you could specify base commit by --base=<base-commit-id> manually")); - else - return NULL; - } - } - - ALLOC_ARRAY(rev, total); - for (i = 0; i < total; i++) - rev[i] = list[i]; - - rev_nr = total; - /* - * Get merge base through pair-wise computations - * and store it in rev[0]. - */ - while (rev_nr > 1) { - for (i = 0; i < rev_nr / 2; i++) { - struct commit_list *merge_base; - merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]); - if (!merge_base || merge_base->next) { - if (die_on_failure) { - die(_("failed to find exact merge base")); - } else { - free(rev); - return NULL; - } - } - - rev[i] = merge_base->item; - } - - if (rev_nr % 2) - rev[i] = rev[2 * i]; - rev_nr = DIV_ROUND_UP(rev_nr, 2); - } - - if (!in_merge_bases(base, rev[0])) { - if (die_on_failure) { - die(_("base commit should be the ancestor of revision list")); - } else { - free(rev); - return NULL; - } - } - - for (i = 0; i < total; i++) { - if (base == list[i]) { - if (die_on_failure) { - die(_("base commit shouldn't be in revision list")); - } else { - free(rev); - return NULL; - } - } - } - - free(rev); - return base; -} - -define_commit_slab(commit_base, int); - -static void prepare_bases(struct base_tree_info *bases, - struct commit *base, - struct commit **list, - int total) -{ - struct commit *commit; - struct rev_info revs; - struct diff_options diffopt; - struct commit_base commit_base; - int i; - - if (!base) - return; - - init_commit_base(&commit_base); - repo_diff_setup(the_repository, &diffopt); - diffopt.flags.recursive = 1; - diff_setup_done(&diffopt); - - oidcpy(&bases->base_commit, &base->object.oid); - - repo_init_revisions(the_repository, &revs, NULL); - revs.max_parents = 1; - revs.topo_order = 1; - for (i = 0; i < total; i++) { - list[i]->object.flags &= ~UNINTERESTING; - add_pending_object(&revs, &list[i]->object, "rev_list"); - *commit_base_at(&commit_base, list[i]) = 1; - } - base->object.flags |= UNINTERESTING; - add_pending_object(&revs, &base->object, "base"); - - if (prepare_revision_walk(&revs)) - die(_("revision walk setup failed")); - /* - * Traverse the commits list, get prerequisite patch ids - * and stuff them in bases structure. - */ - while ((commit = get_revision(&revs)) != NULL) { - struct object_id oid; - struct object_id *patch_id; - if (*commit_base_at(&commit_base, commit)) - continue; - if (commit_patch_id(commit, &diffopt, &oid, 0, 1)) - die(_("cannot get patch id")); - ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id); - patch_id = bases->patch_id + bases->nr_patch_id; - oidcpy(patch_id, &oid); - bases->nr_patch_id++; - } - clear_commit_base(&commit_base); -} - -static void print_bases(struct base_tree_info *bases, FILE *file) -{ - int i; - - /* Only do this once, either for the cover or for the first one */ - if (is_null_oid(&bases->base_commit)) - return; - - /* Show the base commit */ - fprintf(file, "\nbase-commit: %s\n", oid_to_hex(&bases->base_commit)); - - /* Show the prerequisite patches */ - for (i = bases->nr_patch_id - 1; i >= 0; i--) - fprintf(file, "prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i])); - - free(bases->patch_id); - bases->nr_patch_id = 0; - bases->alloc_patch_id = 0; - oidclr(&bases->base_commit); -} - -static const char *diff_title(struct strbuf *sb, int reroll_count, - const char *generic, const char *rerolled) -{ - if (reroll_count <= 0) - strbuf_addstr(sb, generic); - else /* RFC may be v0, so allow -v1 to diff against v0 */ - strbuf_addf(sb, rerolled, reroll_count - 1); - return sb->buf; -} - -static void infer_range_diff_ranges(struct strbuf *r1, - struct strbuf *r2, - const char *prev, - struct commit *origin, - struct commit *head) -{ - const char *head_oid = oid_to_hex(&head->object.oid); - int prev_is_range = !!strstr(prev, ".."); - - if (prev_is_range) - strbuf_addstr(r1, prev); - else - strbuf_addf(r1, "%s..%s", head_oid, prev); - - if (origin) - strbuf_addf(r2, "%s..%s", oid_to_hex(&origin->object.oid), head_oid); - else if (prev_is_range) - die(_("failed to infer range-diff origin of current series")); - else { - warning(_("using '%s' as range-diff origin of current series"), prev); - strbuf_addf(r2, "%s..%s", prev, head_oid); - } -} - -int cmd_format_patch(int argc, const char **argv, const char *prefix) -{ - struct commit *commit; - struct commit **list = NULL; - struct rev_info rev; - struct setup_revision_opt s_r_opt; - int nr = 0, total, i; - int use_stdout = 0; - int start_number = -1; - int just_numbers = 0; - int ignore_if_in_upstream = 0; - int cover_letter = -1; - int boundary_count = 0; - int no_binary_diff = 0; - int zero_commit = 0; - struct commit *origin = NULL; - const char *in_reply_to = NULL; - struct patch_ids ids; - struct strbuf buf = STRBUF_INIT; - int use_patch_format = 0; - int quiet = 0; - int reroll_count = -1; - char *cover_from_description_arg = NULL; - char *branch_name = NULL; - char *base_commit = NULL; - struct base_tree_info bases; - struct commit *base; - int show_progress = 0; - struct progress *progress = NULL; - struct oid_array idiff_prev = OID_ARRAY_INIT; - struct strbuf idiff_title = STRBUF_INIT; - const char *rdiff_prev = NULL; - struct strbuf rdiff1 = STRBUF_INIT; - struct strbuf rdiff2 = STRBUF_INIT; - struct strbuf rdiff_title = STRBUF_INIT; - int creation_factor = -1; - - const struct option builtin_format_patch_options[] = { - OPT_CALLBACK_F('n', "numbered", &numbered, NULL, - N_("use [PATCH n/m] even with a single patch"), - PARSE_OPT_NOARG, numbered_callback), - OPT_CALLBACK_F('N', "no-numbered", &numbered, NULL, - N_("use [PATCH] even with multiple patches"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, no_numbered_callback), - OPT_BOOL('s', "signoff", &do_signoff, N_("add Signed-off-by:")), - OPT_BOOL(0, "stdout", &use_stdout, - N_("print patches to standard out")), - OPT_BOOL(0, "cover-letter", &cover_letter, - N_("generate a cover letter")), - OPT_BOOL(0, "numbered-files", &just_numbers, - N_("use simple number sequence for output file names")), - OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"), - N_("use <sfx> instead of '.patch'")), - OPT_INTEGER(0, "start-number", &start_number, - N_("start numbering patches at <n> instead of 1")), - OPT_INTEGER('v', "reroll-count", &reroll_count, - N_("mark the series as Nth re-roll")), - OPT_CALLBACK_F(0, "rfc", &rev, NULL, - N_("Use [RFC PATCH] instead of [PATCH]"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback), - OPT_STRING(0, "cover-from-description", &cover_from_description_arg, - N_("cover-from-description-mode"), - N_("generate parts of a cover letter based on a branch's description")), - OPT_CALLBACK_F(0, "subject-prefix", &rev, N_("prefix"), - N_("Use [<prefix>] instead of [PATCH]"), - PARSE_OPT_NONEG, subject_prefix_callback), - OPT_CALLBACK_F('o', "output-directory", &output_directory, - N_("dir"), N_("store resulting files in <dir>"), - PARSE_OPT_NONEG, output_directory_callback), - OPT_CALLBACK_F('k', "keep-subject", &rev, NULL, - N_("don't strip/add [PATCH]"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback), - OPT_BOOL(0, "no-binary", &no_binary_diff, - N_("don't output binary diffs")), - OPT_BOOL(0, "zero-commit", &zero_commit, - N_("output all-zero hash in From header")), - OPT_BOOL(0, "ignore-if-in-upstream", &ignore_if_in_upstream, - N_("don't include a patch matching a commit upstream")), - OPT_SET_INT_F('p', "no-stat", &use_patch_format, - N_("show patch format instead of default (patch + stat)"), - 1, PARSE_OPT_NONEG), - OPT_GROUP(N_("Messaging")), - OPT_CALLBACK(0, "add-header", NULL, N_("header"), - N_("add email header"), header_callback), - OPT_CALLBACK(0, "to", NULL, N_("email"), N_("add To: header"), to_callback), - OPT_CALLBACK(0, "cc", NULL, N_("email"), N_("add Cc: header"), cc_callback), - OPT_CALLBACK_F(0, "from", &from, N_("ident"), - N_("set From address to <ident> (or committer ident if absent)"), - PARSE_OPT_OPTARG, from_callback), - OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"), - N_("make first mail a reply to <message-id>")), - OPT_CALLBACK_F(0, "attach", &rev, N_("boundary"), - N_("attach the patch"), PARSE_OPT_OPTARG, - attach_callback), - OPT_CALLBACK_F(0, "inline", &rev, N_("boundary"), - N_("inline the patch"), - PARSE_OPT_OPTARG | PARSE_OPT_NONEG, - inline_callback), - OPT_CALLBACK_F(0, "thread", &thread, N_("style"), - N_("enable message threading, styles: shallow, deep"), - PARSE_OPT_OPTARG, thread_callback), - OPT_STRING(0, "signature", &signature, N_("signature"), - N_("add a signature")), - OPT_CALLBACK_F(0, "base", &base_commit, N_("base-commit"), - N_("add prerequisite tree info to the patch series"), - 0, base_callback), - OPT_FILENAME(0, "signature-file", &signature_file, - N_("add a signature from a file")), - OPT__QUIET(&quiet, N_("don't print the patch filenames")), - OPT_BOOL(0, "progress", &show_progress, - N_("show progress while generating patches")), - OPT_CALLBACK(0, "interdiff", &idiff_prev, N_("rev"), - N_("show changes against <rev> in cover letter or single patch"), - parse_opt_object_name), - OPT_STRING(0, "range-diff", &rdiff_prev, N_("refspec"), - N_("show changes against <refspec> in cover letter or single patch")), - OPT_INTEGER(0, "creation-factor", &creation_factor, - N_("percentage by which creation is weighted")), - OPT_END() - }; - - extra_hdr.strdup_strings = 1; - extra_to.strdup_strings = 1; - extra_cc.strdup_strings = 1; - init_log_defaults(); - init_display_notes(¬es_opt); - git_config(git_format_config, NULL); - repo_init_revisions(the_repository, &rev, prefix); - rev.show_notes = show_notes; - memcpy(&rev.notes_opt, ¬es_opt, sizeof(notes_opt)); - rev.commit_format = CMIT_FMT_EMAIL; - rev.encode_email_headers = default_encode_email_headers; - rev.expand_tabs_in_log_default = 0; - rev.verbose_header = 1; - rev.diff = 1; - rev.max_parents = 1; - rev.diffopt.flags.recursive = 1; - rev.subject_prefix = fmt_patch_subject_prefix; - memset(&s_r_opt, 0, sizeof(s_r_opt)); - s_r_opt.def = "HEAD"; - s_r_opt.revarg_opt = REVARG_COMMITTISH; - - if (default_attach) { - rev.mime_boundary = default_attach; - rev.no_inline = 1; - } - - /* - * Parse the arguments before setup_revisions(), or something - * like "git format-patch -o a123 HEAD^.." may fail; a123 is - * possibly a valid SHA1. - */ - argc = parse_options(argc, argv, prefix, builtin_format_patch_options, - builtin_format_patch_usage, - PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | - PARSE_OPT_KEEP_DASHDASH); - - if (cover_from_description_arg) - cover_from_description_mode = parse_cover_from_description(cover_from_description_arg); - - if (0 < reroll_count) { - struct strbuf sprefix = STRBUF_INIT; - strbuf_addf(&sprefix, "%s v%d", - rev.subject_prefix, reroll_count); - rev.reroll_count = reroll_count; - rev.subject_prefix = strbuf_detach(&sprefix, NULL); - } - - for (i = 0; i < extra_hdr.nr; i++) { - strbuf_addstr(&buf, extra_hdr.items[i].string); - strbuf_addch(&buf, '\n'); - } - - if (extra_to.nr) - strbuf_addstr(&buf, "To: "); - for (i = 0; i < extra_to.nr; i++) { - if (i) - strbuf_addstr(&buf, " "); - strbuf_addstr(&buf, extra_to.items[i].string); - if (i + 1 < extra_to.nr) - strbuf_addch(&buf, ','); - strbuf_addch(&buf, '\n'); - } - - if (extra_cc.nr) - strbuf_addstr(&buf, "Cc: "); - for (i = 0; i < extra_cc.nr; i++) { - if (i) - strbuf_addstr(&buf, " "); - strbuf_addstr(&buf, extra_cc.items[i].string); - if (i + 1 < extra_cc.nr) - strbuf_addch(&buf, ','); - strbuf_addch(&buf, '\n'); - } - - rev.extra_headers = strbuf_detach(&buf, NULL); - - if (from) { - if (split_ident_line(&rev.from_ident, from, strlen(from))) - die(_("invalid ident line: %s"), from); - } - - if (start_number < 0) - start_number = 1; - - /* - * If numbered is set solely due to format.numbered in config, - * and it would conflict with --keep-subject (-k) from the - * command line, reset "numbered". - */ - if (numbered && keep_subject && !numbered_cmdline_opt) - numbered = 0; - - if (numbered && keep_subject) - die(_("-n and -k are mutually exclusive")); - if (keep_subject && subject_prefix) - die(_("--subject-prefix/--rfc and -k are mutually exclusive")); - rev.preserve_subject = keep_subject; - - argc = setup_revisions(argc, argv, &rev, &s_r_opt); - if (argc > 1) - die(_("unrecognized argument: %s"), argv[1]); - - if (rev.diffopt.output_format & DIFF_FORMAT_NAME) - die(_("--name-only does not make sense")); - if (rev.diffopt.output_format & DIFF_FORMAT_NAME_STATUS) - die(_("--name-status does not make sense")); - if (rev.diffopt.output_format & DIFF_FORMAT_CHECKDIFF) - die(_("--check does not make sense")); - - if (!use_patch_format && - (!rev.diffopt.output_format || - rev.diffopt.output_format == DIFF_FORMAT_PATCH)) - rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY; - if (!rev.diffopt.stat_width) - rev.diffopt.stat_width = MAIL_DEFAULT_WRAP; - - /* Always generate a patch */ - rev.diffopt.output_format |= DIFF_FORMAT_PATCH; - - rev.zero_commit = zero_commit; - - if (!rev.diffopt.flags.text && !no_binary_diff) - rev.diffopt.flags.binary = 1; - - if (rev.show_notes) - load_display_notes(&rev.notes_opt); - - if (!output_directory && !use_stdout) - output_directory = config_output_directory; - - if (!use_stdout) - output_directory = set_outdir(prefix, output_directory); - else - setup_pager(); - - if (output_directory) { - int saved; - if (rev.diffopt.use_color != GIT_COLOR_ALWAYS) - rev.diffopt.use_color = GIT_COLOR_NEVER; - if (use_stdout) - die(_("standard output, or directory, which one?")); - /* - * We consider <outdir> as 'outside of gitdir', therefore avoid - * applying adjust_shared_perm in s-c-l-d. - */ - saved = get_shared_repository(); - set_shared_repository(0); - switch (safe_create_leading_directories_const(output_directory)) { - case SCLD_OK: - case SCLD_EXISTS: - break; - default: - die(_("could not create leading directories " - "of '%s'"), output_directory); - } - set_shared_repository(saved); - if (mkdir(output_directory, 0777) < 0 && errno != EEXIST) - die_errno(_("could not create directory '%s'"), - output_directory); - } - - if (rev.pending.nr == 1) { - int check_head = 0; - - if (rev.max_count < 0 && !rev.show_root_diff) { - /* - * This is traditional behaviour of "git format-patch - * origin" that prepares what the origin side still - * does not have. - */ - rev.pending.objects[0].item->flags |= UNINTERESTING; - add_head_to_pending(&rev); - check_head = 1; - } - /* - * Otherwise, it is "format-patch -22 HEAD", and/or - * "format-patch --root HEAD". The user wants - * get_revision() to do the usual traversal. - */ - - if (!strcmp(rev.pending.objects[0].name, "HEAD")) - check_head = 1; - - if (check_head) { - const char *ref, *v; - ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, - NULL, NULL); - if (ref && skip_prefix(ref, "refs/heads/", &v)) - branch_name = xstrdup(v); - else - branch_name = xstrdup(""); /* no branch */ - } - } - - /* - * We cannot move this anywhere earlier because we do want to - * know if --root was given explicitly from the command line. - */ - rev.show_root_diff = 1; - - if (ignore_if_in_upstream) { - /* Don't say anything if head and upstream are the same. */ - if (rev.pending.nr == 2) { - struct object_array_entry *o = rev.pending.objects; - if (oideq(&o[0].item->oid, &o[1].item->oid)) - goto done; - } - get_patch_ids(&rev, &ids); - } - - if (prepare_revision_walk(&rev)) - die(_("revision walk setup failed")); - rev.boundary = 1; - while ((commit = get_revision(&rev)) != NULL) { - if (commit->object.flags & BOUNDARY) { - boundary_count++; - origin = (boundary_count == 1) ? commit : NULL; - continue; - } - - if (ignore_if_in_upstream && has_commit_patch_id(commit, &ids)) - continue; - - nr++; - REALLOC_ARRAY(list, nr); - list[nr - 1] = commit; - } - if (nr == 0) - /* nothing to do */ - goto done; - total = nr; - if (cover_letter == -1) { - if (config_cover_letter == COVER_AUTO) - cover_letter = (total > 1); - else - cover_letter = (config_cover_letter == COVER_ON); - } - if (!keep_subject && auto_number && (total > 1 || cover_letter)) - numbered = 1; - if (numbered) - rev.total = total + start_number - 1; - - if (idiff_prev.nr) { - if (!cover_letter && total != 1) - die(_("--interdiff requires --cover-letter or single patch")); - rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1]; - rev.idiff_oid2 = get_commit_tree_oid(list[0]); - rev.idiff_title = diff_title(&idiff_title, reroll_count, - _("Interdiff:"), - _("Interdiff against v%d:")); - } - - if (creation_factor < 0) - creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT; - else if (!rdiff_prev) - die(_("--creation-factor requires --range-diff")); - - if (rdiff_prev) { - if (!cover_letter && total != 1) - die(_("--range-diff requires --cover-letter or single patch")); - - infer_range_diff_ranges(&rdiff1, &rdiff2, rdiff_prev, - origin, list[0]); - rev.rdiff1 = rdiff1.buf; - rev.rdiff2 = rdiff2.buf; - rev.creation_factor = creation_factor; - rev.rdiff_title = diff_title(&rdiff_title, reroll_count, - _("Range-diff:"), - _("Range-diff against v%d:")); - } - - if (!signature) { - ; /* --no-signature inhibits all signatures */ - } else if (signature && signature != git_version_string) { - ; /* non-default signature already set */ - } else if (signature_file) { - struct strbuf buf = STRBUF_INIT; - - if (strbuf_read_file(&buf, signature_file, 128) < 0) - die_errno(_("unable to read signature file '%s'"), signature_file); - signature = strbuf_detach(&buf, NULL); - } - - memset(&bases, 0, sizeof(bases)); - base = get_base_commit(base_commit, list, nr); - if (base) { - reset_revision_walk(); - clear_object_flags(UNINTERESTING); - prepare_bases(&bases, base, list, nr); - } - - if (in_reply_to || thread || cover_letter) - rev.ref_message_ids = xcalloc(1, sizeof(struct string_list)); - if (in_reply_to) { - const char *msgid = clean_message_id(in_reply_to); - string_list_append(rev.ref_message_ids, msgid); - } - rev.numbered_files = just_numbers; - rev.patch_suffix = fmt_patch_suffix; - if (cover_letter) { - if (thread) - gen_message_id(&rev, "cover"); - make_cover_letter(&rev, use_stdout, - origin, nr, list, branch_name, quiet); - print_bases(&bases, rev.diffopt.file); - print_signature(rev.diffopt.file); - total++; - start_number--; - /* interdiff/range-diff in cover-letter; omit from patches */ - rev.idiff_oid1 = NULL; - rev.rdiff1 = NULL; - } - rev.add_signoff = do_signoff; - - if (show_progress) - progress = start_delayed_progress(_("Generating patches"), total); - while (0 <= --nr) { - int shown; - display_progress(progress, total - nr); - commit = list[nr]; - rev.nr = total - nr + (start_number - 1); - /* Make the second and subsequent mails replies to the first */ - if (thread) { - /* Have we already had a message ID? */ - if (rev.message_id) { - /* - * For deep threading: make every mail - * a reply to the previous one, no - * matter what other options are set. - * - * For shallow threading: - * - * Without --cover-letter and - * --in-reply-to, make every mail a - * reply to the one before. - * - * With --in-reply-to but no - * --cover-letter, make every mail a - * reply to the <reply-to>. - * - * With --cover-letter, make every - * mail but the cover letter a reply - * to the cover letter. The cover - * letter is a reply to the - * --in-reply-to, if specified. - */ - if (thread == THREAD_SHALLOW - && rev.ref_message_ids->nr > 0 - && (!cover_letter || rev.nr > 1)) - free(rev.message_id); - else - string_list_append(rev.ref_message_ids, - rev.message_id); - } - gen_message_id(&rev, oid_to_hex(&commit->object.oid)); - } - - if (!use_stdout && - open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet)) - die(_("failed to create output files")); - shown = log_tree_commit(&rev, commit); - free_commit_buffer(the_repository->parsed_objects, - commit); - - /* We put one extra blank line between formatted - * patches and this flag is used by log-tree code - * to see if it needs to emit a LF before showing - * the log; when using one file per patch, we do - * not want the extra blank line. - */ - if (!use_stdout) - rev.shown_one = 0; - if (shown) { - print_bases(&bases, rev.diffopt.file); - if (rev.mime_boundary) - fprintf(rev.diffopt.file, "\n--%s%s--\n\n\n", - mime_boundary_leader, - rev.mime_boundary); - else - print_signature(rev.diffopt.file); - } - if (!use_stdout) - fclose(rev.diffopt.file); - } - stop_progress(&progress); - free(list); - free(branch_name); - string_list_clear(&extra_to, 0); - string_list_clear(&extra_cc, 0); - string_list_clear(&extra_hdr, 0); - if (ignore_if_in_upstream) - free_patch_ids(&ids); - -done: - oid_array_clear(&idiff_prev); - strbuf_release(&idiff_title); - strbuf_release(&rdiff1); - strbuf_release(&rdiff2); - strbuf_release(&rdiff_title); - return 0; -} - -static int add_pending_commit(const char *arg, struct rev_info *revs, int flags) -{ - struct object_id oid; - if (get_oid(arg, &oid) == 0) { - struct commit *commit = lookup_commit_reference(the_repository, - &oid); - if (commit) { - commit->object.flags |= flags; - add_pending_object(revs, &commit->object, arg); - return 0; - } - } - return -1; -} - -static const char * const cherry_usage[] = { - N_("git cherry [-v] [<upstream> [<head> [<limit>]]]"), - NULL -}; - -static void print_commit(char sign, struct commit *commit, int verbose, - int abbrev, FILE *file) -{ - if (!verbose) { - fprintf(file, "%c %s\n", sign, - find_unique_abbrev(&commit->object.oid, abbrev)); - } else { - struct strbuf buf = STRBUF_INIT; - pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf); - fprintf(file, "%c %s %s\n", sign, - find_unique_abbrev(&commit->object.oid, abbrev), - buf.buf); - strbuf_release(&buf); - } -} - -int cmd_cherry(int argc, const char **argv, const char *prefix) -{ - struct rev_info revs; - struct patch_ids ids; - struct commit *commit; - struct commit_list *list = NULL; - struct branch *current_branch; - const char *upstream; - const char *head = "HEAD"; - const char *limit = NULL; - int verbose = 0, abbrev = 0; - - struct option options[] = { - OPT__ABBREV(&abbrev), - OPT__VERBOSE(&verbose, N_("be verbose")), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, cherry_usage, 0); - - switch (argc) { - case 3: - limit = argv[2]; - /* FALLTHROUGH */ - case 2: - head = argv[1]; - /* FALLTHROUGH */ - case 1: - upstream = argv[0]; - break; - default: - current_branch = branch_get(NULL); - upstream = branch_get_upstream(current_branch, NULL); - if (!upstream) { - fprintf(stderr, _("Could not find a tracked" - " remote branch, please" - " specify <upstream> manually.\n")); - usage_with_options(cherry_usage, options); - } - } - - repo_init_revisions(the_repository, &revs, prefix); - revs.max_parents = 1; - - if (add_pending_commit(head, &revs, 0)) - die(_("unknown commit %s"), head); - if (add_pending_commit(upstream, &revs, UNINTERESTING)) - die(_("unknown commit %s"), upstream); - - /* Don't say anything if head and upstream are the same. */ - if (revs.pending.nr == 2) { - struct object_array_entry *o = revs.pending.objects; - if (oideq(&o[0].item->oid, &o[1].item->oid)) - return 0; - } - - get_patch_ids(&revs, &ids); - - if (limit && add_pending_commit(limit, &revs, UNINTERESTING)) - die(_("unknown commit %s"), limit); - - /* reverse the list of commits */ - if (prepare_revision_walk(&revs)) - die(_("revision walk setup failed")); - while ((commit = get_revision(&revs)) != NULL) { - commit_list_insert(commit, &list); - } - - while (list) { - char sign = '+'; - - commit = list->item; - if (has_commit_patch_id(commit, &ids)) - sign = '-'; - print_commit(sign, commit, verbose, abbrev, revs.diffopt.file); - list = list->next; - } - - free_patch_ids(&ids); - return 0; -} diff --git a/third_party/git/builtin/ls-files.c b/third_party/git/builtin/ls-files.c deleted file mode 100644 index c8eae899b82a..000000000000 --- a/third_party/git/builtin/ls-files.c +++ /dev/null @@ -1,693 +0,0 @@ -/* - * This merges the file listing in the directory cache index - * with the actual working directory list, and shows different - * combinations of the two. - * - * Copyright (C) Linus Torvalds, 2005 - */ -#include "cache.h" -#include "repository.h" -#include "config.h" -#include "quote.h" -#include "dir.h" -#include "builtin.h" -#include "tree.h" -#include "parse-options.h" -#include "resolve-undo.h" -#include "string-list.h" -#include "pathspec.h" -#include "run-command.h" -#include "submodule.h" -#include "submodule-config.h" - -static int abbrev; -static int show_deleted; -static int show_cached; -static int show_others; -static int show_stage; -static int show_unmerged; -static int show_resolve_undo; -static int show_modified; -static int show_killed; -static int show_valid_bit; -static int show_fsmonitor_bit; -static int line_terminator = '\n'; -static int debug_mode; -static int show_eol; -static int recurse_submodules; - -static const char *prefix; -static int max_prefix_len; -static int prefix_len; -static struct pathspec pathspec; -static int error_unmatch; -static char *ps_matched; -static const char *with_tree; -static int exc_given; -static int exclude_args; - -static const char *tag_cached = ""; -static const char *tag_unmerged = ""; -static const char *tag_removed = ""; -static const char *tag_other = ""; -static const char *tag_killed = ""; -static const char *tag_modified = ""; -static const char *tag_skip_worktree = ""; -static const char *tag_resolve_undo = ""; - -static void write_eolinfo(const struct index_state *istate, - const struct cache_entry *ce, const char *path) -{ - if (show_eol) { - struct stat st; - const char *i_txt = ""; - const char *w_txt = ""; - const char *a_txt = get_convert_attr_ascii(istate, path); - if (ce && S_ISREG(ce->ce_mode)) - i_txt = get_cached_convert_stats_ascii(istate, - ce->name); - if (!lstat(path, &st) && S_ISREG(st.st_mode)) - w_txt = get_wt_convert_stats_ascii(path); - printf("i/%-5s w/%-5s attr/%-17s\t", i_txt, w_txt, a_txt); - } -} - -static void write_name(const char *name) -{ - /* - * With "--full-name", prefix_len=0; this caller needs to pass - * an empty string in that case (a NULL is good for ""). - */ - write_name_quoted_relative(name, prefix_len ? prefix : NULL, - stdout, line_terminator); -} - -static const char *get_tag(const struct cache_entry *ce, const char *tag) -{ - static char alttag[4]; - - if (tag && *tag && ((show_valid_bit && (ce->ce_flags & CE_VALID)) || - (show_fsmonitor_bit && (ce->ce_flags & CE_FSMONITOR_VALID)))) { - memcpy(alttag, tag, 3); - - if (isalpha(tag[0])) { - alttag[0] = tolower(tag[0]); - } else if (tag[0] == '?') { - alttag[0] = '!'; - } else { - alttag[0] = 'v'; - alttag[1] = tag[0]; - alttag[2] = ' '; - alttag[3] = 0; - } - - tag = alttag; - } - - return tag; -} - -static void print_debug(const struct cache_entry *ce) -{ - if (debug_mode) { - const struct stat_data *sd = &ce->ce_stat_data; - - printf(" ctime: %u:%u\n", sd->sd_ctime.sec, sd->sd_ctime.nsec); - printf(" mtime: %u:%u\n", sd->sd_mtime.sec, sd->sd_mtime.nsec); - printf(" dev: %u\tino: %u\n", sd->sd_dev, sd->sd_ino); - printf(" uid: %u\tgid: %u\n", sd->sd_uid, sd->sd_gid); - printf(" size: %u\tflags: %x\n", sd->sd_size, ce->ce_flags); - } -} - -static void show_dir_entry(const struct index_state *istate, - const char *tag, struct dir_entry *ent) -{ - int len = max_prefix_len; - - if (len > ent->len) - die("git ls-files: internal error - directory entry not superset of prefix"); - - /* If ps_matches is non-NULL, figure out which pathspec(s) match. */ - if (ps_matched) - dir_path_match(istate, ent, &pathspec, len, ps_matched); - - fputs(tag, stdout); - write_eolinfo(istate, NULL, ent->name); - write_name(ent->name); -} - -static void show_other_files(const struct index_state *istate, - const struct dir_struct *dir) -{ - int i; - - for (i = 0; i < dir->nr; i++) { - struct dir_entry *ent = dir->entries[i]; - if (!index_name_is_other(istate, ent->name, ent->len)) - continue; - show_dir_entry(istate, tag_other, ent); - } -} - -static void show_killed_files(const struct index_state *istate, - const struct dir_struct *dir) -{ - int i; - for (i = 0; i < dir->nr; i++) { - struct dir_entry *ent = dir->entries[i]; - char *cp, *sp; - int pos, len, killed = 0; - - for (cp = ent->name; cp - ent->name < ent->len; cp = sp + 1) { - sp = strchr(cp, '/'); - if (!sp) { - /* If ent->name is prefix of an entry in the - * cache, it will be killed. - */ - pos = index_name_pos(istate, ent->name, ent->len); - if (0 <= pos) - BUG("killed-file %.*s not found", - ent->len, ent->name); - pos = -pos - 1; - while (pos < istate->cache_nr && - ce_stage(istate->cache[pos])) - pos++; /* skip unmerged */ - if (istate->cache_nr <= pos) - break; - /* pos points at a name immediately after - * ent->name in the cache. Does it expect - * ent->name to be a directory? - */ - len = ce_namelen(istate->cache[pos]); - if ((ent->len < len) && - !strncmp(istate->cache[pos]->name, - ent->name, ent->len) && - istate->cache[pos]->name[ent->len] == '/') - killed = 1; - break; - } - if (0 <= index_name_pos(istate, ent->name, sp - ent->name)) { - /* If any of the leading directories in - * ent->name is registered in the cache, - * ent->name will be killed. - */ - killed = 1; - break; - } - } - if (killed) - show_dir_entry(istate, tag_killed, dir->entries[i]); - } -} - -static void show_files(struct repository *repo, struct dir_struct *dir); - -static void show_submodule(struct repository *superproject, - struct dir_struct *dir, const char *path) -{ - struct repository subrepo; - const struct submodule *sub = submodule_from_path(superproject, - &null_oid, path); - - if (repo_submodule_init(&subrepo, superproject, sub)) - return; - - if (repo_read_index(&subrepo) < 0) - die("index file corrupt"); - - show_files(&subrepo, dir); - - repo_clear(&subrepo); -} - -static void show_ce(struct repository *repo, struct dir_struct *dir, - const struct cache_entry *ce, const char *fullname, - const char *tag) -{ - if (max_prefix_len > strlen(fullname)) - die("git ls-files: internal error - cache entry not superset of prefix"); - - if (recurse_submodules && S_ISGITLINK(ce->ce_mode) && - is_submodule_active(repo, ce->name)) { - show_submodule(repo, dir, ce->name); - } else if (match_pathspec(repo->index, &pathspec, fullname, strlen(fullname), - max_prefix_len, ps_matched, - S_ISDIR(ce->ce_mode) || - S_ISGITLINK(ce->ce_mode))) { - tag = get_tag(ce, tag); - - if (!show_stage) { - fputs(tag, stdout); - } else { - printf("%s%06o %s %d\t", - tag, - ce->ce_mode, - find_unique_abbrev(&ce->oid, abbrev), - ce_stage(ce)); - } - write_eolinfo(repo->index, ce, fullname); - write_name(fullname); - print_debug(ce); - } -} - -static void show_ru_info(const struct index_state *istate) -{ - struct string_list_item *item; - - if (!istate->resolve_undo) - return; - - for_each_string_list_item(item, istate->resolve_undo) { - const char *path = item->string; - struct resolve_undo_info *ui = item->util; - int i, len; - - len = strlen(path); - if (len < max_prefix_len) - continue; /* outside of the prefix */ - if (!match_pathspec(istate, &pathspec, path, len, - max_prefix_len, ps_matched, 0)) - continue; /* uninterested */ - for (i = 0; i < 3; i++) { - if (!ui->mode[i]) - continue; - printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i], - find_unique_abbrev(&ui->oid[i], abbrev), - i + 1); - write_name(path); - } - } -} - -static int ce_excluded(struct dir_struct *dir, struct index_state *istate, - const char *fullname, const struct cache_entry *ce) -{ - int dtype = ce_to_dtype(ce); - return is_excluded(dir, istate, fullname, &dtype); -} - -static void construct_fullname(struct strbuf *out, const struct repository *repo, - const struct cache_entry *ce) -{ - strbuf_reset(out); - if (repo->submodule_prefix) - strbuf_addstr(out, repo->submodule_prefix); - strbuf_addstr(out, ce->name); -} - -static void show_files(struct repository *repo, struct dir_struct *dir) -{ - int i; - struct strbuf fullname = STRBUF_INIT; - - /* For cached/deleted files we don't need to even do the readdir */ - if (show_others || show_killed) { - if (!show_others) - dir->flags |= DIR_COLLECT_KILLED_ONLY; - fill_directory(dir, repo->index, &pathspec); - if (show_others) - show_other_files(repo->index, dir); - if (show_killed) - show_killed_files(repo->index, dir); - } - if (show_cached || show_stage) { - for (i = 0; i < repo->index->cache_nr; i++) { - const struct cache_entry *ce = repo->index->cache[i]; - - construct_fullname(&fullname, repo, ce); - - if ((dir->flags & DIR_SHOW_IGNORED) && - !ce_excluded(dir, repo->index, fullname.buf, ce)) - continue; - if (show_unmerged && !ce_stage(ce)) - continue; - if (ce->ce_flags & CE_UPDATE) - continue; - show_ce(repo, dir, ce, fullname.buf, - ce_stage(ce) ? tag_unmerged : - (ce_skip_worktree(ce) ? tag_skip_worktree : - tag_cached)); - } - } - if (show_deleted || show_modified) { - for (i = 0; i < repo->index->cache_nr; i++) { - const struct cache_entry *ce = repo->index->cache[i]; - struct stat st; - int err; - - construct_fullname(&fullname, repo, ce); - - if ((dir->flags & DIR_SHOW_IGNORED) && - !ce_excluded(dir, repo->index, fullname.buf, ce)) - continue; - if (ce->ce_flags & CE_UPDATE) - continue; - if (ce_skip_worktree(ce)) - continue; - err = lstat(fullname.buf, &st); - if (show_deleted && err) - show_ce(repo, dir, ce, fullname.buf, tag_removed); - if (show_modified && ie_modified(repo->index, ce, &st, 0)) - show_ce(repo, dir, ce, fullname.buf, tag_modified); - } - } - - strbuf_release(&fullname); -} - -/* - * Prune the index to only contain stuff starting with "prefix" - */ -static void prune_index(struct index_state *istate, - const char *prefix, size_t prefixlen) -{ - int pos; - unsigned int first, last; - - if (!prefix || !istate->cache_nr) - return; - pos = index_name_pos(istate, prefix, prefixlen); - if (pos < 0) - pos = -pos-1; - first = pos; - last = istate->cache_nr; - while (last > first) { - int next = first + ((last - first) >> 1); - const struct cache_entry *ce = istate->cache[next]; - if (!strncmp(ce->name, prefix, prefixlen)) { - first = next+1; - continue; - } - last = next; - } - MOVE_ARRAY(istate->cache, istate->cache + pos, last - pos); - istate->cache_nr = last - pos; -} - -static int get_common_prefix_len(const char *common_prefix) -{ - int common_prefix_len; - - if (!common_prefix) - return 0; - - common_prefix_len = strlen(common_prefix); - - /* - * If the prefix has a trailing slash, strip it so that submodules wont - * be pruned from the index. - */ - if (common_prefix[common_prefix_len - 1] == '/') - common_prefix_len--; - - return common_prefix_len; -} - -/* - * Read the tree specified with --with-tree option - * (typically, HEAD) into stage #1 and then - * squash them down to stage #0. This is used for - * --error-unmatch to list and check the path patterns - * that were given from the command line. We are not - * going to write this index out. - */ -void overlay_tree_on_index(struct index_state *istate, - const char *tree_name, const char *prefix) -{ - struct tree *tree; - struct object_id oid; - struct pathspec pathspec; - struct cache_entry *last_stage0 = NULL; - int i; - - if (get_oid(tree_name, &oid)) - die("tree-ish %s not found.", tree_name); - tree = parse_tree_indirect(&oid); - if (!tree) - die("bad tree-ish %s", tree_name); - - /* Hoist the unmerged entries up to stage #3 to make room */ - for (i = 0; i < istate->cache_nr; i++) { - struct cache_entry *ce = istate->cache[i]; - if (!ce_stage(ce)) - continue; - ce->ce_flags |= CE_STAGEMASK; - } - - if (prefix) { - static const char *(matchbuf[1]); - matchbuf[0] = NULL; - parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC, - PATHSPEC_PREFER_CWD, prefix, matchbuf); - } else - memset(&pathspec, 0, sizeof(pathspec)); - if (read_tree(the_repository, tree, 1, &pathspec, istate)) - die("unable to read tree entries %s", tree_name); - - for (i = 0; i < istate->cache_nr; i++) { - struct cache_entry *ce = istate->cache[i]; - switch (ce_stage(ce)) { - case 0: - last_stage0 = ce; - /* fallthru */ - default: - continue; - case 1: - /* - * If there is stage #0 entry for this, we do not - * need to show it. We use CE_UPDATE bit to mark - * such an entry. - */ - if (last_stage0 && - !strcmp(last_stage0->name, ce->name)) - ce->ce_flags |= CE_UPDATE; - } - } -} - -static const char * const ls_files_usage[] = { - N_("git ls-files [<options>] [<file>...]"), - NULL -}; - -static int option_parse_exclude(const struct option *opt, - const char *arg, int unset) -{ - struct string_list *exclude_list = opt->value; - - BUG_ON_OPT_NEG(unset); - - exc_given = 1; - string_list_append(exclude_list, arg); - - return 0; -} - -static int option_parse_exclude_from(const struct option *opt, - const char *arg, int unset) -{ - struct dir_struct *dir = opt->value; - - BUG_ON_OPT_NEG(unset); - - exc_given = 1; - add_patterns_from_file(dir, arg); - - return 0; -} - -static int option_parse_exclude_standard(const struct option *opt, - const char *arg, int unset) -{ - struct dir_struct *dir = opt->value; - - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - - exc_given = 1; - setup_standard_excludes(dir); - - return 0; -} - -int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) -{ - int require_work_tree = 0, show_tag = 0, i; - const char *max_prefix; - struct dir_struct dir; - struct pattern_list *pl; - struct string_list exclude_list = STRING_LIST_INIT_NODUP; - struct option builtin_ls_files_options[] = { - /* Think twice before adding "--nul" synonym to this */ - OPT_SET_INT('z', NULL, &line_terminator, - N_("paths are separated with NUL character"), '\0'), - OPT_BOOL('t', NULL, &show_tag, - N_("identify the file status with tags")), - OPT_BOOL('v', NULL, &show_valid_bit, - N_("use lowercase letters for 'assume unchanged' files")), - OPT_BOOL('f', NULL, &show_fsmonitor_bit, - N_("use lowercase letters for 'fsmonitor clean' files")), - OPT_BOOL('c', "cached", &show_cached, - N_("show cached files in the output (default)")), - OPT_BOOL('d', "deleted", &show_deleted, - N_("show deleted files in the output")), - OPT_BOOL('m', "modified", &show_modified, - N_("show modified files in the output")), - OPT_BOOL('o', "others", &show_others, - N_("show other files in the output")), - OPT_BIT('i', "ignored", &dir.flags, - N_("show ignored files in the output"), - DIR_SHOW_IGNORED), - OPT_BOOL('s', "stage", &show_stage, - N_("show staged contents' object name in the output")), - OPT_BOOL('k', "killed", &show_killed, - N_("show files on the filesystem that need to be removed")), - OPT_BIT(0, "directory", &dir.flags, - N_("show 'other' directories' names only"), - DIR_SHOW_OTHER_DIRECTORIES), - OPT_BOOL(0, "eol", &show_eol, N_("show line endings of files")), - OPT_NEGBIT(0, "empty-directory", &dir.flags, - N_("don't show empty directories"), - DIR_HIDE_EMPTY_DIRECTORIES), - OPT_BOOL('u', "unmerged", &show_unmerged, - N_("show unmerged files in the output")), - OPT_BOOL(0, "resolve-undo", &show_resolve_undo, - N_("show resolve-undo information")), - OPT_CALLBACK_F('x', "exclude", &exclude_list, N_("pattern"), - N_("skip files matching pattern"), - PARSE_OPT_NONEG, option_parse_exclude), - OPT_CALLBACK_F('X', "exclude-from", &dir, N_("file"), - N_("exclude patterns are read from <file>"), - PARSE_OPT_NONEG, option_parse_exclude_from), - OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, N_("file"), - N_("read additional per-directory exclude patterns in <file>")), - OPT_CALLBACK_F(0, "exclude-standard", &dir, NULL, - N_("add the standard git exclusions"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, - option_parse_exclude_standard), - OPT_SET_INT_F(0, "full-name", &prefix_len, - N_("make the output relative to the project top directory"), - 0, PARSE_OPT_NONEG), - OPT_BOOL(0, "recurse-submodules", &recurse_submodules, - N_("recurse through submodules")), - OPT_BOOL(0, "error-unmatch", &error_unmatch, - N_("if any <file> is not in the index, treat this as an error")), - OPT_STRING(0, "with-tree", &with_tree, N_("tree-ish"), - N_("pretend that paths removed since <tree-ish> are still present")), - OPT__ABBREV(&abbrev), - OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")), - OPT_END() - }; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage_with_options(ls_files_usage, builtin_ls_files_options); - - dir_init(&dir); - prefix = cmd_prefix; - if (prefix) - prefix_len = strlen(prefix); - git_config(git_default_config, NULL); - - if (repo_read_index(the_repository) < 0) - die("index file corrupt"); - - argc = parse_options(argc, argv, prefix, builtin_ls_files_options, - ls_files_usage, 0); - pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option"); - for (i = 0; i < exclude_list.nr; i++) { - add_pattern(exclude_list.items[i].string, "", 0, pl, --exclude_args); - } - if (show_tag || show_valid_bit || show_fsmonitor_bit) { - tag_cached = "H "; - tag_unmerged = "M "; - tag_removed = "R "; - tag_modified = "C "; - tag_other = "? "; - tag_killed = "K "; - tag_skip_worktree = "S "; - tag_resolve_undo = "U "; - } - if (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed) - require_work_tree = 1; - if (show_unmerged) - /* - * There's no point in showing unmerged unless - * you also show the stage information. - */ - show_stage = 1; - if (dir.exclude_per_dir) - exc_given = 1; - - if (require_work_tree && !is_inside_work_tree()) - setup_work_tree(); - - if (recurse_submodules && - (show_stage || show_deleted || show_others || show_unmerged || - show_killed || show_modified || show_resolve_undo || with_tree)) - die("ls-files --recurse-submodules unsupported mode"); - - if (recurse_submodules && error_unmatch) - die("ls-files --recurse-submodules does not support " - "--error-unmatch"); - - parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_CWD, - prefix, argv); - - /* - * Find common prefix for all pathspec's - * This is used as a performance optimization which unfortunately cannot - * be done when recursing into submodules because when a pathspec is - * given which spans repository boundaries you can't simply remove the - * submodule entry because the pathspec may match something inside the - * submodule. - */ - if (recurse_submodules) - max_prefix = NULL; - else - max_prefix = common_prefix(&pathspec); - max_prefix_len = get_common_prefix_len(max_prefix); - - prune_index(the_repository->index, max_prefix, max_prefix_len); - - /* Treat unmatching pathspec elements as errors */ - if (pathspec.nr && error_unmatch) - ps_matched = xcalloc(pathspec.nr, 1); - - if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given) - die("ls-files --ignored needs some exclude pattern"); - - /* With no flags, we default to showing the cached files */ - if (!(show_stage || show_deleted || show_others || show_unmerged || - show_killed || show_modified || show_resolve_undo)) - show_cached = 1; - - if (with_tree) { - /* - * Basic sanity check; show-stages and show-unmerged - * would not make any sense with this option. - */ - if (show_stage || show_unmerged) - die("ls-files --with-tree is incompatible with -s or -u"); - overlay_tree_on_index(the_repository->index, with_tree, max_prefix); - } - - show_files(the_repository, &dir); - - if (show_resolve_undo) - show_ru_info(the_repository->index); - - if (ps_matched) { - int bad; - bad = report_path_error(ps_matched, &pathspec); - if (bad) - fprintf(stderr, "Did you forget to 'git add'?\n"); - - return bad ? 1 : 0; - } - - dir_clear(&dir); - return 0; -} diff --git a/third_party/git/builtin/ls-remote.c b/third_party/git/builtin/ls-remote.c deleted file mode 100644 index 092917eca29b..000000000000 --- a/third_party/git/builtin/ls-remote.c +++ /dev/null @@ -1,154 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "transport.h" -#include "ref-filter.h" -#include "remote.h" -#include "refs.h" - -static const char * const ls_remote_usage[] = { - N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n" - " [-q | --quiet] [--exit-code] [--get-url]\n" - " [--symref] [<repository> [<refs>...]]"), - NULL -}; - -/* - * Is there one among the list of patterns that match the tail part - * of the path? - */ -static int tail_match(const char **pattern, const char *path) -{ - const char *p; - char *pathbuf; - - if (!pattern) - return 1; /* no restriction */ - - pathbuf = xstrfmt("/%s", path); - while ((p = *(pattern++)) != NULL) { - if (!wildmatch(p, pathbuf, 0)) { - free(pathbuf); - return 1; - } - } - free(pathbuf); - return 0; -} - -int cmd_ls_remote(int argc, const char **argv, const char *prefix) -{ - const char *dest = NULL; - unsigned flags = 0; - int get_url = 0; - int quiet = 0; - int status = 0; - int show_symref_target = 0; - const char *uploadpack = NULL; - const char **pattern = NULL; - struct strvec ref_prefixes = STRVEC_INIT; - int i; - struct string_list server_options = STRING_LIST_INIT_DUP; - - struct remote *remote; - struct transport *transport; - const struct ref *ref; - struct ref_array ref_array; - static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; - - struct option options[] = { - OPT__QUIET(&quiet, N_("do not print remote URL")), - OPT_STRING(0, "upload-pack", &uploadpack, N_("exec"), - N_("path of git-upload-pack on the remote host")), - { OPTION_STRING, 0, "exec", &uploadpack, N_("exec"), - N_("path of git-upload-pack on the remote host"), - PARSE_OPT_HIDDEN }, - OPT_BIT('t', "tags", &flags, N_("limit to tags"), REF_TAGS), - OPT_BIT('h', "heads", &flags, N_("limit to heads"), REF_HEADS), - OPT_BIT(0, "refs", &flags, N_("do not show peeled tags"), REF_NORMAL), - OPT_BOOL(0, "get-url", &get_url, - N_("take url.<base>.insteadOf into account")), - OPT_REF_SORT(sorting_tail), - OPT_SET_INT_F(0, "exit-code", &status, - N_("exit with exit code 2 if no matching refs are found"), - 2, PARSE_OPT_NOCOMPLETE), - OPT_BOOL(0, "symref", &show_symref_target, - N_("show underlying ref in addition to the object pointed by it")), - OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")), - OPT_END() - }; - - memset(&ref_array, 0, sizeof(ref_array)); - - argc = parse_options(argc, argv, prefix, options, ls_remote_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - dest = argv[0]; - - UNLEAK(sorting); - - if (argc > 1) { - int i; - pattern = xcalloc(argc, sizeof(const char *)); - for (i = 1; i < argc; i++) { - pattern[i - 1] = xstrfmt("*/%s", argv[i]); - } - } - - if (flags & REF_TAGS) - strvec_push(&ref_prefixes, "refs/tags/"); - if (flags & REF_HEADS) - strvec_push(&ref_prefixes, "refs/heads/"); - - remote = remote_get(dest); - if (!remote) { - if (dest) - die("bad repository '%s'", dest); - die("No remote configured to list refs from."); - } - if (!remote->url_nr) - die("remote %s has no configured URL", dest); - - if (get_url) { - printf("%s\n", *remote->url); - return 0; - } - - transport = transport_get(remote, NULL); - if (uploadpack != NULL) - transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack); - if (server_options.nr) - transport->server_options = &server_options; - - ref = transport_get_remote_refs(transport, &ref_prefixes); - if (ref) { - int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport)); - repo_set_hash_algo(the_repository, hash_algo); - } - if (transport_disconnect(transport)) - return 1; - - if (!dest && !quiet) - fprintf(stderr, "From %s\n", *remote->url); - for ( ; ref; ref = ref->next) { - struct ref_array_item *item; - if (!check_ref_type(ref, flags)) - continue; - if (!tail_match(pattern, ref->name)) - continue; - item = ref_array_push(&ref_array, ref->name, &ref->old_oid); - item->symref = xstrdup_or_null(ref->symref); - } - - if (sorting) - ref_array_sort(sorting, &ref_array); - - for (i = 0; i < ref_array.nr; i++) { - const struct ref_array_item *ref = ref_array.items[i]; - if (show_symref_target && ref->symref) - printf("ref: %s\t%s\n", ref->symref, ref->refname); - printf("%s\t%s\n", oid_to_hex(&ref->objectname), ref->refname); - status = 0; /* we found something */ - } - - ref_array_clear(&ref_array); - return status; -} diff --git a/third_party/git/builtin/ls-tree.c b/third_party/git/builtin/ls-tree.c deleted file mode 100644 index 7cad3f24ebd0..000000000000 --- a/third_party/git/builtin/ls-tree.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * GIT - The information manager from hell - * - * Copyright (C) Linus Torvalds, 2005 - */ -#include "cache.h" -#include "config.h" -#include "object-store.h" -#include "blob.h" -#include "tree.h" -#include "commit.h" -#include "quote.h" -#include "builtin.h" -#include "parse-options.h" -#include "pathspec.h" - -static int line_termination = '\n'; -#define LS_RECURSIVE 1 -#define LS_TREE_ONLY 2 -#define LS_SHOW_TREES 4 -#define LS_NAME_ONLY 8 -#define LS_SHOW_SIZE 16 -static int abbrev; -static int ls_options; -static struct pathspec pathspec; -static int chomp_prefix; -static const char *ls_tree_prefix; - -static const char * const ls_tree_usage[] = { - N_("git ls-tree [<options>] <tree-ish> [<path>...]"), - NULL -}; - -static int show_recursive(const char *base, int baselen, const char *pathname) -{ - int i; - - if (ls_options & LS_RECURSIVE) - return 1; - - if (!pathspec.nr) - return 0; - - for (i = 0; i < pathspec.nr; i++) { - const char *spec = pathspec.items[i].match; - int len, speclen; - - if (strncmp(base, spec, baselen)) - continue; - len = strlen(pathname); - spec += baselen; - speclen = strlen(spec); - if (speclen <= len) - continue; - if (spec[len] && spec[len] != '/') - continue; - if (memcmp(pathname, spec, len)) - continue; - return 1; - } - return 0; -} - -static int show_tree(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, int stage, void *context) -{ - int retval = 0; - int baselen; - const char *type = blob_type; - - if (S_ISGITLINK(mode)) { - /* - * Maybe we want to have some recursive version here? - * - * Something similar to this incomplete example: - * - if (show_subprojects(base, baselen, pathname)) - retval = READ_TREE_RECURSIVE; - * - */ - type = commit_type; - } else if (S_ISDIR(mode)) { - if (show_recursive(base->buf, base->len, pathname)) { - retval = READ_TREE_RECURSIVE; - if (!(ls_options & LS_SHOW_TREES)) - return retval; - } - type = tree_type; - } - else if (ls_options & LS_TREE_ONLY) - return 0; - - if (!(ls_options & LS_NAME_ONLY)) { - if (ls_options & LS_SHOW_SIZE) { - char size_text[24]; - if (!strcmp(type, blob_type)) { - unsigned long size; - if (oid_object_info(the_repository, oid, &size) == OBJ_BAD) - xsnprintf(size_text, sizeof(size_text), - "BAD"); - else - xsnprintf(size_text, sizeof(size_text), - "%"PRIuMAX, (uintmax_t)size); - } else - xsnprintf(size_text, sizeof(size_text), "-"); - printf("%06o %s %s %7s\t", mode, type, - find_unique_abbrev(oid, abbrev), - size_text); - } else - printf("%06o %s %s\t", mode, type, - find_unique_abbrev(oid, abbrev)); - } - baselen = base->len; - strbuf_addstr(base, pathname); - write_name_quoted_relative(base->buf, - chomp_prefix ? ls_tree_prefix : NULL, - stdout, line_termination); - strbuf_setlen(base, baselen); - return retval; -} - -int cmd_ls_tree(int argc, const char **argv, const char *prefix) -{ - struct object_id oid; - struct tree *tree; - int i, full_tree = 0; - const struct option ls_tree_options[] = { - OPT_BIT('d', NULL, &ls_options, N_("only show trees"), - LS_TREE_ONLY), - OPT_BIT('r', NULL, &ls_options, N_("recurse into subtrees"), - LS_RECURSIVE), - OPT_BIT('t', NULL, &ls_options, N_("show trees when recursing"), - LS_SHOW_TREES), - OPT_SET_INT('z', NULL, &line_termination, - N_("terminate entries with NUL byte"), 0), - OPT_BIT('l', "long", &ls_options, N_("include object size"), - LS_SHOW_SIZE), - OPT_BIT(0, "name-only", &ls_options, N_("list only filenames"), - LS_NAME_ONLY), - OPT_BIT(0, "name-status", &ls_options, N_("list only filenames"), - LS_NAME_ONLY), - OPT_SET_INT(0, "full-name", &chomp_prefix, - N_("use full path names"), 0), - OPT_BOOL(0, "full-tree", &full_tree, - N_("list entire tree; not just current directory " - "(implies --full-name)")), - OPT__ABBREV(&abbrev), - OPT_END() - }; - - git_config(git_default_config, NULL); - ls_tree_prefix = prefix; - if (prefix && *prefix) - chomp_prefix = strlen(prefix); - - argc = parse_options(argc, argv, prefix, ls_tree_options, - ls_tree_usage, 0); - if (full_tree) { - ls_tree_prefix = prefix = NULL; - chomp_prefix = 0; - } - /* -d -r should imply -t, but -d by itself should not have to. */ - if ( (LS_TREE_ONLY|LS_RECURSIVE) == - ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options)) - ls_options |= LS_SHOW_TREES; - - if (argc < 1) - usage_with_options(ls_tree_usage, ls_tree_options); - if (get_oid(argv[0], &oid)) - die("Not a valid object name %s", argv[0]); - - /* - * show_recursive() rolls its own matching code and is - * generally ignorant of 'struct pathspec'. The magic mask - * cannot be lifted until it is converted to use - * match_pathspec() or tree_entry_interesting() - */ - parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC & - ~(PATHSPEC_FROMTOP | PATHSPEC_LITERAL), - PATHSPEC_PREFER_CWD, - prefix, argv + 1); - for (i = 0; i < pathspec.nr; i++) - pathspec.items[i].nowildcard_len = pathspec.items[i].len; - pathspec.has_wildcard = 0; - tree = parse_tree_indirect(&oid); - if (!tree) - die("not a tree object"); - return !!read_tree_recursive(the_repository, tree, "", 0, 0, - &pathspec, show_tree, NULL); -} diff --git a/third_party/git/builtin/mailinfo.c b/third_party/git/builtin/mailinfo.c deleted file mode 100644 index cfb667a594c8..000000000000 --- a/third_party/git/builtin/mailinfo.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Another stupid program, this one parsing the headers of an - * email to figure out authorship and subject - */ -#include "cache.h" -#include "builtin.h" -#include "utf8.h" -#include "strbuf.h" -#include "mailinfo.h" - -static const char mailinfo_usage[] = - "git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info"; - -int cmd_mailinfo(int argc, const char **argv, const char *prefix) -{ - const char *def_charset; - struct mailinfo mi; - int status; - char *msgfile, *patchfile; - - setup_mailinfo(&mi); - - def_charset = get_commit_output_encoding(); - mi.metainfo_charset = def_charset; - - while (1 < argc && argv[1][0] == '-') { - if (!strcmp(argv[1], "-k")) - mi.keep_subject = 1; - else if (!strcmp(argv[1], "-b")) - mi.keep_non_patch_brackets_in_subject = 1; - else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id")) - mi.add_message_id = 1; - else if (!strcmp(argv[1], "-u")) - mi.metainfo_charset = def_charset; - else if (!strcmp(argv[1], "-n")) - mi.metainfo_charset = NULL; - else if (starts_with(argv[1], "--encoding=")) - mi.metainfo_charset = argv[1] + 11; - else if (!strcmp(argv[1], "--scissors")) - mi.use_scissors = 1; - else if (!strcmp(argv[1], "--no-scissors")) - mi.use_scissors = 0; - else if (!strcmp(argv[1], "--no-inbody-headers")) - mi.use_inbody_headers = 0; - else - usage(mailinfo_usage); - argc--; argv++; - } - - if (argc != 3) - usage(mailinfo_usage); - - mi.input = stdin; - mi.output = stdout; - - msgfile = prefix_filename(prefix, argv[1]); - patchfile = prefix_filename(prefix, argv[2]); - - status = !!mailinfo(&mi, msgfile, patchfile); - clear_mailinfo(&mi); - - free(msgfile); - free(patchfile); - return status; -} diff --git a/third_party/git/builtin/mailsplit.c b/third_party/git/builtin/mailsplit.c deleted file mode 100644 index 664400b8169b..000000000000 --- a/third_party/git/builtin/mailsplit.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Totally braindamaged mbox splitter program. - * - * It just splits a mbox into a list of files: "0001" "0002" .. - * so you can process them further from there. - */ -#include "cache.h" -#include "builtin.h" -#include "string-list.h" -#include "strbuf.h" - -static const char git_mailsplit_usage[] = -"git mailsplit [-d<prec>] [-f<n>] [-b] [--keep-cr] -o<directory> [(<mbox>|<Maildir>)...]"; - -static int is_from_line(const char *line, int len) -{ - const char *colon; - - if (len < 20 || memcmp("From ", line, 5)) - return 0; - - colon = line + len - 2; - line += 5; - for (;;) { - if (colon < line) - return 0; - if (*--colon == ':') - break; - } - - if (!isdigit(colon[-4]) || - !isdigit(colon[-2]) || - !isdigit(colon[-1]) || - !isdigit(colon[ 1]) || - !isdigit(colon[ 2])) - return 0; - - /* year */ - if (strtol(colon+3, NULL, 10) <= 90) - return 0; - - /* Ok, close enough */ - return 1; -} - -static struct strbuf buf = STRBUF_INIT; -static int keep_cr; -static int mboxrd; - -static int is_gtfrom(const struct strbuf *buf) -{ - size_t min = strlen(">From "); - size_t ngt; - - if (buf->len < min) - return 0; - - ngt = strspn(buf->buf, ">"); - return ngt && starts_with(buf->buf + ngt, "From "); -} - -/* Called with the first line (potentially partial) - * already in buf[] -- normally that should begin with - * the Unix "From " line. Write it into the specified - * file. - */ -static int split_one(FILE *mbox, const char *name, int allow_bare) -{ - FILE *output; - int fd; - int status = 0; - int is_bare = !is_from_line(buf.buf, buf.len); - - if (is_bare && !allow_bare) { - fprintf(stderr, "corrupt mailbox\n"); - exit(1); - } - fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666); - if (fd < 0) - die_errno("cannot open output file '%s'", name); - output = xfdopen(fd, "w"); - - /* Copy it out, while searching for a line that begins with - * "From " and having something that looks like a date format. - */ - for (;;) { - if (!keep_cr && buf.len > 1 && buf.buf[buf.len-1] == '\n' && - buf.buf[buf.len-2] == '\r') { - strbuf_setlen(&buf, buf.len-2); - strbuf_addch(&buf, '\n'); - } - - if (mboxrd && is_gtfrom(&buf)) - strbuf_remove(&buf, 0, 1); - - if (fwrite(buf.buf, 1, buf.len, output) != buf.len) - die_errno("cannot write output"); - - if (strbuf_getwholeline(&buf, mbox, '\n')) { - if (feof(mbox)) { - status = 1; - break; - } - die_errno("cannot read mbox"); - } - if (!is_bare && is_from_line(buf.buf, buf.len)) - break; /* done with one message */ - } - fclose(output); - return status; -} - -static int populate_maildir_list(struct string_list *list, const char *path) -{ - DIR *dir; - struct dirent *dent; - char *name = NULL; - char *subs[] = { "cur", "new", NULL }; - char **sub; - int ret = -1; - - for (sub = subs; *sub; ++sub) { - free(name); - name = xstrfmt("%s/%s", path, *sub); - if ((dir = opendir(name)) == NULL) { - if (errno == ENOENT) - continue; - error_errno("cannot opendir %s", name); - goto out; - } - - while ((dent = readdir(dir)) != NULL) { - if (dent->d_name[0] == '.') - continue; - free(name); - name = xstrfmt("%s/%s", *sub, dent->d_name); - string_list_insert(list, name); - } - - closedir(dir); - } - - ret = 0; - -out: - free(name); - return ret; -} - -static int maildir_filename_cmp(const char *a, const char *b) -{ - while (*a && *b) { - if (isdigit(*a) && isdigit(*b)) { - long int na, nb; - na = strtol(a, (char **)&a, 10); - nb = strtol(b, (char **)&b, 10); - if (na != nb) - return na - nb; - /* strtol advanced our pointers */ - } - else { - if (*a != *b) - return (unsigned char)*a - (unsigned char)*b; - a++; - b++; - } - } - return (unsigned char)*a - (unsigned char)*b; -} - -static int split_maildir(const char *maildir, const char *dir, - int nr_prec, int skip) -{ - char *file = NULL; - FILE *f = NULL; - int ret = -1; - int i; - struct string_list list = STRING_LIST_INIT_DUP; - - list.cmp = maildir_filename_cmp; - - if (populate_maildir_list(&list, maildir) < 0) - goto out; - - for (i = 0; i < list.nr; i++) { - char *name; - - free(file); - file = xstrfmt("%s/%s", maildir, list.items[i].string); - - f = fopen(file, "r"); - if (!f) { - error_errno("cannot open mail %s", file); - goto out; - } - - if (strbuf_getwholeline(&buf, f, '\n')) { - error_errno("cannot read mail %s", file); - goto out; - } - - name = xstrfmt("%s/%0*d", dir, nr_prec, ++skip); - split_one(f, name, 1); - free(name); - - fclose(f); - f = NULL; - } - - ret = skip; -out: - if (f) - fclose(f); - free(file); - string_list_clear(&list, 1); - return ret; -} - -static int split_mbox(const char *file, const char *dir, int allow_bare, - int nr_prec, int skip) -{ - int ret = -1; - int peek; - - FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r"); - int file_done = 0; - - if (!f) { - error_errno("cannot open mbox %s", file); - goto out; - } - - do { - peek = fgetc(f); - if (peek == EOF) { - if (f == stdin) - /* empty stdin is OK */ - ret = skip; - else { - fclose(f); - error(_("empty mbox: '%s'"), file); - } - goto out; - } - } while (isspace(peek)); - ungetc(peek, f); - - if (strbuf_getwholeline(&buf, f, '\n')) { - /* empty stdin is OK */ - if (f != stdin) { - error("cannot read mbox %s", file); - goto out; - } - file_done = 1; - } - - while (!file_done) { - char *name = xstrfmt("%s/%0*d", dir, nr_prec, ++skip); - file_done = split_one(f, name, allow_bare); - free(name); - } - - if (f != stdin) - fclose(f); - - ret = skip; -out: - return ret; -} - -int cmd_mailsplit(int argc, const char **argv, const char *prefix) -{ - int nr = 0, nr_prec = 4, num = 0; - int allow_bare = 0; - const char *dir = NULL; - const char **argp; - static const char *stdin_only[] = { "-", NULL }; - - for (argp = argv+1; *argp; argp++) { - const char *arg = *argp; - - if (arg[0] != '-') - break; - /* do flags here */ - if ( arg[1] == 'd' ) { - nr_prec = strtol(arg+2, NULL, 10); - if (nr_prec < 3 || 10 <= nr_prec) - usage(git_mailsplit_usage); - continue; - } else if ( arg[1] == 'f' ) { - nr = strtol(arg+2, NULL, 10); - } else if ( arg[1] == 'h' ) { - usage(git_mailsplit_usage); - } else if ( arg[1] == 'b' && !arg[2] ) { - allow_bare = 1; - } else if (!strcmp(arg, "--keep-cr")) { - keep_cr = 1; - } else if ( arg[1] == 'o' && arg[2] ) { - dir = arg+2; - } else if (!strcmp(arg, "--mboxrd")) { - mboxrd = 1; - } else if ( arg[1] == '-' && !arg[2] ) { - argp++; /* -- marks end of options */ - break; - } else { - die("unknown option: %s", arg); - } - } - - if ( !dir ) { - /* Backwards compatibility: if no -o specified, accept - <mbox> <dir> or just <dir> */ - switch (argc - (argp-argv)) { - case 1: - dir = argp[0]; - argp = stdin_only; - break; - case 2: - stdin_only[0] = argp[0]; - dir = argp[1]; - argp = stdin_only; - break; - default: - usage(git_mailsplit_usage); - } - } else { - /* New usage: if no more argument, parse stdin */ - if ( !*argp ) - argp = stdin_only; - } - - while (*argp) { - const char *arg = *argp++; - struct stat argstat; - int ret = 0; - - if (arg[0] == '-' && arg[1] == 0) { - ret = split_mbox(arg, dir, allow_bare, nr_prec, nr); - if (ret < 0) { - error("cannot split patches from stdin"); - return 1; - } - num += (ret - nr); - nr = ret; - continue; - } - - if (stat(arg, &argstat) == -1) { - error_errno("cannot stat %s", arg); - return 1; - } - - if (S_ISDIR(argstat.st_mode)) - ret = split_maildir(arg, dir, nr_prec, nr); - else - ret = split_mbox(arg, dir, allow_bare, nr_prec, nr); - - if (ret < 0) { - error("cannot split patches from %s", arg); - return 1; - } - num += (ret - nr); - nr = ret; - } - - printf("%d\n", num); - - return 0; -} diff --git a/third_party/git/builtin/merge-base.c b/third_party/git/builtin/merge-base.c deleted file mode 100644 index 6719ac198dc2..000000000000 --- a/third_party/git/builtin/merge-base.c +++ /dev/null @@ -1,188 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "commit.h" -#include "refs.h" -#include "diff.h" -#include "revision.h" -#include "parse-options.h" -#include "repository.h" -#include "commit-reach.h" - -static int show_merge_base(struct commit **rev, int rev_nr, int show_all) -{ - struct commit_list *result, *r; - - result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1); - - if (!result) - return 1; - - for (r = result; r; r = r->next) { - printf("%s\n", oid_to_hex(&r->item->object.oid)); - if (!show_all) - break; - } - - free_commit_list(result); - return 0; -} - -static const char * const merge_base_usage[] = { - N_("git merge-base [-a | --all] <commit> <commit>..."), - N_("git merge-base [-a | --all] --octopus <commit>..."), - N_("git merge-base --independent <commit>..."), - N_("git merge-base --is-ancestor <commit> <commit>"), - N_("git merge-base --fork-point <ref> [<commit>]"), - NULL -}; - -static struct commit *get_commit_reference(const char *arg) -{ - struct object_id revkey; - struct commit *r; - - if (get_oid(arg, &revkey)) - die("Not a valid object name %s", arg); - r = lookup_commit_reference(the_repository, &revkey); - if (!r) - die("Not a valid commit name %s", arg); - - return r; -} - -static int handle_independent(int count, const char **args) -{ - struct commit_list *revs = NULL, *rev; - int i; - - for (i = count - 1; i >= 0; i--) - commit_list_insert(get_commit_reference(args[i]), &revs); - - reduce_heads_replace(&revs); - - if (!revs) - return 1; - - for (rev = revs; rev; rev = rev->next) - printf("%s\n", oid_to_hex(&rev->item->object.oid)); - - free_commit_list(revs); - return 0; -} - -static int handle_octopus(int count, const char **args, int show_all) -{ - struct commit_list *revs = NULL; - struct commit_list *result, *rev; - int i; - - for (i = count - 1; i >= 0; i--) - commit_list_insert(get_commit_reference(args[i]), &revs); - - result = get_octopus_merge_bases(revs); - free_commit_list(revs); - reduce_heads_replace(&result); - - if (!result) - return 1; - - for (rev = result; rev; rev = rev->next) { - printf("%s\n", oid_to_hex(&rev->item->object.oid)); - if (!show_all) - break; - } - - free_commit_list(result); - return 0; -} - -static int handle_is_ancestor(int argc, const char **argv) -{ - struct commit *one, *two; - - if (argc != 2) - die("--is-ancestor takes exactly two commits"); - one = get_commit_reference(argv[0]); - two = get_commit_reference(argv[1]); - if (in_merge_bases(one, two)) - return 0; - else - return 1; -} - -static int handle_fork_point(int argc, const char **argv) -{ - struct object_id oid; - struct commit *derived, *fork_point; - const char *commitname; - - commitname = (argc == 2) ? argv[1] : "HEAD"; - if (get_oid(commitname, &oid)) - die("Not a valid object name: '%s'", commitname); - - derived = lookup_commit_reference(the_repository, &oid); - - fork_point = get_fork_point(argv[0], derived); - - if (!fork_point) - return 1; - - printf("%s\n", oid_to_hex(&fork_point->object.oid)); - return 0; -} - -int cmd_merge_base(int argc, const char **argv, const char *prefix) -{ - struct commit **rev; - int rev_nr = 0; - int show_all = 0; - int cmdmode = 0; - - struct option options[] = { - OPT_BOOL('a', "all", &show_all, N_("output all common ancestors")), - OPT_CMDMODE(0, "octopus", &cmdmode, - N_("find ancestors for a single n-way merge"), 'o'), - OPT_CMDMODE(0, "independent", &cmdmode, - N_("list revs not reachable from others"), 'r'), - OPT_CMDMODE(0, "is-ancestor", &cmdmode, - N_("is the first one ancestor of the other?"), 'a'), - OPT_CMDMODE(0, "fork-point", &cmdmode, - N_("find where <commit> forked from reflog of <ref>"), 'f'), - OPT_END() - }; - - git_config(git_default_config, NULL); - argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0); - - if (cmdmode == 'a') { - if (argc < 2) - usage_with_options(merge_base_usage, options); - if (show_all) - die("--is-ancestor cannot be used with --all"); - return handle_is_ancestor(argc, argv); - } - - if (cmdmode == 'r' && show_all) - die("--independent cannot be used with --all"); - - if (cmdmode == 'o') - return handle_octopus(argc, argv, show_all); - - if (cmdmode == 'r') - return handle_independent(argc, argv); - - if (cmdmode == 'f') { - if (argc < 1 || 2 < argc) - usage_with_options(merge_base_usage, options); - return handle_fork_point(argc, argv); - } - - if (argc < 2) - usage_with_options(merge_base_usage, options); - - ALLOC_ARRAY(rev, argc); - while (argc-- > 0) - rev[rev_nr++] = get_commit_reference(*argv++); - return show_merge_base(rev, rev_nr, show_all); -} diff --git a/third_party/git/builtin/merge-file.c b/third_party/git/builtin/merge-file.c deleted file mode 100644 index 06a2f90c4875..000000000000 --- a/third_party/git/builtin/merge-file.c +++ /dev/null @@ -1,118 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "xdiff/xdiff.h" -#include "xdiff-interface.h" -#include "parse-options.h" - -static const char *const merge_file_usage[] = { - N_("git merge-file [<options>] [-L <name1> [-L <orig> [-L <name2>]]] <file1> <orig-file> <file2>"), - NULL -}; - -static int label_cb(const struct option *opt, const char *arg, int unset) -{ - static int label_count = 0; - const char **names = (const char **)opt->value; - - BUG_ON_OPT_NEG(unset); - - if (label_count >= 3) - return error("too many labels on the command line"); - names[label_count++] = arg; - return 0; -} - -int cmd_merge_file(int argc, const char **argv, const char *prefix) -{ - const char *names[3] = { NULL, NULL, NULL }; - mmfile_t mmfs[3]; - mmbuffer_t result = {NULL, 0}; - xmparam_t xmp = {{0}}; - int ret = 0, i = 0, to_stdout = 0; - int quiet = 0; - struct option options[] = { - OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")), - OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3), - OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"), - XDL_MERGE_FAVOR_OURS), - OPT_SET_INT(0, "theirs", &xmp.favor, N_("for conflicts, use their version"), - XDL_MERGE_FAVOR_THEIRS), - OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"), - XDL_MERGE_FAVOR_UNION), - OPT_INTEGER(0, "marker-size", &xmp.marker_size, - N_("for conflicts, use this marker size")), - OPT__QUIET(&quiet, N_("do not warn about conflicts")), - OPT_CALLBACK('L', NULL, names, N_("name"), - N_("set labels for file1/orig-file/file2"), &label_cb), - OPT_END(), - }; - - xmp.level = XDL_MERGE_ZEALOUS_ALNUM; - xmp.style = 0; - xmp.favor = 0; - - if (startup_info->have_repository) { - /* Read the configuration file */ - git_config(git_xmerge_config, NULL); - if (0 <= git_xmerge_style) - xmp.style = git_xmerge_style; - } - - argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0); - if (argc != 3) - usage_with_options(merge_file_usage, options); - if (quiet) { - if (!freopen("/dev/null", "w", stderr)) - return error_errno("failed to redirect stderr to /dev/null"); - } - - for (i = 0; i < 3; i++) { - char *fname; - int ret; - - if (!names[i]) - names[i] = argv[i]; - - fname = prefix_filename(prefix, argv[i]); - ret = read_mmfile(mmfs + i, fname); - free(fname); - if (ret) - return -1; - - if (mmfs[i].size > MAX_XDIFF_SIZE || - buffer_is_binary(mmfs[i].ptr, mmfs[i].size)) - return error("Cannot merge binary files: %s", - argv[i]); - } - - xmp.ancestor = names[1]; - xmp.file1 = names[0]; - xmp.file2 = names[2]; - ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result); - - for (i = 0; i < 3; i++) - free(mmfs[i].ptr); - - if (ret >= 0) { - const char *filename = argv[0]; - char *fpath = prefix_filename(prefix, argv[0]); - FILE *f = to_stdout ? stdout : fopen(fpath, "wb"); - - if (!f) - ret = error_errno("Could not open %s for writing", - filename); - else if (result.size && - fwrite(result.ptr, result.size, 1, f) != 1) - ret = error_errno("Could not write to %s", filename); - else if (fclose(f)) - ret = error_errno("Could not close %s", filename); - free(result.ptr); - free(fpath); - } - - if (ret > 127) - ret = 127; - - return ret; -} diff --git a/third_party/git/builtin/merge-index.c b/third_party/git/builtin/merge-index.c deleted file mode 100644 index 38ea6ad6ca25..000000000000 --- a/third_party/git/builtin/merge-index.c +++ /dev/null @@ -1,111 +0,0 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "run-command.h" - -static const char *pgm; -static int one_shot, quiet; -static int err; - -static int merge_entry(int pos, const char *path) -{ - int found; - const char *arguments[] = { pgm, "", "", "", path, "", "", "", NULL }; - char hexbuf[4][GIT_MAX_HEXSZ + 1]; - char ownbuf[4][60]; - - if (pos >= active_nr) - die("git merge-index: %s not in the cache", path); - found = 0; - do { - const struct cache_entry *ce = active_cache[pos]; - int stage = ce_stage(ce); - - if (strcmp(ce->name, path)) - break; - found++; - oid_to_hex_r(hexbuf[stage], &ce->oid); - xsnprintf(ownbuf[stage], sizeof(ownbuf[stage]), "%o", ce->ce_mode); - arguments[stage] = hexbuf[stage]; - arguments[stage + 4] = ownbuf[stage]; - } while (++pos < active_nr); - if (!found) - die("git merge-index: %s not in the cache", path); - - if (run_command_v_opt(arguments, 0)) { - if (one_shot) - err++; - else { - if (!quiet) - die("merge program failed"); - exit(1); - } - } - return found; -} - -static void merge_one_path(const char *path) -{ - int pos = cache_name_pos(path, strlen(path)); - - /* - * If it already exists in the cache as stage0, it's - * already merged and there is nothing to do. - */ - if (pos < 0) - merge_entry(-pos-1, path); -} - -static void merge_all(void) -{ - int i; - for (i = 0; i < active_nr; i++) { - const struct cache_entry *ce = active_cache[i]; - if (!ce_stage(ce)) - continue; - i += merge_entry(i, ce->name)-1; - } -} - -int cmd_merge_index(int argc, const char **argv, const char *prefix) -{ - int i, force_file = 0; - - /* Without this we cannot rely on waitpid() to tell - * what happened to our children. - */ - signal(SIGCHLD, SIG_DFL); - - if (argc < 3) - usage("git merge-index [-o] [-q] <merge-program> (-a | [--] [<filename>...])"); - - read_cache(); - - i = 1; - if (!strcmp(argv[i], "-o")) { - one_shot = 1; - i++; - } - if (!strcmp(argv[i], "-q")) { - quiet = 1; - i++; - } - pgm = argv[i++]; - for (; i < argc; i++) { - const char *arg = argv[i]; - if (!force_file && *arg == '-') { - if (!strcmp(arg, "--")) { - force_file = 1; - continue; - } - if (!strcmp(arg, "-a")) { - merge_all(); - continue; - } - die("git merge-index: unknown option %s", arg); - } - merge_one_path(arg); - } - if (err && !quiet) - die("merge program failed"); - return err; -} diff --git a/third_party/git/builtin/merge-ours.c b/third_party/git/builtin/merge-ours.c deleted file mode 100644 index 459450742053..000000000000 --- a/third_party/git/builtin/merge-ours.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Implementation of git-merge-ours.sh as builtin - * - * Copyright (c) 2007 Thomas Harning Jr - * Original: - * Original Copyright (c) 2005 Junio C Hamano - * - * Pretend we resolved the heads, but declare our tree trumps everybody else. - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "git-compat-util.h" -#include "builtin.h" -#include "diff.h" - -static const char builtin_merge_ours_usage[] = - "git merge-ours <base>... -- HEAD <remote>..."; - -int cmd_merge_ours(int argc, const char **argv, const char *prefix) -{ - if (argc == 2 && !strcmp(argv[1], "-h")) - usage(builtin_merge_ours_usage); - - /* - * The contents of the current index becomes the tree we - * commit. The index must match HEAD, or this merge cannot go - * through. - */ - if (read_cache() < 0) - die_errno("read_cache failed"); - if (index_differs_from(the_repository, "HEAD", NULL, 0)) - exit(2); - exit(0); -} diff --git a/third_party/git/builtin/merge-recursive.c b/third_party/git/builtin/merge-recursive.c deleted file mode 100644 index a4bfd8fc51d6..000000000000 --- a/third_party/git/builtin/merge-recursive.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "cache.h" -#include "builtin.h" -#include "commit.h" -#include "tag.h" -#include "merge-recursive.h" -#include "xdiff-interface.h" - -static const char builtin_merge_recursive_usage[] = - "git %s <base>... -- <head> <remote> ..."; - -static char *better_branch_name(const char *branch) -{ - static char githead_env[8 + GIT_MAX_HEXSZ + 1]; - char *name; - - if (strlen(branch) != the_hash_algo->hexsz) - return xstrdup(branch); - xsnprintf(githead_env, sizeof(githead_env), "GITHEAD_%s", branch); - name = getenv(githead_env); - return xstrdup(name ? name : branch); -} - -int cmd_merge_recursive(int argc, const char **argv, const char *prefix) -{ - const struct object_id *bases[21]; - unsigned bases_count = 0; - int i, failed; - struct object_id h1, h2; - struct merge_options o; - char *better1, *better2; - struct commit *result; - - init_merge_options(&o, the_repository); - if (argv[0] && ends_with(argv[0], "-subtree")) - o.subtree_shift = ""; - - if (argc < 4) - usagef(builtin_merge_recursive_usage, argv[0]); - - for (i = 1; i < argc; ++i) { - const char *arg = argv[i]; - - if (starts_with(arg, "--")) { - if (!arg[2]) - break; - if (parse_merge_opt(&o, arg + 2)) - die(_("unknown option %s"), arg); - continue; - } - if (bases_count < ARRAY_SIZE(bases)-1) { - struct object_id *oid = xmalloc(sizeof(struct object_id)); - if (get_oid(argv[i], oid)) - die(_("could not parse object '%s'"), argv[i]); - bases[bases_count++] = oid; - } - else - warning(Q_("cannot handle more than %d base. " - "Ignoring %s.", - "cannot handle more than %d bases. " - "Ignoring %s.", - (int)ARRAY_SIZE(bases)-1), - (int)ARRAY_SIZE(bases)-1, argv[i]); - } - if (argc - i != 3) /* "--" "<head>" "<remote>" */ - die(_("not handling anything other than two heads merge.")); - - if (repo_read_index_unmerged(the_repository)) - die_resolve_conflict("merge"); - - o.branch1 = argv[++i]; - o.branch2 = argv[++i]; - - if (get_oid(o.branch1, &h1)) - die(_("could not resolve ref '%s'"), o.branch1); - if (get_oid(o.branch2, &h2)) - die(_("could not resolve ref '%s'"), o.branch2); - - o.branch1 = better1 = better_branch_name(o.branch1); - o.branch2 = better2 = better_branch_name(o.branch2); - - if (o.verbosity >= 3) - printf(_("Merging %s with %s\n"), o.branch1, o.branch2); - - failed = merge_recursive_generic(&o, &h1, &h2, bases_count, bases, &result); - - free(better1); - free(better2); - - if (failed < 0) - return 128; /* die() error code */ - return failed; -} diff --git a/third_party/git/builtin/merge-tree.c b/third_party/git/builtin/merge-tree.c deleted file mode 100644 index e72714a5a87d..000000000000 --- a/third_party/git/builtin/merge-tree.c +++ /dev/null @@ -1,390 +0,0 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "tree-walk.h" -#include "xdiff-interface.h" -#include "object-store.h" -#include "repository.h" -#include "blob.h" -#include "exec-cmd.h" -#include "merge-blobs.h" - -static const char merge_tree_usage[] = "git merge-tree <base-tree> <branch1> <branch2>"; - -struct merge_list { - struct merge_list *next; - struct merge_list *link; /* other stages for this object */ - - unsigned int stage : 2; - unsigned int mode; - const char *path; - struct blob *blob; -}; - -static struct merge_list *merge_result, **merge_result_end = &merge_result; - -static void add_merge_entry(struct merge_list *entry) -{ - *merge_result_end = entry; - merge_result_end = &entry->next; -} - -static void merge_trees(struct tree_desc t[3], const char *base); - -static const char *explanation(struct merge_list *entry) -{ - switch (entry->stage) { - case 0: - return "merged"; - case 3: - return "added in remote"; - case 2: - if (entry->link) - return "added in both"; - return "added in local"; - } - - /* Existed in base */ - entry = entry->link; - if (!entry) - return "removed in both"; - - if (entry->link) - return "changed in both"; - - if (entry->stage == 3) - return "removed in local"; - return "removed in remote"; -} - -static void *result(struct merge_list *entry, unsigned long *size) -{ - enum object_type type; - struct blob *base, *our, *their; - const char *path = entry->path; - - if (!entry->stage) - return read_object_file(&entry->blob->object.oid, &type, size); - base = NULL; - if (entry->stage == 1) { - base = entry->blob; - entry = entry->link; - } - our = NULL; - if (entry && entry->stage == 2) { - our = entry->blob; - entry = entry->link; - } - their = NULL; - if (entry) - their = entry->blob; - return merge_blobs(the_repository->index, path, - base, our, their, size); -} - -static void *origin(struct merge_list *entry, unsigned long *size) -{ - enum object_type type; - while (entry) { - if (entry->stage == 2) - return read_object_file(&entry->blob->object.oid, - &type, size); - entry = entry->link; - } - return NULL; -} - -static int show_outf(void *priv_, mmbuffer_t *mb, int nbuf) -{ - int i; - for (i = 0; i < nbuf; i++) - printf("%.*s", (int) mb[i].size, mb[i].ptr); - return 0; -} - -static void show_diff(struct merge_list *entry) -{ - unsigned long size; - mmfile_t src, dst; - xpparam_t xpp; - xdemitconf_t xecfg; - xdemitcb_t ecb; - - xpp.flags = 0; - memset(&xecfg, 0, sizeof(xecfg)); - xecfg.ctxlen = 3; - ecb.out_hunk = NULL; - ecb.out_line = show_outf; - ecb.priv = NULL; - - src.ptr = origin(entry, &size); - if (!src.ptr) - size = 0; - src.size = size; - dst.ptr = result(entry, &size); - if (!dst.ptr) - size = 0; - dst.size = size; - if (xdi_diff(&src, &dst, &xpp, &xecfg, &ecb)) - die("unable to generate diff"); - free(src.ptr); - free(dst.ptr); -} - -static void show_result_list(struct merge_list *entry) -{ - printf("%s\n", explanation(entry)); - do { - struct merge_list *link = entry->link; - static const char *desc[4] = { "result", "base", "our", "their" }; - printf(" %-6s %o %s %s\n", desc[entry->stage], entry->mode, oid_to_hex(&entry->blob->object.oid), entry->path); - entry = link; - } while (entry); -} - -static void show_result(void) -{ - struct merge_list *walk; - - walk = merge_result; - while (walk) { - show_result_list(walk); - show_diff(walk); - walk = walk->next; - } -} - -/* An empty entry never compares same, not even to another empty entry */ -static int same_entry(struct name_entry *a, struct name_entry *b) -{ - return !is_null_oid(&a->oid) && - !is_null_oid(&b->oid) && - oideq(&a->oid, &b->oid) && - a->mode == b->mode; -} - -static int both_empty(struct name_entry *a, struct name_entry *b) -{ - return is_null_oid(&a->oid) && is_null_oid(&b->oid); -} - -static struct merge_list *create_entry(unsigned stage, unsigned mode, const struct object_id *oid, const char *path) -{ - struct merge_list *res = xcalloc(1, sizeof(*res)); - - res->stage = stage; - res->path = path; - res->mode = mode; - res->blob = lookup_blob(the_repository, oid); - return res; -} - -static char *traverse_path(const struct traverse_info *info, const struct name_entry *n) -{ - struct strbuf buf = STRBUF_INIT; - strbuf_make_traverse_path(&buf, info, n->path, n->pathlen); - return strbuf_detach(&buf, NULL); -} - -static void resolve(const struct traverse_info *info, struct name_entry *ours, struct name_entry *result) -{ - struct merge_list *orig, *final; - const char *path; - - /* If it's already ours, don't bother showing it */ - if (!ours) - return; - - path = traverse_path(info, result); - orig = create_entry(2, ours->mode, &ours->oid, path); - final = create_entry(0, result->mode, &result->oid, path); - - final->link = orig; - - add_merge_entry(final); -} - -static void unresolved_directory(const struct traverse_info *info, - struct name_entry n[3]) -{ - struct repository *r = the_repository; - char *newbase; - struct name_entry *p; - struct tree_desc t[3]; - void *buf0, *buf1, *buf2; - - for (p = n; p < n + 3; p++) { - if (p->mode && S_ISDIR(p->mode)) - break; - } - if (n + 3 <= p) - return; /* there is no tree here */ - - newbase = traverse_path(info, p); - -#define ENTRY_OID(e) (((e)->mode && S_ISDIR((e)->mode)) ? &(e)->oid : NULL) - buf0 = fill_tree_descriptor(r, t + 0, ENTRY_OID(n + 0)); - buf1 = fill_tree_descriptor(r, t + 1, ENTRY_OID(n + 1)); - buf2 = fill_tree_descriptor(r, t + 2, ENTRY_OID(n + 2)); -#undef ENTRY_OID - - merge_trees(t, newbase); - - free(buf0); - free(buf1); - free(buf2); - free(newbase); -} - - -static struct merge_list *link_entry(unsigned stage, const struct traverse_info *info, struct name_entry *n, struct merge_list *entry) -{ - const char *path; - struct merge_list *link; - - if (!n->mode) - return entry; - if (entry) - path = entry->path; - else - path = traverse_path(info, n); - link = create_entry(stage, n->mode, &n->oid, path); - link->link = entry; - return link; -} - -static void unresolved(const struct traverse_info *info, struct name_entry n[3]) -{ - struct merge_list *entry = NULL; - int i; - unsigned dirmask = 0, mask = 0; - - for (i = 0; i < 3; i++) { - mask |= (1 << i); - /* - * Treat missing entries as directories so that we return - * after unresolved_directory has handled this. - */ - if (!n[i].mode || S_ISDIR(n[i].mode)) - dirmask |= (1 << i); - } - - unresolved_directory(info, n); - - if (dirmask == mask) - return; - - if (n[2].mode && !S_ISDIR(n[2].mode)) - entry = link_entry(3, info, n + 2, entry); - if (n[1].mode && !S_ISDIR(n[1].mode)) - entry = link_entry(2, info, n + 1, entry); - if (n[0].mode && !S_ISDIR(n[0].mode)) - entry = link_entry(1, info, n + 0, entry); - - add_merge_entry(entry); -} - -/* - * Merge two trees together (t[1] and t[2]), using a common base (t[0]) - * as the origin. - * - * This walks the (sorted) trees in lock-step, checking every possible - * name. Note that directories automatically sort differently from other - * files (see "base_name_compare"), so you'll never see file/directory - * conflicts, because they won't ever compare the same. - * - * IOW, if a directory changes to a filename, it will automatically be - * seen as the directory going away, and the filename being created. - * - * Think of this as a three-way diff. - * - * The output will be either: - * - successful merge - * "0 mode sha1 filename" - * NOTE NOTE NOTE! FIXME! We really really need to walk the index - * in parallel with this too! - * - * - conflict: - * "1 mode sha1 filename" - * "2 mode sha1 filename" - * "3 mode sha1 filename" - * where not all of the 1/2/3 lines may exist, of course. - * - * The successful merge rules are the same as for the three-way merge - * in git-read-tree. - */ -static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info) -{ - /* Same in both? */ - if (same_entry(entry+1, entry+2) || both_empty(entry+1, entry+2)) { - /* Modified, added or removed identically */ - resolve(info, NULL, entry+1); - return mask; - } - - if (same_entry(entry+0, entry+1)) { - if (!is_null_oid(&entry[2].oid) && !S_ISDIR(entry[2].mode)) { - /* We did not touch, they modified -- take theirs */ - resolve(info, entry+1, entry+2); - return mask; - } - /* - * If we did not touch a directory but they made it - * into a file, we fall through and unresolved() - * recurses down. Likewise for the opposite case. - */ - } - - if (same_entry(entry+0, entry+2) || both_empty(entry+0, entry+2)) { - /* We added, modified or removed, they did not touch -- take ours */ - resolve(info, NULL, entry+1); - return mask; - } - - unresolved(info, entry); - return mask; -} - -static void merge_trees(struct tree_desc t[3], const char *base) -{ - struct traverse_info info; - - setup_traverse_info(&info, base); - info.fn = threeway_callback; - traverse_trees(&the_index, 3, t, &info); -} - -static void *get_tree_descriptor(struct repository *r, - struct tree_desc *desc, - const char *rev) -{ - struct object_id oid; - void *buf; - - if (repo_get_oid(r, rev, &oid)) - die("unknown rev %s", rev); - buf = fill_tree_descriptor(r, desc, &oid); - if (!buf) - die("%s is not a tree", rev); - return buf; -} - -int cmd_merge_tree(int argc, const char **argv, const char *prefix) -{ - struct repository *r = the_repository; - struct tree_desc t[3]; - void *buf1, *buf2, *buf3; - - if (argc != 4) - usage(merge_tree_usage); - - buf1 = get_tree_descriptor(r, t+0, argv[1]); - buf2 = get_tree_descriptor(r, t+1, argv[2]); - buf3 = get_tree_descriptor(r, t+2, argv[3]); - merge_trees(t, ""); - free(buf1); - free(buf2); - free(buf3); - - show_result(); - return 0; -} diff --git a/third_party/git/builtin/merge.c b/third_party/git/builtin/merge.c deleted file mode 100644 index 9d5359edc2f7..000000000000 --- a/third_party/git/builtin/merge.c +++ /dev/null @@ -1,1715 +0,0 @@ -/* - * Builtin "git merge" - * - * Copyright (c) 2008 Miklos Vajna <vmiklos@frugalware.org> - * - * Based on git-merge.sh by Junio C Hamano. - */ - -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "parse-options.h" -#include "builtin.h" -#include "lockfile.h" -#include "run-command.h" -#include "diff.h" -#include "refs.h" -#include "refspec.h" -#include "commit.h" -#include "diffcore.h" -#include "revision.h" -#include "unpack-trees.h" -#include "cache-tree.h" -#include "dir.h" -#include "utf8.h" -#include "log-tree.h" -#include "color.h" -#include "rerere.h" -#include "help.h" -#include "merge-recursive.h" -#include "resolve-undo.h" -#include "remote.h" -#include "fmt-merge-msg.h" -#include "gpg-interface.h" -#include "sequencer.h" -#include "string-list.h" -#include "packfile.h" -#include "tag.h" -#include "alias.h" -#include "branch.h" -#include "commit-reach.h" -#include "wt-status.h" -#include "commit-graph.h" - -#define DEFAULT_TWOHEAD (1<<0) -#define DEFAULT_OCTOPUS (1<<1) -#define NO_FAST_FORWARD (1<<2) -#define NO_TRIVIAL (1<<3) - -struct strategy { - const char *name; - unsigned attr; -}; - -static const char * const builtin_merge_usage[] = { - N_("git merge [<options>] [<commit>...]"), - N_("git merge --abort"), - N_("git merge --continue"), - NULL -}; - -static int show_diffstat = 1, shortlog_len = -1, squash; -static int option_commit = -1; -static int option_edit = -1; -static int allow_trivial = 1, have_message, verify_signatures; -static int check_trust_level = 1; -static int overwrite_ignore = 1; -static struct strbuf merge_msg = STRBUF_INIT; -static struct strategy **use_strategies; -static size_t use_strategies_nr, use_strategies_alloc; -static const char **xopts; -static size_t xopts_nr, xopts_alloc; -static const char *branch; -static char *branch_mergeoptions; -static int verbosity; -static int allow_rerere_auto; -static int abort_current_merge; -static int quit_current_merge; -static int continue_current_merge; -static int allow_unrelated_histories; -static int show_progress = -1; -static int default_to_upstream = 1; -static int signoff; -static const char *sign_commit; -static int autostash; -static int no_verify; - -static struct strategy all_strategy[] = { - { "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL }, - { "octopus", DEFAULT_OCTOPUS }, - { "resolve", 0 }, - { "ours", NO_FAST_FORWARD | NO_TRIVIAL }, - { "subtree", NO_FAST_FORWARD | NO_TRIVIAL }, -}; - -static const char *pull_twohead, *pull_octopus; - -enum ff_type { - FF_NO, - FF_ALLOW, - FF_ONLY -}; - -static enum ff_type fast_forward = FF_ALLOW; - -static const char *cleanup_arg; -static enum commit_msg_cleanup_mode cleanup_mode; - -static int option_parse_message(const struct option *opt, - const char *arg, int unset) -{ - struct strbuf *buf = opt->value; - - if (unset) - strbuf_setlen(buf, 0); - else if (arg) { - strbuf_addf(buf, "%s%s", buf->len ? "\n\n" : "", arg); - have_message = 1; - } else - return error(_("switch `m' requires a value")); - return 0; -} - -static enum parse_opt_result option_read_message(struct parse_opt_ctx_t *ctx, - const struct option *opt, - const char *arg_not_used, - int unset) -{ - struct strbuf *buf = opt->value; - const char *arg; - - BUG_ON_OPT_ARG(arg_not_used); - if (unset) - BUG("-F cannot be negated"); - - if (ctx->opt) { - arg = ctx->opt; - ctx->opt = NULL; - } else if (ctx->argc > 1) { - ctx->argc--; - arg = *++ctx->argv; - } else - return error(_("option `%s' requires a value"), opt->long_name); - - if (buf->len) - strbuf_addch(buf, '\n'); - if (ctx->prefix && !is_absolute_path(arg)) - arg = prefix_filename(ctx->prefix, arg); - if (strbuf_read_file(buf, arg, 0) < 0) - return error(_("could not read file '%s'"), arg); - have_message = 1; - - return 0; -} - -static struct strategy *get_strategy(const char *name) -{ - int i; - struct strategy *ret; - static struct cmdnames main_cmds, other_cmds; - static int loaded; - - if (!name) - return NULL; - - for (i = 0; i < ARRAY_SIZE(all_strategy); i++) - if (!strcmp(name, all_strategy[i].name)) - return &all_strategy[i]; - - if (!loaded) { - struct cmdnames not_strategies; - loaded = 1; - - memset(¬_strategies, 0, sizeof(struct cmdnames)); - load_command_list("git-merge-", &main_cmds, &other_cmds); - for (i = 0; i < main_cmds.cnt; i++) { - int j, found = 0; - struct cmdname *ent = main_cmds.names[i]; - for (j = 0; j < ARRAY_SIZE(all_strategy); j++) - if (!strncmp(ent->name, all_strategy[j].name, ent->len) - && !all_strategy[j].name[ent->len]) - found = 1; - if (!found) - add_cmdname(¬_strategies, ent->name, ent->len); - } - exclude_cmds(&main_cmds, ¬_strategies); - } - if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) { - fprintf(stderr, _("Could not find merge strategy '%s'.\n"), name); - fprintf(stderr, _("Available strategies are:")); - for (i = 0; i < main_cmds.cnt; i++) - fprintf(stderr, " %s", main_cmds.names[i]->name); - fprintf(stderr, ".\n"); - if (other_cmds.cnt) { - fprintf(stderr, _("Available custom strategies are:")); - for (i = 0; i < other_cmds.cnt; i++) - fprintf(stderr, " %s", other_cmds.names[i]->name); - fprintf(stderr, ".\n"); - } - exit(1); - } - - ret = xcalloc(1, sizeof(struct strategy)); - ret->name = xstrdup(name); - ret->attr = NO_TRIVIAL; - return ret; -} - -static void append_strategy(struct strategy *s) -{ - ALLOC_GROW(use_strategies, use_strategies_nr + 1, use_strategies_alloc); - use_strategies[use_strategies_nr++] = s; -} - -static int option_parse_strategy(const struct option *opt, - const char *name, int unset) -{ - if (unset) - return 0; - - append_strategy(get_strategy(name)); - return 0; -} - -static int option_parse_x(const struct option *opt, - const char *arg, int unset) -{ - if (unset) - return 0; - - ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc); - xopts[xopts_nr++] = xstrdup(arg); - return 0; -} - -static int option_parse_n(const struct option *opt, - const char *arg, int unset) -{ - BUG_ON_OPT_ARG(arg); - show_diffstat = unset; - return 0; -} - -static struct option builtin_merge_options[] = { - OPT_CALLBACK_F('n', NULL, NULL, NULL, - N_("do not show a diffstat at the end of the merge"), - PARSE_OPT_NOARG, option_parse_n), - OPT_BOOL(0, "stat", &show_diffstat, - N_("show a diffstat at the end of the merge")), - OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")), - { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"), - N_("add (at most <n>) entries from shortlog to merge commit message"), - PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN }, - OPT_BOOL(0, "squash", &squash, - N_("create a single commit instead of doing a merge")), - OPT_BOOL(0, "commit", &option_commit, - N_("perform a commit if the merge succeeds (default)")), - OPT_BOOL('e', "edit", &option_edit, - N_("edit message before committing")), - OPT_CLEANUP(&cleanup_arg), - OPT_SET_INT(0, "ff", &fast_forward, N_("allow fast-forward (default)"), FF_ALLOW), - OPT_SET_INT_F(0, "ff-only", &fast_forward, - N_("abort if fast-forward is not possible"), - FF_ONLY, PARSE_OPT_NONEG), - OPT_RERERE_AUTOUPDATE(&allow_rerere_auto), - OPT_BOOL(0, "verify-signatures", &verify_signatures, - N_("verify that the named commit has a valid GPG signature")), - OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"), - N_("merge strategy to use"), option_parse_strategy), - OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"), - N_("option for selected merge strategy"), option_parse_x), - OPT_CALLBACK('m', "message", &merge_msg, N_("message"), - N_("merge commit message (for a non-fast-forward merge)"), - option_parse_message), - { OPTION_LOWLEVEL_CALLBACK, 'F', "file", &merge_msg, N_("path"), - N_("read message from file"), PARSE_OPT_NONEG, - NULL, 0, option_read_message }, - OPT__VERBOSITY(&verbosity), - OPT_BOOL(0, "abort", &abort_current_merge, - N_("abort the current in-progress merge")), - OPT_BOOL(0, "quit", &quit_current_merge, - N_("--abort but leave index and working tree alone")), - OPT_BOOL(0, "continue", &continue_current_merge, - N_("continue the current in-progress merge")), - OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories, - N_("allow merging unrelated histories")), - OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1), - { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), - N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - OPT_AUTOSTASH(&autostash), - OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")), - OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")), - OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")), - OPT_END() -}; - -static int save_state(struct object_id *stash) -{ - int len; - struct child_process cp = CHILD_PROCESS_INIT; - struct strbuf buffer = STRBUF_INIT; - const char *argv[] = {"stash", "create", NULL}; - int rc = -1; - - cp.argv = argv; - cp.out = -1; - cp.git_cmd = 1; - - if (start_command(&cp)) - die(_("could not run stash.")); - len = strbuf_read(&buffer, cp.out, 1024); - close(cp.out); - - if (finish_command(&cp) || len < 0) - die(_("stash failed")); - else if (!len) /* no changes */ - goto out; - strbuf_setlen(&buffer, buffer.len-1); - if (get_oid(buffer.buf, stash)) - die(_("not a valid object: %s"), buffer.buf); - rc = 0; -out: - strbuf_release(&buffer); - return rc; -} - -static void read_empty(const struct object_id *oid, int verbose) -{ - int i = 0; - const char *args[7]; - - args[i++] = "read-tree"; - if (verbose) - args[i++] = "-v"; - args[i++] = "-m"; - args[i++] = "-u"; - args[i++] = empty_tree_oid_hex(); - args[i++] = oid_to_hex(oid); - args[i] = NULL; - - if (run_command_v_opt(args, RUN_GIT_CMD)) - die(_("read-tree failed")); -} - -static void reset_hard(const struct object_id *oid, int verbose) -{ - int i = 0; - const char *args[6]; - - args[i++] = "read-tree"; - if (verbose) - args[i++] = "-v"; - args[i++] = "--reset"; - args[i++] = "-u"; - args[i++] = oid_to_hex(oid); - args[i] = NULL; - - if (run_command_v_opt(args, RUN_GIT_CMD)) - die(_("read-tree failed")); -} - -static void restore_state(const struct object_id *head, - const struct object_id *stash) -{ - struct strbuf sb = STRBUF_INIT; - const char *args[] = { "stash", "apply", NULL, NULL }; - - if (is_null_oid(stash)) - return; - - reset_hard(head, 1); - - args[2] = oid_to_hex(stash); - - /* - * It is OK to ignore error here, for example when there was - * nothing to restore. - */ - run_command_v_opt(args, RUN_GIT_CMD); - - strbuf_release(&sb); - refresh_cache(REFRESH_QUIET); -} - -/* This is called when no merge was necessary. */ -static void finish_up_to_date(const char *msg) -{ - if (verbosity >= 0) - printf("%s%s\n", squash ? _(" (nothing to squash)") : "", msg); - remove_merge_branch_state(the_repository); -} - -static void squash_message(struct commit *commit, struct commit_list *remoteheads) -{ - struct rev_info rev; - struct strbuf out = STRBUF_INIT; - struct commit_list *j; - struct pretty_print_context ctx = {0}; - - printf(_("Squash commit -- not updating HEAD\n")); - - repo_init_revisions(the_repository, &rev, NULL); - rev.ignore_merges = 1; - rev.commit_format = CMIT_FMT_MEDIUM; - - commit->object.flags |= UNINTERESTING; - add_pending_object(&rev, &commit->object, NULL); - - for (j = remoteheads; j; j = j->next) - add_pending_object(&rev, &j->item->object, NULL); - - setup_revisions(0, NULL, &rev, NULL); - if (prepare_revision_walk(&rev)) - die(_("revision walk setup failed")); - - ctx.abbrev = rev.abbrev; - ctx.date_mode = rev.date_mode; - ctx.fmt = rev.commit_format; - - strbuf_addstr(&out, "Squashed commit of the following:\n"); - while ((commit = get_revision(&rev)) != NULL) { - strbuf_addch(&out, '\n'); - strbuf_addf(&out, "commit %s\n", - oid_to_hex(&commit->object.oid)); - pretty_print_commit(&ctx, commit, &out); - } - write_file_buf(git_path_squash_msg(the_repository), out.buf, out.len); - strbuf_release(&out); -} - -static void finish(struct commit *head_commit, - struct commit_list *remoteheads, - const struct object_id *new_head, const char *msg) -{ - struct strbuf reflog_message = STRBUF_INIT; - const struct object_id *head = &head_commit->object.oid; - - if (!msg) - strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION")); - else { - if (verbosity >= 0) - printf("%s\n", msg); - strbuf_addf(&reflog_message, "%s: %s", - getenv("GIT_REFLOG_ACTION"), msg); - } - if (squash) { - squash_message(head_commit, remoteheads); - } else { - if (verbosity >= 0 && !merge_msg.len) - printf(_("No merge message -- not updating HEAD\n")); - else { - update_ref(reflog_message.buf, "HEAD", new_head, head, - 0, UPDATE_REFS_DIE_ON_ERR); - /* - * We ignore errors in 'gc --auto', since the - * user should see them. - */ - close_object_store(the_repository->objects); - run_auto_maintenance(verbosity < 0); - } - } - if (new_head && show_diffstat) { - struct diff_options opts; - repo_diff_setup(the_repository, &opts); - opts.stat_width = -1; /* use full terminal width */ - opts.stat_graph_width = -1; /* respect statGraphWidth config */ - opts.output_format |= - DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; - opts.detect_rename = DIFF_DETECT_RENAME; - diff_setup_done(&opts); - diff_tree_oid(head, new_head, "", &opts); - diffcore_std(&opts); - diff_flush(&opts); - } - - /* Run a post-merge hook */ - run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL); - - apply_autostash(git_path_merge_autostash(the_repository)); - strbuf_release(&reflog_message); -} - -/* Get the name for the merge commit's message. */ -static void merge_name(const char *remote, struct strbuf *msg) -{ - struct commit *remote_head; - struct object_id branch_head; - struct strbuf buf = STRBUF_INIT; - struct strbuf bname = STRBUF_INIT; - struct merge_remote_desc *desc; - const char *ptr; - char *found_ref; - int len, early; - - strbuf_branchname(&bname, remote, 0); - remote = bname.buf; - - oidclr(&branch_head); - remote_head = get_merge_parent(remote); - if (!remote_head) - die(_("'%s' does not point to a commit"), remote); - - if (dwim_ref(remote, strlen(remote), &branch_head, &found_ref, 0) > 0) { - if (starts_with(found_ref, "refs/heads/")) { - strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", - oid_to_hex(&branch_head), remote); - goto cleanup; - } - if (starts_with(found_ref, "refs/tags/")) { - strbuf_addf(msg, "%s\t\ttag '%s' of .\n", - oid_to_hex(&branch_head), remote); - goto cleanup; - } - if (starts_with(found_ref, "refs/remotes/")) { - strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n", - oid_to_hex(&branch_head), remote); - goto cleanup; - } - } - - /* See if remote matches <name>^^^.. or <name>~<number> */ - for (len = 0, ptr = remote + strlen(remote); - remote < ptr && ptr[-1] == '^'; - ptr--) - len++; - if (len) - early = 1; - else { - early = 0; - ptr = strrchr(remote, '~'); - if (ptr) { - int seen_nonzero = 0; - - len++; /* count ~ */ - while (*++ptr && isdigit(*ptr)) { - seen_nonzero |= (*ptr != '0'); - len++; - } - if (*ptr) - len = 0; /* not ...~<number> */ - else if (seen_nonzero) - early = 1; - else if (len == 1) - early = 1; /* "name~" is "name~1"! */ - } - } - if (len) { - struct strbuf truname = STRBUF_INIT; - strbuf_addf(&truname, "refs/heads/%s", remote); - strbuf_setlen(&truname, truname.len - len); - if (ref_exists(truname.buf)) { - strbuf_addf(msg, - "%s\t\tbranch '%s'%s of .\n", - oid_to_hex(&remote_head->object.oid), - truname.buf + 11, - (early ? " (early part)" : "")); - strbuf_release(&truname); - goto cleanup; - } - strbuf_release(&truname); - } - - desc = merge_remote_util(remote_head); - if (desc && desc->obj && desc->obj->type == OBJ_TAG) { - strbuf_addf(msg, "%s\t\t%s '%s'\n", - oid_to_hex(&desc->obj->oid), - type_name(desc->obj->type), - remote); - goto cleanup; - } - - strbuf_addf(msg, "%s\t\tcommit '%s'\n", - oid_to_hex(&remote_head->object.oid), remote); -cleanup: - strbuf_release(&buf); - strbuf_release(&bname); -} - -static void parse_branch_merge_options(char *bmo) -{ - const char **argv; - int argc; - - if (!bmo) - return; - argc = split_cmdline(bmo, &argv); - if (argc < 0) - die(_("Bad branch.%s.mergeoptions string: %s"), branch, - _(split_cmdline_strerror(argc))); - REALLOC_ARRAY(argv, argc + 2); - MOVE_ARRAY(argv + 1, argv, argc + 1); - argc++; - argv[0] = "branch.*.mergeoptions"; - parse_options(argc, argv, NULL, builtin_merge_options, - builtin_merge_usage, 0); - free(argv); -} - -static int git_merge_config(const char *k, const char *v, void *cb) -{ - int status; - const char *str; - - if (branch && - skip_prefix(k, "branch.", &str) && - skip_prefix(str, branch, &str) && - !strcmp(str, ".mergeoptions")) { - free(branch_mergeoptions); - branch_mergeoptions = xstrdup(v); - return 0; - } - - if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) - show_diffstat = git_config_bool(k, v); - else if (!strcmp(k, "merge.verifysignatures")) - verify_signatures = git_config_bool(k, v); - else if (!strcmp(k, "pull.twohead")) - return git_config_string(&pull_twohead, k, v); - else if (!strcmp(k, "pull.octopus")) - return git_config_string(&pull_octopus, k, v); - else if (!strcmp(k, "commit.cleanup")) - return git_config_string(&cleanup_arg, k, v); - else if (!strcmp(k, "merge.ff")) { - int boolval = git_parse_maybe_bool(v); - if (0 <= boolval) { - fast_forward = boolval ? FF_ALLOW : FF_NO; - } else if (v && !strcmp(v, "only")) { - fast_forward = FF_ONLY; - } /* do not barf on values from future versions of git */ - return 0; - } else if (!strcmp(k, "merge.defaulttoupstream")) { - default_to_upstream = git_config_bool(k, v); - return 0; - } else if (!strcmp(k, "commit.gpgsign")) { - sign_commit = git_config_bool(k, v) ? "" : NULL; - return 0; - } else if (!strcmp(k, "gpg.mintrustlevel")) { - check_trust_level = 0; - } else if (!strcmp(k, "merge.autostash")) { - autostash = git_config_bool(k, v); - return 0; - } - - status = fmt_merge_msg_config(k, v, cb); - if (status) - return status; - status = git_gpg_config(k, v, NULL); - if (status) - return status; - return git_diff_ui_config(k, v, cb); -} - -static int read_tree_trivial(struct object_id *common, struct object_id *head, - struct object_id *one) -{ - int i, nr_trees = 0; - struct tree *trees[MAX_UNPACK_TREES]; - struct tree_desc t[MAX_UNPACK_TREES]; - struct unpack_trees_options opts; - - memset(&opts, 0, sizeof(opts)); - opts.head_idx = 2; - opts.src_index = &the_index; - opts.dst_index = &the_index; - opts.update = 1; - opts.verbose_update = 1; - opts.trivial_merges_only = 1; - opts.merge = 1; - trees[nr_trees] = parse_tree_indirect(common); - if (!trees[nr_trees++]) - return -1; - trees[nr_trees] = parse_tree_indirect(head); - if (!trees[nr_trees++]) - return -1; - trees[nr_trees] = parse_tree_indirect(one); - if (!trees[nr_trees++]) - return -1; - opts.fn = threeway_merge; - cache_tree_free(&active_cache_tree); - for (i = 0; i < nr_trees; i++) { - parse_tree(trees[i]); - init_tree_desc(t+i, trees[i]->buffer, trees[i]->size); - } - if (unpack_trees(nr_trees, t, &opts)) - return -1; - return 0; -} - -static void write_tree_trivial(struct object_id *oid) -{ - if (write_cache_as_tree(oid, 0, NULL)) - die(_("git write-tree failed to write a tree")); -} - -static int try_merge_strategy(const char *strategy, struct commit_list *common, - struct commit_list *remoteheads, - struct commit *head) -{ - const char *head_arg = "HEAD"; - - if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0) - return error(_("Unable to write index.")); - - if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) { - struct lock_file lock = LOCK_INIT; - int clean, x; - struct commit *result; - struct commit_list *reversed = NULL; - struct merge_options o; - struct commit_list *j; - - if (remoteheads->next) { - error(_("Not handling anything other than two heads merge.")); - return 2; - } - - init_merge_options(&o, the_repository); - if (!strcmp(strategy, "subtree")) - o.subtree_shift = ""; - - o.show_rename_progress = - show_progress == -1 ? isatty(2) : show_progress; - - for (x = 0; x < xopts_nr; x++) - if (parse_merge_opt(&o, xopts[x])) - die(_("Unknown option for merge-recursive: -X%s"), xopts[x]); - - o.branch1 = head_arg; - o.branch2 = merge_remote_util(remoteheads->item)->name; - - for (j = common; j; j = j->next) - commit_list_insert(j->item, &reversed); - - hold_locked_index(&lock, LOCK_DIE_ON_ERROR); - clean = merge_recursive(&o, head, - remoteheads->item, reversed, &result); - if (clean < 0) - exit(128); - if (write_locked_index(&the_index, &lock, - COMMIT_LOCK | SKIP_IF_UNCHANGED)) - die(_("unable to write %s"), get_index_file()); - return clean ? 0 : 1; - } else { - return try_merge_command(the_repository, - strategy, xopts_nr, xopts, - common, head_arg, remoteheads); - } -} - -static void count_diff_files(struct diff_queue_struct *q, - struct diff_options *opt, void *data) -{ - int *count = data; - - (*count) += q->nr; -} - -static int count_unmerged_entries(void) -{ - int i, ret = 0; - - for (i = 0; i < active_nr; i++) - if (ce_stage(active_cache[i])) - ret++; - - return ret; -} - -static void add_strategies(const char *string, unsigned attr) -{ - int i; - - if (string) { - struct string_list list = STRING_LIST_INIT_DUP; - struct string_list_item *item; - string_list_split(&list, string, ' ', -1); - for_each_string_list_item(item, &list) - append_strategy(get_strategy(item->string)); - string_list_clear(&list, 0); - return; - } - for (i = 0; i < ARRAY_SIZE(all_strategy); i++) - if (all_strategy[i].attr & attr) - append_strategy(&all_strategy[i]); - -} - -static void read_merge_msg(struct strbuf *msg) -{ - const char *filename = git_path_merge_msg(the_repository); - strbuf_reset(msg); - if (strbuf_read_file(msg, filename, 0) < 0) - die_errno(_("Could not read from '%s'"), filename); -} - -static void write_merge_state(struct commit_list *); -static void abort_commit(struct commit_list *remoteheads, const char *err_msg) -{ - if (err_msg) - error("%s", err_msg); - fprintf(stderr, - _("Not committing merge; use 'git commit' to complete the merge.\n")); - write_merge_state(remoteheads); - exit(1); -} - -static const char merge_editor_comment[] = -N_("Please enter a commit message to explain why this merge is necessary,\n" - "especially if it merges an updated upstream into a topic branch.\n" - "\n"); - -static const char scissors_editor_comment[] = -N_("An empty message aborts the commit.\n"); - -static const char no_scissors_editor_comment[] = -N_("Lines starting with '%c' will be ignored, and an empty message aborts\n" - "the commit.\n"); - -static void write_merge_heads(struct commit_list *); -static void prepare_to_commit(struct commit_list *remoteheads) -{ - struct strbuf msg = STRBUF_INIT; - const char *index_file = get_index_file(); - - if (!no_verify && run_commit_hook(0 < option_edit, index_file, "pre-merge-commit", NULL)) - abort_commit(remoteheads, NULL); - /* - * Re-read the index as pre-merge-commit hook could have updated it, - * and write it out as a tree. We must do this before we invoke - * the editor and after we invoke run_status above. - */ - if (find_hook("pre-merge-commit")) - discard_cache(); - read_cache_from(index_file); - strbuf_addbuf(&msg, &merge_msg); - if (squash) - BUG("the control must not reach here under --squash"); - if (0 < option_edit) { - strbuf_addch(&msg, '\n'); - if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS) { - wt_status_append_cut_line(&msg); - strbuf_commented_addf(&msg, "\n"); - } - strbuf_commented_addf(&msg, _(merge_editor_comment)); - strbuf_commented_addf(&msg, _(cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS ? - scissors_editor_comment : - no_scissors_editor_comment), comment_line_char); - } - if (signoff) - append_signoff(&msg, ignore_non_trailer(msg.buf, msg.len), 0); - write_merge_heads(remoteheads); - write_file_buf(git_path_merge_msg(the_repository), msg.buf, msg.len); - if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg", - git_path_merge_msg(the_repository), "merge", NULL)) - abort_commit(remoteheads, NULL); - if (0 < option_edit) { - if (launch_editor(git_path_merge_msg(the_repository), NULL, NULL)) - abort_commit(remoteheads, NULL); - } - - if (!no_verify && run_commit_hook(0 < option_edit, get_index_file(), - "commit-msg", - git_path_merge_msg(the_repository), NULL)) - abort_commit(remoteheads, NULL); - - read_merge_msg(&msg); - cleanup_message(&msg, cleanup_mode, 0); - if (!msg.len) - abort_commit(remoteheads, _("Empty commit message.")); - strbuf_release(&merge_msg); - strbuf_addbuf(&merge_msg, &msg); - strbuf_release(&msg); -} - -static int merge_trivial(struct commit *head, struct commit_list *remoteheads) -{ - struct object_id result_tree, result_commit; - struct commit_list *parents, **pptr = &parents; - - if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0) - return error(_("Unable to write index.")); - - write_tree_trivial(&result_tree); - printf(_("Wonderful.\n")); - pptr = commit_list_append(head, pptr); - pptr = commit_list_append(remoteheads->item, pptr); - prepare_to_commit(remoteheads); - if (commit_tree(merge_msg.buf, merge_msg.len, &result_tree, parents, - &result_commit, NULL, sign_commit)) - die(_("failed to write commit object")); - finish(head, remoteheads, &result_commit, "In-index merge"); - remove_merge_branch_state(the_repository); - return 0; -} - -static int finish_automerge(struct commit *head, - int head_subsumed, - struct commit_list *common, - struct commit_list *remoteheads, - struct object_id *result_tree, - const char *wt_strategy) -{ - struct commit_list *parents = NULL; - struct strbuf buf = STRBUF_INIT; - struct object_id result_commit; - - write_tree_trivial(result_tree); - free_commit_list(common); - parents = remoteheads; - if (!head_subsumed || fast_forward == FF_NO) - commit_list_insert(head, &parents); - prepare_to_commit(remoteheads); - if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents, - &result_commit, NULL, sign_commit)) - die(_("failed to write commit object")); - strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy); - finish(head, remoteheads, &result_commit, buf.buf); - strbuf_release(&buf); - remove_merge_branch_state(the_repository); - return 0; -} - -static int suggest_conflicts(void) -{ - const char *filename; - FILE *fp; - struct strbuf msgbuf = STRBUF_INIT; - - filename = git_path_merge_msg(the_repository); - fp = xfopen(filename, "a"); - - /* - * We can't use cleanup_mode because if we're not using the editor, - * get_cleanup_mode will return COMMIT_MSG_CLEANUP_SPACE instead, even - * though the message is meant to be processed later by git-commit. - * Thus, we will get the cleanup mode which is returned when we _are_ - * using an editor. - */ - append_conflicts_hint(&the_index, &msgbuf, - get_cleanup_mode(cleanup_arg, 1)); - fputs(msgbuf.buf, fp); - strbuf_release(&msgbuf); - fclose(fp); - repo_rerere(the_repository, allow_rerere_auto); - printf(_("Automatic merge failed; " - "fix conflicts and then commit the result.\n")); - return 1; -} - -static int evaluate_result(void) -{ - int cnt = 0; - struct rev_info rev; - - /* Check how many files differ. */ - repo_init_revisions(the_repository, &rev, ""); - setup_revisions(0, NULL, &rev, NULL); - rev.diffopt.output_format |= - DIFF_FORMAT_CALLBACK; - rev.diffopt.format_callback = count_diff_files; - rev.diffopt.format_callback_data = &cnt; - run_diff_files(&rev, 0); - - /* - * Check how many unmerged entries are - * there. - */ - cnt += count_unmerged_entries(); - - return cnt; -} - -/* - * Pretend as if the user told us to merge with the remote-tracking - * branch we have for the upstream of the current branch - */ -static int setup_with_upstream(const char ***argv) -{ - struct branch *branch = branch_get(NULL); - int i; - const char **args; - - if (!branch) - die(_("No current branch.")); - if (!branch->remote_name) - die(_("No remote for the current branch.")); - if (!branch->merge_nr) - die(_("No default upstream defined for the current branch.")); - - args = xcalloc(st_add(branch->merge_nr, 1), sizeof(char *)); - for (i = 0; i < branch->merge_nr; i++) { - if (!branch->merge[i]->dst) - die(_("No remote-tracking branch for %s from %s"), - branch->merge[i]->src, branch->remote_name); - args[i] = branch->merge[i]->dst; - } - args[i] = NULL; - *argv = args; - return i; -} - -static void write_merge_heads(struct commit_list *remoteheads) -{ - struct commit_list *j; - struct strbuf buf = STRBUF_INIT; - - for (j = remoteheads; j; j = j->next) { - struct object_id *oid; - struct commit *c = j->item; - struct merge_remote_desc *desc; - - desc = merge_remote_util(c); - if (desc && desc->obj) { - oid = &desc->obj->oid; - } else { - oid = &c->object.oid; - } - strbuf_addf(&buf, "%s\n", oid_to_hex(oid)); - } - write_file_buf(git_path_merge_head(the_repository), buf.buf, buf.len); - - strbuf_reset(&buf); - if (fast_forward == FF_NO) - strbuf_addstr(&buf, "no-ff"); - write_file_buf(git_path_merge_mode(the_repository), buf.buf, buf.len); - strbuf_release(&buf); -} - -static void write_merge_state(struct commit_list *remoteheads) -{ - write_merge_heads(remoteheads); - strbuf_addch(&merge_msg, '\n'); - write_file_buf(git_path_merge_msg(the_repository), merge_msg.buf, - merge_msg.len); -} - -static int default_edit_option(void) -{ - static const char name[] = "GIT_MERGE_AUTOEDIT"; - const char *e = getenv(name); - struct stat st_stdin, st_stdout; - - if (have_message) - /* an explicit -m msg without --[no-]edit */ - return 0; - - if (e) { - int v = git_parse_maybe_bool(e); - if (v < 0) - die(_("Bad value '%s' in environment '%s'"), e, name); - return v; - } - - /* Use editor if stdin and stdout are the same and is a tty */ - return (!fstat(0, &st_stdin) && - !fstat(1, &st_stdout) && - isatty(0) && isatty(1) && - st_stdin.st_dev == st_stdout.st_dev && - st_stdin.st_ino == st_stdout.st_ino && - st_stdin.st_mode == st_stdout.st_mode); -} - -static struct commit_list *reduce_parents(struct commit *head_commit, - int *head_subsumed, - struct commit_list *remoteheads) -{ - struct commit_list *parents, **remotes; - - /* - * Is the current HEAD reachable from another commit being - * merged? If so we do not want to record it as a parent of - * the resulting merge, unless --no-ff is given. We will flip - * this variable to 0 when we find HEAD among the independent - * tips being merged. - */ - *head_subsumed = 1; - - /* Find what parents to record by checking independent ones. */ - parents = reduce_heads(remoteheads); - free_commit_list(remoteheads); - - remoteheads = NULL; - remotes = &remoteheads; - while (parents) { - struct commit *commit = pop_commit(&parents); - if (commit == head_commit) - *head_subsumed = 0; - else - remotes = &commit_list_insert(commit, remotes)->next; - } - return remoteheads; -} - -static void prepare_merge_message(struct strbuf *merge_names, struct strbuf *merge_msg) -{ - struct fmt_merge_msg_opts opts; - - memset(&opts, 0, sizeof(opts)); - opts.add_title = !have_message; - opts.shortlog_len = shortlog_len; - opts.credit_people = (0 < option_edit); - - fmt_merge_msg(merge_names, merge_msg, &opts); - if (merge_msg->len) - strbuf_setlen(merge_msg, merge_msg->len - 1); -} - -static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge_names) -{ - const char *filename; - int fd, pos, npos; - struct strbuf fetch_head_file = STRBUF_INIT; - const unsigned hexsz = the_hash_algo->hexsz; - - if (!merge_names) - merge_names = &fetch_head_file; - - filename = git_path_fetch_head(the_repository); - fd = open(filename, O_RDONLY); - if (fd < 0) - die_errno(_("could not open '%s' for reading"), filename); - - if (strbuf_read(merge_names, fd, 0) < 0) - die_errno(_("could not read '%s'"), filename); - if (close(fd) < 0) - die_errno(_("could not close '%s'"), filename); - - for (pos = 0; pos < merge_names->len; pos = npos) { - struct object_id oid; - char *ptr; - struct commit *commit; - - ptr = strchr(merge_names->buf + pos, '\n'); - if (ptr) - npos = ptr - merge_names->buf + 1; - else - npos = merge_names->len; - - if (npos - pos < hexsz + 2 || - get_oid_hex(merge_names->buf + pos, &oid)) - commit = NULL; /* bad */ - else if (memcmp(merge_names->buf + pos + hexsz, "\t\t", 2)) - continue; /* not-for-merge */ - else { - char saved = merge_names->buf[pos + hexsz]; - merge_names->buf[pos + hexsz] = '\0'; - commit = get_merge_parent(merge_names->buf + pos); - merge_names->buf[pos + hexsz] = saved; - } - if (!commit) { - if (ptr) - *ptr = '\0'; - die(_("not something we can merge in %s: %s"), - filename, merge_names->buf + pos); - } - remotes = &commit_list_insert(commit, remotes)->next; - } - - if (merge_names == &fetch_head_file) - strbuf_release(&fetch_head_file); -} - -static struct commit_list *collect_parents(struct commit *head_commit, - int *head_subsumed, - int argc, const char **argv, - struct strbuf *merge_msg) -{ - int i; - struct commit_list *remoteheads = NULL; - struct commit_list **remotes = &remoteheads; - struct strbuf merge_names = STRBUF_INIT, *autogen = NULL; - - if (merge_msg && (!have_message || shortlog_len)) - autogen = &merge_names; - - if (head_commit) - remotes = &commit_list_insert(head_commit, remotes)->next; - - if (argc == 1 && !strcmp(argv[0], "FETCH_HEAD")) { - handle_fetch_head(remotes, autogen); - remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads); - } else { - for (i = 0; i < argc; i++) { - struct commit *commit = get_merge_parent(argv[i]); - if (!commit) - help_unknown_ref(argv[i], "merge", - _("not something we can merge")); - remotes = &commit_list_insert(commit, remotes)->next; - } - remoteheads = reduce_parents(head_commit, head_subsumed, remoteheads); - if (autogen) { - struct commit_list *p; - for (p = remoteheads; p; p = p->next) - merge_name(merge_remote_util(p->item)->name, autogen); - } - } - - if (autogen) { - prepare_merge_message(autogen, merge_msg); - strbuf_release(autogen); - } - - return remoteheads; -} - -static int merging_a_throwaway_tag(struct commit *commit) -{ - char *tag_ref; - struct object_id oid; - int is_throwaway_tag = 0; - - /* Are we merging a tag? */ - if (!merge_remote_util(commit) || - !merge_remote_util(commit)->obj || - merge_remote_util(commit)->obj->type != OBJ_TAG) - return is_throwaway_tag; - - /* - * Now we know we are merging a tag object. Are we downstream - * and following the tags from upstream? If so, we must have - * the tag object pointed at by "refs/tags/$T" where $T is the - * tagname recorded in the tag object. We want to allow such - * a "just to catch up" merge to fast-forward. - * - * Otherwise, we are playing an integrator's role, making a - * merge with a throw-away tag from a contributor with - * something like "git pull $contributor $signed_tag". - * We want to forbid such a merge from fast-forwarding - * by default; otherwise we would not keep the signature - * anywhere. - */ - tag_ref = xstrfmt("refs/tags/%s", - ((struct tag *)merge_remote_util(commit)->obj)->tag); - if (!read_ref(tag_ref, &oid) && - oideq(&oid, &merge_remote_util(commit)->obj->oid)) - is_throwaway_tag = 0; - else - is_throwaway_tag = 1; - free(tag_ref); - return is_throwaway_tag; -} - -int cmd_merge(int argc, const char **argv, const char *prefix) -{ - struct object_id result_tree, stash, head_oid; - struct commit *head_commit; - struct strbuf buf = STRBUF_INIT; - int i, ret = 0, head_subsumed; - int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0; - struct commit_list *common = NULL; - const char *best_strategy = NULL, *wt_strategy = NULL; - struct commit_list *remoteheads, *p; - void *branch_to_free; - int orig_argc = argc; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage_with_options(builtin_merge_usage, builtin_merge_options); - - /* - * Check if we are _not_ on a detached HEAD, i.e. if there is a - * current branch. - */ - branch = branch_to_free = resolve_refdup("HEAD", 0, &head_oid, NULL); - if (branch) - skip_prefix(branch, "refs/heads/", &branch); - - init_diff_ui_defaults(); - git_config(git_merge_config, NULL); - - if (!branch || is_null_oid(&head_oid)) - head_commit = NULL; - else - head_commit = lookup_commit_or_die(&head_oid, "HEAD"); - - if (branch_mergeoptions) - parse_branch_merge_options(branch_mergeoptions); - argc = parse_options(argc, argv, prefix, builtin_merge_options, - builtin_merge_usage, 0); - if (shortlog_len < 0) - shortlog_len = (merge_log_config > 0) ? merge_log_config : 0; - - if (verbosity < 0 && show_progress == -1) - show_progress = 0; - - if (abort_current_merge) { - int nargc = 2; - const char *nargv[] = {"reset", "--merge", NULL}; - struct strbuf stash_oid = STRBUF_INIT; - - if (orig_argc != 2) - usage_msg_opt(_("--abort expects no arguments"), - builtin_merge_usage, builtin_merge_options); - - if (!file_exists(git_path_merge_head(the_repository))) - die(_("There is no merge to abort (MERGE_HEAD missing).")); - - if (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository), - READ_ONELINER_SKIP_IF_EMPTY)) - unlink(git_path_merge_autostash(the_repository)); - - /* Invoke 'git reset --merge' */ - ret = cmd_reset(nargc, nargv, prefix); - - if (stash_oid.len) - apply_autostash_oid(stash_oid.buf); - - strbuf_release(&stash_oid); - goto done; - } - - if (quit_current_merge) { - if (orig_argc != 2) - usage_msg_opt(_("--quit expects no arguments"), - builtin_merge_usage, - builtin_merge_options); - - remove_merge_branch_state(the_repository); - goto done; - } - - if (continue_current_merge) { - int nargc = 1; - const char *nargv[] = {"commit", NULL}; - - if (orig_argc != 2) - usage_msg_opt(_("--continue expects no arguments"), - builtin_merge_usage, builtin_merge_options); - - if (!file_exists(git_path_merge_head(the_repository))) - die(_("There is no merge in progress (MERGE_HEAD missing).")); - - /* Invoke 'git commit' */ - ret = cmd_commit(nargc, nargv, prefix); - goto done; - } - - if (read_cache_unmerged()) - die_resolve_conflict("merge"); - - if (file_exists(git_path_merge_head(the_repository))) { - /* - * There is no unmerged entry, don't advise 'git - * add/rm <file>', just 'git commit'. - */ - if (advice_resolve_conflict) - die(_("You have not concluded your merge (MERGE_HEAD exists).\n" - "Please, commit your changes before you merge.")); - else - die(_("You have not concluded your merge (MERGE_HEAD exists).")); - } - if (ref_exists("CHERRY_PICK_HEAD")) { - if (advice_resolve_conflict) - die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n" - "Please, commit your changes before you merge.")); - else - die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).")); - } - resolve_undo_clear(); - - if (option_edit < 0) - option_edit = default_edit_option(); - - cleanup_mode = get_cleanup_mode(cleanup_arg, 0 < option_edit); - - if (verbosity < 0) - show_diffstat = 0; - - if (squash) { - if (fast_forward == FF_NO) - die(_("You cannot combine --squash with --no-ff.")); - if (option_commit > 0) - die(_("You cannot combine --squash with --commit.")); - /* - * squash can now silently disable option_commit - this is not - * a problem as it is only overriding the default, not a user - * supplied option. - */ - option_commit = 0; - } - - if (option_commit < 0) - option_commit = 1; - - if (!argc) { - if (default_to_upstream) - argc = setup_with_upstream(&argv); - else - die(_("No commit specified and merge.defaultToUpstream not set.")); - } else if (argc == 1 && !strcmp(argv[0], "-")) { - argv[0] = "@{-1}"; - } - - if (!argc) - usage_with_options(builtin_merge_usage, - builtin_merge_options); - - if (!head_commit) { - /* - * If the merged head is a valid one there is no reason - * to forbid "git merge" into a branch yet to be born. - * We do the same for "git pull". - */ - struct object_id *remote_head_oid; - if (squash) - die(_("Squash commit into empty head not supported yet")); - if (fast_forward == FF_NO) - die(_("Non-fast-forward commit does not make sense into " - "an empty head")); - remoteheads = collect_parents(head_commit, &head_subsumed, - argc, argv, NULL); - if (!remoteheads) - die(_("%s - not something we can merge"), argv[0]); - if (remoteheads->next) - die(_("Can merge only exactly one commit into empty head")); - - if (verify_signatures) - verify_merge_signature(remoteheads->item, verbosity, - check_trust_level); - - remote_head_oid = &remoteheads->item->object.oid; - read_empty(remote_head_oid, 0); - update_ref("initial pull", "HEAD", remote_head_oid, NULL, 0, - UPDATE_REFS_DIE_ON_ERR); - goto done; - } - - /* - * All the rest are the commits being merged; prepare - * the standard merge summary message to be appended - * to the given message. - */ - remoteheads = collect_parents(head_commit, &head_subsumed, - argc, argv, &merge_msg); - - if (!head_commit || !argc) - usage_with_options(builtin_merge_usage, - builtin_merge_options); - - if (verify_signatures) { - for (p = remoteheads; p; p = p->next) { - verify_merge_signature(p->item, verbosity, - check_trust_level); - } - } - - strbuf_addstr(&buf, "merge"); - for (p = remoteheads; p; p = p->next) - strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name); - setenv("GIT_REFLOG_ACTION", buf.buf, 0); - strbuf_reset(&buf); - - for (p = remoteheads; p; p = p->next) { - struct commit *commit = p->item; - strbuf_addf(&buf, "GITHEAD_%s", - oid_to_hex(&commit->object.oid)); - setenv(buf.buf, merge_remote_util(commit)->name, 1); - strbuf_reset(&buf); - if (fast_forward != FF_ONLY && merging_a_throwaway_tag(commit)) - fast_forward = FF_NO; - } - - if (!use_strategies) { - if (!remoteheads) - ; /* already up-to-date */ - else if (!remoteheads->next) - add_strategies(pull_twohead, DEFAULT_TWOHEAD); - else - add_strategies(pull_octopus, DEFAULT_OCTOPUS); - } - - for (i = 0; i < use_strategies_nr; i++) { - if (use_strategies[i]->attr & NO_FAST_FORWARD) - fast_forward = FF_NO; - if (use_strategies[i]->attr & NO_TRIVIAL) - allow_trivial = 0; - } - - if (!remoteheads) - ; /* already up-to-date */ - else if (!remoteheads->next) - common = get_merge_bases(head_commit, remoteheads->item); - else { - struct commit_list *list = remoteheads; - commit_list_insert(head_commit, &list); - common = get_octopus_merge_bases(list); - free(list); - } - - update_ref("updating ORIG_HEAD", "ORIG_HEAD", - &head_commit->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); - - if (remoteheads && !common) { - /* No common ancestors found. */ - if (!allow_unrelated_histories) - die(_("refusing to merge unrelated histories")); - /* otherwise, we need a real merge. */ - } else if (!remoteheads || - (!remoteheads->next && !common->next && - common->item == remoteheads->item)) { - /* - * If head can reach all the merge then we are up to date. - * but first the most common case of merging one remote. - */ - finish_up_to_date(_("Already up to date.")); - goto done; - } else if (fast_forward != FF_NO && !remoteheads->next && - !common->next && - oideq(&common->item->object.oid, &head_commit->object.oid)) { - /* Again the most common case of merging one remote. */ - struct strbuf msg = STRBUF_INIT; - struct commit *commit; - - if (verbosity >= 0) { - printf(_("Updating %s..%s\n"), - find_unique_abbrev(&head_commit->object.oid, - DEFAULT_ABBREV), - find_unique_abbrev(&remoteheads->item->object.oid, - DEFAULT_ABBREV)); - } - strbuf_addstr(&msg, "Fast-forward"); - if (have_message) - strbuf_addstr(&msg, - " (no commit created; -m option ignored)"); - commit = remoteheads->item; - if (!commit) { - ret = 1; - goto done; - } - - if (autostash) - create_autostash(the_repository, - git_path_merge_autostash(the_repository), - "merge"); - if (checkout_fast_forward(the_repository, - &head_commit->object.oid, - &commit->object.oid, - overwrite_ignore)) { - ret = 1; - goto done; - } - - finish(head_commit, remoteheads, &commit->object.oid, msg.buf); - remove_merge_branch_state(the_repository); - goto done; - } else if (!remoteheads->next && common->next) - ; - /* - * We are not doing octopus and not fast-forward. Need - * a real merge. - */ - else if (!remoteheads->next && !common->next && option_commit) { - /* - * We are not doing octopus, not fast-forward, and have - * only one common. - */ - refresh_cache(REFRESH_QUIET); - if (allow_trivial && fast_forward != FF_ONLY) { - /* See if it is really trivial. */ - git_committer_info(IDENT_STRICT); - printf(_("Trying really trivial in-index merge...\n")); - if (!read_tree_trivial(&common->item->object.oid, - &head_commit->object.oid, - &remoteheads->item->object.oid)) { - ret = merge_trivial(head_commit, remoteheads); - goto done; - } - printf(_("Nope.\n")); - } - } else { - /* - * An octopus. If we can reach all the remote we are up - * to date. - */ - int up_to_date = 1; - struct commit_list *j; - - for (j = remoteheads; j; j = j->next) { - struct commit_list *common_one; - - /* - * Here we *have* to calculate the individual - * merge_bases again, otherwise "git merge HEAD^ - * HEAD^^" would be missed. - */ - common_one = get_merge_bases(head_commit, j->item); - if (!oideq(&common_one->item->object.oid, &j->item->object.oid)) { - up_to_date = 0; - break; - } - } - if (up_to_date) { - finish_up_to_date(_("Already up to date. Yeeah!")); - goto done; - } - } - - if (fast_forward == FF_ONLY) - die(_("Not possible to fast-forward, aborting.")); - - if (autostash) - create_autostash(the_repository, - git_path_merge_autostash(the_repository), - "merge"); - - /* We are going to make a new commit. */ - git_committer_info(IDENT_STRICT); - - /* - * At this point, we need a real merge. No matter what strategy - * we use, it would operate on the index, possibly affecting the - * working tree, and when resolved cleanly, have the desired - * tree in the index -- this means that the index must be in - * sync with the head commit. The strategies are responsible - * to ensure this. - */ - if (use_strategies_nr == 1 || - /* - * Stash away the local changes so that we can try more than one. - */ - save_state(&stash)) - oidclr(&stash); - - for (i = 0; !merge_was_ok && i < use_strategies_nr; i++) { - int ret, cnt; - if (i) { - printf(_("Rewinding the tree to pristine...\n")); - restore_state(&head_commit->object.oid, &stash); - } - if (use_strategies_nr != 1) - printf(_("Trying merge strategy %s...\n"), - use_strategies[i]->name); - /* - * Remember which strategy left the state in the working - * tree. - */ - wt_strategy = use_strategies[i]->name; - - ret = try_merge_strategy(use_strategies[i]->name, - common, remoteheads, - head_commit); - /* - * The backend exits with 1 when conflicts are - * left to be resolved, with 2 when it does not - * handle the given merge at all. - */ - if (ret < 2) { - if (!ret) { - if (option_commit) { - /* Automerge succeeded. */ - automerge_was_ok = 1; - break; - } - merge_was_ok = 1; - } - cnt = (use_strategies_nr > 1) ? evaluate_result() : 0; - if (best_cnt <= 0 || cnt <= best_cnt) { - best_strategy = use_strategies[i]->name; - best_cnt = cnt; - } - } - } - - /* - * If we have a resulting tree, that means the strategy module - * auto resolved the merge cleanly. - */ - if (automerge_was_ok) { - ret = finish_automerge(head_commit, head_subsumed, - common, remoteheads, - &result_tree, wt_strategy); - goto done; - } - - /* - * Pick the result from the best strategy and have the user fix - * it up. - */ - if (!best_strategy) { - restore_state(&head_commit->object.oid, &stash); - if (use_strategies_nr > 1) - fprintf(stderr, - _("No merge strategy handled the merge.\n")); - else - fprintf(stderr, _("Merge with strategy %s failed.\n"), - use_strategies[0]->name); - ret = 2; - goto done; - } else if (best_strategy == wt_strategy) - ; /* We already have its result in the working tree. */ - else { - printf(_("Rewinding the tree to pristine...\n")); - restore_state(&head_commit->object.oid, &stash); - printf(_("Using the %s to prepare resolving by hand.\n"), - best_strategy); - try_merge_strategy(best_strategy, common, remoteheads, - head_commit); - } - - if (squash) { - finish(head_commit, remoteheads, NULL, NULL); - - git_test_write_commit_graph_or_die(); - } else - write_merge_state(remoteheads); - - if (merge_was_ok) - fprintf(stderr, _("Automatic merge went well; " - "stopped before committing as requested\n")); - else - ret = suggest_conflicts(); - -done: - free(branch_to_free); - return ret; -} diff --git a/third_party/git/builtin/mktag.c b/third_party/git/builtin/mktag.c deleted file mode 100644 index 4982d3a93efb..000000000000 --- a/third_party/git/builtin/mktag.c +++ /dev/null @@ -1,179 +0,0 @@ -#include "builtin.h" -#include "tag.h" -#include "replace-object.h" -#include "object-store.h" - -/* - * A signature file has a very simple fixed format: four lines - * of "object <sha1>" + "type <typename>" + "tag <tagname>" + - * "tagger <committer>", followed by a blank line, a free-form tag - * message and a signature block that git itself doesn't care about, - * but that can be verified with gpg or similar. - * - * The first four lines are guaranteed to be at least 83 bytes: - * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the - * shortest possible type-line, "tag .\n" at 6 bytes is the shortest - * single-character-tag line, and "tagger . <> 0 +0000\n" at 20 bytes is - * the shortest possible tagger-line. - */ - -/* - * We refuse to tag something we can't verify. Just because. - */ -static int verify_object(const struct object_id *oid, const char *expected_type) -{ - int ret = -1; - enum object_type type; - unsigned long size; - void *buffer = read_object_file(oid, &type, &size); - const struct object_id *repl = lookup_replace_object(the_repository, oid); - - if (buffer) { - if (type == type_from_string(expected_type)) { - ret = check_object_signature(the_repository, repl, - buffer, size, - expected_type); - } - free(buffer); - } - return ret; -} - -static int verify_tag(char *buffer, unsigned long size) -{ - int typelen; - char type[20]; - struct object_id oid; - const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p; - size_t len; - - if (size < 84) - return error("wanna fool me ? you obviously got the size wrong !"); - - buffer[size] = 0; - - /* Verify object line */ - object = buffer; - if (memcmp(object, "object ", 7)) - return error("char%d: does not start with \"object \"", 0); - - if (parse_oid_hex(object + 7, &oid, &p)) - return error("char%d: could not get SHA1 hash", 7); - - /* Verify type line */ - type_line = p + 1; - if (memcmp(type_line - 1, "\ntype ", 6)) - return error("char%d: could not find \"\\ntype \"", 47); - - /* Verify tag-line */ - tag_line = strchr(type_line, '\n'); - if (!tag_line) - return error("char%"PRIuMAX": could not find next \"\\n\"", - (uintmax_t) (type_line - buffer)); - tag_line++; - if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n') - return error("char%"PRIuMAX": no \"tag \" found", - (uintmax_t) (tag_line - buffer)); - - /* Get the actual type */ - typelen = tag_line - type_line - strlen("type \n"); - if (typelen >= sizeof(type)) - return error("char%"PRIuMAX": type too long", - (uintmax_t) (type_line+5 - buffer)); - - memcpy(type, type_line+5, typelen); - type[typelen] = 0; - - /* Verify that the object matches */ - if (verify_object(&oid, type)) - return error("char%d: could not verify object %s", 7, oid_to_hex(&oid)); - - /* Verify the tag-name: we don't allow control characters or spaces in it */ - tag_line += 4; - for (;;) { - unsigned char c = *tag_line++; - if (c == '\n') - break; - if (c > ' ') - continue; - return error("char%"PRIuMAX": could not verify tag name", - (uintmax_t) (tag_line - buffer)); - } - - /* Verify the tagger line */ - tagger_line = tag_line; - - if (memcmp(tagger_line, "tagger ", 7)) - return error("char%"PRIuMAX": could not find \"tagger \"", - (uintmax_t) (tagger_line - buffer)); - - /* - * Check for correct form for name and email - * i.e. " <" followed by "> " on _this_ line - * No angle brackets within the name or email address fields. - * No spaces within the email address field. - */ - tagger_line += 7; - if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> ")) || - strpbrk(tagger_line, "<>\n") != lb+1 || - strpbrk(lb+2, "><\n ") != rb) - return error("char%"PRIuMAX": malformed tagger field", - (uintmax_t) (tagger_line - buffer)); - - /* Check for author name, at least one character, space is acceptable */ - if (lb == tagger_line) - return error("char%"PRIuMAX": missing tagger name", - (uintmax_t) (tagger_line - buffer)); - - /* timestamp, 1 or more digits followed by space */ - tagger_line = rb + 2; - if (!(len = strspn(tagger_line, "0123456789"))) - return error("char%"PRIuMAX": missing tag timestamp", - (uintmax_t) (tagger_line - buffer)); - tagger_line += len; - if (*tagger_line != ' ') - return error("char%"PRIuMAX": malformed tag timestamp", - (uintmax_t) (tagger_line - buffer)); - tagger_line++; - - /* timezone, 5 digits [+-]hhmm, max. 1400 */ - if (!((tagger_line[0] == '+' || tagger_line[0] == '-') && - strspn(tagger_line+1, "0123456789") == 4 && - tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400)) - return error("char%"PRIuMAX": malformed tag timezone", - (uintmax_t) (tagger_line - buffer)); - tagger_line += 6; - - /* Verify the blank line separating the header from the body */ - if (*tagger_line != '\n') - return error("char%"PRIuMAX": trailing garbage in tag header", - (uintmax_t) (tagger_line - buffer)); - - /* The actual stuff afterwards we don't care about.. */ - return 0; -} - -int cmd_mktag(int argc, const char **argv, const char *prefix) -{ - struct strbuf buf = STRBUF_INIT; - struct object_id result; - - if (argc != 1) - usage("git mktag"); - - if (strbuf_read(&buf, 0, 4096) < 0) { - die_errno("could not read from stdin"); - } - - /* Verify it for some basic sanity: it needs to start with - "object <sha1>\ntype\ntagger " */ - if (verify_tag(buf.buf, buf.len) < 0) - die("invalid tag signature file"); - - if (write_object_file(buf.buf, buf.len, tag_type, &result) < 0) - die("unable to write tag file"); - - strbuf_release(&buf); - printf("%s\n", oid_to_hex(&result)); - return 0; -} diff --git a/third_party/git/builtin/mktree.c b/third_party/git/builtin/mktree.c deleted file mode 100644 index 891991b00d67..000000000000 --- a/third_party/git/builtin/mktree.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * GIT - the stupid content tracker - * - * Copyright (c) Junio C Hamano, 2006, 2009 - */ -#include "builtin.h" -#include "quote.h" -#include "tree.h" -#include "parse-options.h" -#include "object-store.h" - -static struct treeent { - unsigned mode; - struct object_id oid; - int len; - char name[FLEX_ARRAY]; -} **entries; -static int alloc, used; - -static void append_to_tree(unsigned mode, struct object_id *oid, char *path) -{ - struct treeent *ent; - size_t len = strlen(path); - if (strchr(path, '/')) - die("path %s contains slash", path); - - FLEX_ALLOC_MEM(ent, name, path, len); - ent->mode = mode; - ent->len = len; - oidcpy(&ent->oid, oid); - - ALLOC_GROW(entries, used + 1, alloc); - entries[used++] = ent; -} - -static int ent_compare(const void *a_, const void *b_) -{ - struct treeent *a = *(struct treeent **)a_; - struct treeent *b = *(struct treeent **)b_; - return base_name_compare(a->name, a->len, a->mode, - b->name, b->len, b->mode); -} - -static void write_tree(struct object_id *oid) -{ - struct strbuf buf; - size_t size; - int i; - - QSORT(entries, used, ent_compare); - for (size = i = 0; i < used; i++) - size += 32 + entries[i]->len; - - strbuf_init(&buf, size); - for (i = 0; i < used; i++) { - struct treeent *ent = entries[i]; - strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0'); - strbuf_add(&buf, ent->oid.hash, the_hash_algo->rawsz); - } - - write_object_file(buf.buf, buf.len, tree_type, oid); - strbuf_release(&buf); -} - -static const char *mktree_usage[] = { - N_("git mktree [-z] [--missing] [--batch]"), - NULL -}; - -static void mktree_line(char *buf, int nul_term_line, int allow_missing) -{ - char *ptr, *ntr; - const char *p; - unsigned mode; - enum object_type mode_type; /* object type derived from mode */ - enum object_type obj_type; /* object type derived from sha */ - char *path, *to_free = NULL; - struct object_id oid; - - ptr = buf; - /* - * Read non-recursive ls-tree output format: - * mode SP type SP sha1 TAB name - */ - mode = strtoul(ptr, &ntr, 8); - if (ptr == ntr || !ntr || *ntr != ' ') - die("input format error: %s", buf); - ptr = ntr + 1; /* type */ - ntr = strchr(ptr, ' '); - if (!ntr || parse_oid_hex(ntr + 1, &oid, &p) || - *p != '\t') - die("input format error: %s", buf); - - /* It is perfectly normal if we do not have a commit from a submodule */ - if (S_ISGITLINK(mode)) - allow_missing = 1; - - - *ntr++ = 0; /* now at the beginning of SHA1 */ - - path = (char *)p + 1; /* at the beginning of name */ - if (!nul_term_line && path[0] == '"') { - struct strbuf p_uq = STRBUF_INIT; - if (unquote_c_style(&p_uq, path, NULL)) - die("invalid quoting"); - path = to_free = strbuf_detach(&p_uq, NULL); - } - - /* - * Object type is redundantly derivable three ways. - * These should all agree. - */ - mode_type = object_type(mode); - if (mode_type != type_from_string(ptr)) { - die("entry '%s' object type (%s) doesn't match mode type (%s)", - path, ptr, type_name(mode_type)); - } - - /* Check the type of object identified by sha1 */ - obj_type = oid_object_info(the_repository, &oid, NULL); - if (obj_type < 0) { - if (allow_missing) { - ; /* no problem - missing objects are presumed to be of the right type */ - } else { - die("entry '%s' object %s is unavailable", path, oid_to_hex(&oid)); - } - } else { - if (obj_type != mode_type) { - /* - * The object exists but is of the wrong type. - * This is a problem regardless of allow_missing - * because the new tree entry will never be correct. - */ - die("entry '%s' object %s is a %s but specified type was (%s)", - path, oid_to_hex(&oid), type_name(obj_type), type_name(mode_type)); - } - } - - append_to_tree(mode, &oid, path); - free(to_free); -} - -int cmd_mktree(int ac, const char **av, const char *prefix) -{ - struct strbuf sb = STRBUF_INIT; - struct object_id oid; - int nul_term_line = 0; - int allow_missing = 0; - int is_batch_mode = 0; - int got_eof = 0; - strbuf_getline_fn getline_fn; - - const struct option option[] = { - OPT_BOOL('z', NULL, &nul_term_line, N_("input is NUL terminated")), - OPT_SET_INT( 0 , "missing", &allow_missing, N_("allow missing objects"), 1), - OPT_SET_INT( 0 , "batch", &is_batch_mode, N_("allow creation of more than one tree"), 1), - OPT_END() - }; - - ac = parse_options(ac, av, prefix, option, mktree_usage, 0); - getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; - - while (!got_eof) { - while (1) { - if (getline_fn(&sb, stdin) == EOF) { - got_eof = 1; - break; - } - if (sb.buf[0] == '\0') { - /* empty lines denote tree boundaries in batch mode */ - if (is_batch_mode) - break; - die("input format error: (blank line only valid in batch mode)"); - } - mktree_line(sb.buf, nul_term_line, allow_missing); - } - if (is_batch_mode && got_eof && used < 1) { - /* - * Execution gets here if the last tree entry is terminated with a - * new-line. The final new-line has been made optional to be - * consistent with the original non-batch behaviour of mktree. - */ - ; /* skip creating an empty tree */ - } else { - write_tree(&oid); - puts(oid_to_hex(&oid)); - fflush(stdout); - } - used=0; /* reset tree entry buffer for re-use in batch mode */ - } - strbuf_release(&sb); - exit(0); -} diff --git a/third_party/git/builtin/multi-pack-index.c b/third_party/git/builtin/multi-pack-index.c deleted file mode 100644 index 5bf88cd2a8e2..000000000000 --- a/third_party/git/builtin/multi-pack-index.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "parse-options.h" -#include "midx.h" -#include "trace2.h" - -static char const * const builtin_multi_pack_index_usage[] = { - N_("git multi-pack-index [<options>] (write|verify|expire|repack --batch-size=<size>)"), - NULL -}; - -static struct opts_multi_pack_index { - const char *object_dir; - unsigned long batch_size; - int progress; -} opts; - -int cmd_multi_pack_index(int argc, const char **argv, - const char *prefix) -{ - unsigned flags = 0; - - static struct option builtin_multi_pack_index_options[] = { - OPT_FILENAME(0, "object-dir", &opts.object_dir, - N_("object directory containing set of packfile and pack-index pairs")), - OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")), - OPT_MAGNITUDE(0, "batch-size", &opts.batch_size, - N_("during repack, collect pack-files of smaller size into a batch that is larger than this size")), - OPT_END(), - }; - - git_config(git_default_config, NULL); - - opts.progress = isatty(2); - argc = parse_options(argc, argv, prefix, - builtin_multi_pack_index_options, - builtin_multi_pack_index_usage, 0); - - if (!opts.object_dir) - opts.object_dir = get_object_directory(); - if (opts.progress) - flags |= MIDX_PROGRESS; - - if (argc == 0) - usage_with_options(builtin_multi_pack_index_usage, - builtin_multi_pack_index_options); - - if (argc > 1) { - die(_("too many arguments")); - return 1; - } - - trace2_cmd_mode(argv[0]); - - if (!strcmp(argv[0], "repack")) - return midx_repack(the_repository, opts.object_dir, - (size_t)opts.batch_size, flags); - if (opts.batch_size) - die(_("--batch-size option is only for 'repack' subcommand")); - - if (!strcmp(argv[0], "write")) - return write_midx_file(opts.object_dir, flags); - if (!strcmp(argv[0], "verify")) - return verify_midx_file(the_repository, opts.object_dir, flags); - if (!strcmp(argv[0], "expire")) - return expire_midx_packs(the_repository, opts.object_dir, flags); - - die(_("unrecognized subcommand: %s"), argv[0]); -} diff --git a/third_party/git/builtin/mv.c b/third_party/git/builtin/mv.c deleted file mode 100644 index 7dac714af908..000000000000 --- a/third_party/git/builtin/mv.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * "git mv" builtin command - * - * Copyright (C) 2006 Johannes Schindelin - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "config.h" -#include "pathspec.h" -#include "lockfile.h" -#include "dir.h" -#include "cache-tree.h" -#include "string-list.h" -#include "parse-options.h" -#include "submodule.h" - -static const char * const builtin_mv_usage[] = { - N_("git mv [<options>] <source>... <destination>"), - NULL -}; - -#define DUP_BASENAME 1 -#define KEEP_TRAILING_SLASH 2 - -static const char **internal_prefix_pathspec(const char *prefix, - const char **pathspec, - int count, unsigned flags) -{ - int i; - const char **result; - int prefixlen = prefix ? strlen(prefix) : 0; - ALLOC_ARRAY(result, count + 1); - - /* Create an intermediate copy of the pathspec based on the flags */ - for (i = 0; i < count; i++) { - int length = strlen(pathspec[i]); - int to_copy = length; - char *it; - while (!(flags & KEEP_TRAILING_SLASH) && - to_copy > 0 && is_dir_sep(pathspec[i][to_copy - 1])) - to_copy--; - - it = xmemdupz(pathspec[i], to_copy); - if (flags & DUP_BASENAME) { - result[i] = xstrdup(basename(it)); - free(it); - } else { - result[i] = it; - } - } - result[count] = NULL; - - /* Prefix the pathspec and free the old intermediate strings */ - for (i = 0; i < count; i++) { - const char *match = prefix_path(prefix, prefixlen, result[i]); - free((char *) result[i]); - result[i] = match; - } - - return result; -} - -static const char *add_slash(const char *path) -{ - size_t len = strlen(path); - if (path[len - 1] != '/') { - char *with_slash = xmalloc(st_add(len, 2)); - memcpy(with_slash, path, len); - with_slash[len++] = '/'; - with_slash[len] = 0; - return with_slash; - } - return path; -} - -#define SUBMODULE_WITH_GITDIR ((const char *)1) - -static void prepare_move_submodule(const char *src, int first, - const char **submodule_gitfile) -{ - struct strbuf submodule_dotgit = STRBUF_INIT; - if (!S_ISGITLINK(active_cache[first]->ce_mode)) - die(_("Directory %s is in index and no submodule?"), src); - if (!is_staging_gitmodules_ok(&the_index)) - die(_("Please stage your changes to .gitmodules or stash them to proceed")); - strbuf_addf(&submodule_dotgit, "%s/.git", src); - *submodule_gitfile = read_gitfile(submodule_dotgit.buf); - if (*submodule_gitfile) - *submodule_gitfile = xstrdup(*submodule_gitfile); - else - *submodule_gitfile = SUBMODULE_WITH_GITDIR; - strbuf_release(&submodule_dotgit); -} - -static int index_range_of_same_dir(const char *src, int length, - int *first_p, int *last_p) -{ - const char *src_w_slash = add_slash(src); - int first, last, len_w_slash = length + 1; - - first = cache_name_pos(src_w_slash, len_w_slash); - if (first >= 0) - die(_("%.*s is in index"), len_w_slash, src_w_slash); - - first = -1 - first; - for (last = first; last < active_nr; last++) { - const char *path = active_cache[last]->name; - if (strncmp(path, src_w_slash, len_w_slash)) - break; - } - if (src_w_slash != src) - free((char *)src_w_slash); - *first_p = first; - *last_p = last; - return last - first; -} - -int cmd_mv(int argc, const char **argv, const char *prefix) -{ - int i, flags, gitmodules_modified = 0; - int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; - struct option builtin_mv_options[] = { - OPT__VERBOSE(&verbose, N_("be verbose")), - OPT__DRY_RUN(&show_only, N_("dry run")), - OPT__FORCE(&force, N_("force move/rename even if target exists"), - PARSE_OPT_NOCOMPLETE), - OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")), - OPT_END(), - }; - const char **source, **destination, **dest_path, **submodule_gitfile; - enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; - struct stat st; - struct string_list src_for_dst = STRING_LIST_INIT_NODUP; - struct lock_file lock_file = LOCK_INIT; - struct cache_entry *ce; - - git_config(git_default_config, NULL); - - argc = parse_options(argc, argv, prefix, builtin_mv_options, - builtin_mv_usage, 0); - if (--argc < 1) - usage_with_options(builtin_mv_usage, builtin_mv_options); - - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); - if (read_cache() < 0) - die(_("index file corrupt")); - - source = internal_prefix_pathspec(prefix, argv, argc, 0); - modes = xcalloc(argc, sizeof(enum update_mode)); - /* - * Keep trailing slash, needed to let - * "git mv file no-such-dir/" error out, except in the case - * "git mv directory no-such-dir/". - */ - flags = KEEP_TRAILING_SLASH; - if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1])) - flags = 0; - dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags); - submodule_gitfile = xcalloc(argc, sizeof(char *)); - - if (dest_path[0][0] == '\0') - /* special case: "." was normalized to "" */ - destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); - else if (!lstat(dest_path[0], &st) && - S_ISDIR(st.st_mode)) { - dest_path[0] = add_slash(dest_path[0]); - destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); - } else { - if (argc != 1) - die(_("destination '%s' is not a directory"), dest_path[0]); - destination = dest_path; - } - - /* Checking */ - for (i = 0; i < argc; i++) { - const char *src = source[i], *dst = destination[i]; - int length, src_is_dir; - const char *bad = NULL; - - if (show_only) - printf(_("Checking rename of '%s' to '%s'\n"), src, dst); - - length = strlen(src); - if (lstat(src, &st) < 0) - bad = _("bad source"); - else if (!strncmp(src, dst, length) && - (dst[length] == 0 || dst[length] == '/')) { - bad = _("can not move directory into itself"); - } else if ((src_is_dir = S_ISDIR(st.st_mode)) - && lstat(dst, &st) == 0) - bad = _("cannot move directory over file"); - else if (src_is_dir) { - int first = cache_name_pos(src, length), last; - - if (first >= 0) - prepare_move_submodule(src, first, - submodule_gitfile + i); - else if (index_range_of_same_dir(src, length, - &first, &last) < 1) - bad = _("source directory is empty"); - else { /* last - first >= 1 */ - int j, dst_len, n; - - modes[i] = WORKING_DIRECTORY; - n = argc + last - first; - REALLOC_ARRAY(source, n); - REALLOC_ARRAY(destination, n); - REALLOC_ARRAY(modes, n); - REALLOC_ARRAY(submodule_gitfile, n); - - dst = add_slash(dst); - dst_len = strlen(dst); - - for (j = 0; j < last - first; j++) { - const char *path = active_cache[first + j]->name; - source[argc + j] = path; - destination[argc + j] = - prefix_path(dst, dst_len, path + length + 1); - modes[argc + j] = INDEX; - submodule_gitfile[argc + j] = NULL; - } - argc += last - first; - } - } else if (!(ce = cache_file_exists(src, length, ignore_case))) { - bad = _("not under version control"); - } else if (ce_stage(ce)) { - bad = _("conflicted"); - } else if (lstat(dst, &st) == 0 && - (!ignore_case || strcasecmp(src, dst))) { - bad = _("destination exists"); - if (force) { - /* - * only files can overwrite each other: - * check both source and destination - */ - if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { - if (verbose) - warning(_("overwriting '%s'"), dst); - bad = NULL; - } else - bad = _("Cannot overwrite"); - } - } else if (string_list_has_string(&src_for_dst, dst)) - bad = _("multiple sources for the same target"); - else if (is_dir_sep(dst[strlen(dst) - 1])) - bad = _("destination directory does not exist"); - else - string_list_insert(&src_for_dst, dst); - - if (!bad) - continue; - if (!ignore_errors) - die(_("%s, source=%s, destination=%s"), - bad, src, dst); - if (--argc > 0) { - int n = argc - i; - memmove(source + i, source + i + 1, - n * sizeof(char *)); - memmove(destination + i, destination + i + 1, - n * sizeof(char *)); - memmove(modes + i, modes + i + 1, - n * sizeof(enum update_mode)); - memmove(submodule_gitfile + i, submodule_gitfile + i + 1, - n * sizeof(char *)); - i--; - } - } - - for (i = 0; i < argc; i++) { - const char *src = source[i], *dst = destination[i]; - enum update_mode mode = modes[i]; - int pos; - if (show_only || verbose) - printf(_("Renaming %s to %s\n"), src, dst); - if (show_only) - continue; - if (mode != INDEX && rename(src, dst) < 0) { - if (ignore_errors) - continue; - die_errno(_("renaming '%s' failed"), src); - } - if (submodule_gitfile[i]) { - if (!update_path_in_gitmodules(src, dst)) - gitmodules_modified = 1; - if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) - connect_work_tree_and_git_dir(dst, - submodule_gitfile[i], - 1); - } - - if (mode == WORKING_DIRECTORY) - continue; - - pos = cache_name_pos(src, strlen(src)); - assert(pos >= 0); - rename_cache_entry_at(pos, dst); - } - - if (gitmodules_modified) - stage_updated_gitmodules(&the_index); - - if (write_locked_index(&the_index, &lock_file, - COMMIT_LOCK | SKIP_IF_UNCHANGED)) - die(_("Unable to write new index file")); - - return 0; -} diff --git a/third_party/git/builtin/name-rev.c b/third_party/git/builtin/name-rev.c deleted file mode 100644 index 725dd0451913..000000000000 --- a/third_party/git/builtin/name-rev.c +++ /dev/null @@ -1,636 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "repository.h" -#include "config.h" -#include "commit.h" -#include "tag.h" -#include "refs.h" -#include "parse-options.h" -#include "prio-queue.h" -#include "sha1-lookup.h" -#include "commit-slab.h" - -/* - * One day. See the 'name a rev shortly after epoch' test in t6120 when - * changing this value - */ -#define CUTOFF_DATE_SLOP 86400 - -struct rev_name { - char *tip_name; - timestamp_t taggerdate; - int generation; - int distance; - int from_tag; -}; - -define_commit_slab(commit_rev_name, struct rev_name); - -static timestamp_t cutoff = TIME_MAX; -static struct commit_rev_name rev_names; - -/* How many generations are maximally preferred over _one_ merge traversal? */ -#define MERGE_TRAVERSAL_WEIGHT 65535 - -static int is_valid_rev_name(const struct rev_name *name) -{ - return name && (name->generation || name->tip_name); -} - -static struct rev_name *get_commit_rev_name(const struct commit *commit) -{ - struct rev_name *name = commit_rev_name_peek(&rev_names, commit); - - return is_valid_rev_name(name) ? name : NULL; -} - -static int is_better_name(struct rev_name *name, - timestamp_t taggerdate, - int distance, - int from_tag) -{ - /* - * When comparing names based on tags, prefer names - * based on the older tag, even if it is farther away. - */ - if (from_tag && name->from_tag) - return (name->taggerdate > taggerdate || - (name->taggerdate == taggerdate && - name->distance > distance)); - - /* - * We know that at least one of them is a non-tag at this point. - * favor a tag over a non-tag. - */ - if (name->from_tag != from_tag) - return from_tag; - - /* - * We are now looking at two non-tags. Tiebreak to favor - * shorter hops. - */ - if (name->distance != distance) - return name->distance > distance; - - /* ... or tiebreak to favor older date */ - if (name->taggerdate != taggerdate) - return name->taggerdate > taggerdate; - - /* keep the current one if we cannot decide */ - return 0; -} - -static struct rev_name *create_or_update_name(struct commit *commit, - timestamp_t taggerdate, - int generation, int distance, - int from_tag) -{ - struct rev_name *name = commit_rev_name_at(&rev_names, commit); - - if (is_valid_rev_name(name)) { - if (!is_better_name(name, taggerdate, distance, from_tag)) - return NULL; - - /* - * This string might still be shared with ancestors - * (generation > 0). We can release it here regardless, - * because the new name that has just won will be better - * for them as well, so name_rev() will replace these - * stale pointers when it processes the parents. - */ - if (!name->generation) - free(name->tip_name); - } - - name->taggerdate = taggerdate; - name->generation = generation; - name->distance = distance; - name->from_tag = from_tag; - - return name; -} - -static char *get_parent_name(const struct rev_name *name, int parent_number) -{ - struct strbuf sb = STRBUF_INIT; - size_t len; - - strip_suffix(name->tip_name, "^0", &len); - if (name->generation > 0) { - strbuf_grow(&sb, len + - 1 + decimal_width(name->generation) + - 1 + decimal_width(parent_number)); - strbuf_addf(&sb, "%.*s~%d^%d", (int)len, name->tip_name, - name->generation, parent_number); - } else { - strbuf_grow(&sb, len + - 1 + decimal_width(parent_number)); - strbuf_addf(&sb, "%.*s^%d", (int)len, name->tip_name, - parent_number); - } - return strbuf_detach(&sb, NULL); -} - -static void name_rev(struct commit *start_commit, - const char *tip_name, timestamp_t taggerdate, - int from_tag, int deref) -{ - struct prio_queue queue; - struct commit *commit; - struct commit **parents_to_queue = NULL; - size_t parents_to_queue_nr, parents_to_queue_alloc = 0; - struct rev_name *start_name; - - parse_commit(start_commit); - if (start_commit->date < cutoff) - return; - - start_name = create_or_update_name(start_commit, taggerdate, 0, 0, - from_tag); - if (!start_name) - return; - if (deref) - start_name->tip_name = xstrfmt("%s^0", tip_name); - else - start_name->tip_name = xstrdup(tip_name); - - memset(&queue, 0, sizeof(queue)); /* Use the prio_queue as LIFO */ - prio_queue_put(&queue, start_commit); - - while ((commit = prio_queue_get(&queue))) { - struct rev_name *name = get_commit_rev_name(commit); - struct commit_list *parents; - int parent_number = 1; - - parents_to_queue_nr = 0; - - for (parents = commit->parents; - parents; - parents = parents->next, parent_number++) { - struct commit *parent = parents->item; - struct rev_name *parent_name; - int generation, distance; - - parse_commit(parent); - if (parent->date < cutoff) - continue; - - if (parent_number > 1) { - generation = 0; - distance = name->distance + MERGE_TRAVERSAL_WEIGHT; - } else { - generation = name->generation + 1; - distance = name->distance + 1; - } - - parent_name = create_or_update_name(parent, taggerdate, - generation, - distance, from_tag); - if (parent_name) { - if (parent_number > 1) - parent_name->tip_name = - get_parent_name(name, - parent_number); - else - parent_name->tip_name = name->tip_name; - ALLOC_GROW(parents_to_queue, - parents_to_queue_nr + 1, - parents_to_queue_alloc); - parents_to_queue[parents_to_queue_nr] = parent; - parents_to_queue_nr++; - } - } - - /* The first parent must come out first from the prio_queue */ - while (parents_to_queue_nr) - prio_queue_put(&queue, - parents_to_queue[--parents_to_queue_nr]); - } - - clear_prio_queue(&queue); - free(parents_to_queue); -} - -static int subpath_matches(const char *path, const char *filter) -{ - const char *subpath = path; - - while (subpath) { - if (!wildmatch(filter, subpath, 0)) - return subpath - path; - subpath = strchr(subpath, '/'); - if (subpath) - subpath++; - } - return -1; -} - -static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous) -{ - if (shorten_unambiguous) - refname = shorten_unambiguous_ref(refname, 0); - else if (skip_prefix(refname, "refs/heads/", &refname)) - ; /* refname already advanced */ - else - skip_prefix(refname, "refs/", &refname); - return refname; -} - -struct name_ref_data { - int tags_only; - int name_only; - struct string_list ref_filters; - struct string_list exclude_filters; -}; - -static struct tip_table { - struct tip_table_entry { - struct object_id oid; - const char *refname; - struct commit *commit; - timestamp_t taggerdate; - unsigned int from_tag:1; - unsigned int deref:1; - } *table; - int nr; - int alloc; - int sorted; -} tip_table; - -static void add_to_tip_table(const struct object_id *oid, const char *refname, - int shorten_unambiguous, struct commit *commit, - timestamp_t taggerdate, int from_tag, int deref) -{ - refname = name_ref_abbrev(refname, shorten_unambiguous); - - ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc); - oidcpy(&tip_table.table[tip_table.nr].oid, oid); - tip_table.table[tip_table.nr].refname = xstrdup(refname); - tip_table.table[tip_table.nr].commit = commit; - tip_table.table[tip_table.nr].taggerdate = taggerdate; - tip_table.table[tip_table.nr].from_tag = from_tag; - tip_table.table[tip_table.nr].deref = deref; - tip_table.nr++; - tip_table.sorted = 0; -} - -static int tipcmp(const void *a_, const void *b_) -{ - const struct tip_table_entry *a = a_, *b = b_; - return oidcmp(&a->oid, &b->oid); -} - -static int cmp_by_tag_and_age(const void *a_, const void *b_) -{ - const struct tip_table_entry *a = a_, *b = b_; - int cmp; - - /* Prefer tags. */ - cmp = b->from_tag - a->from_tag; - if (cmp) - return cmp; - - /* Older is better. */ - if (a->taggerdate < b->taggerdate) - return -1; - return a->taggerdate != b->taggerdate; -} - -static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data) -{ - struct object *o = parse_object(the_repository, oid); - struct name_ref_data *data = cb_data; - int can_abbreviate_output = data->tags_only && data->name_only; - int deref = 0; - int from_tag = 0; - struct commit *commit = NULL; - timestamp_t taggerdate = TIME_MAX; - - if (data->tags_only && !starts_with(path, "refs/tags/")) - return 0; - - if (data->exclude_filters.nr) { - struct string_list_item *item; - - for_each_string_list_item(item, &data->exclude_filters) { - if (subpath_matches(path, item->string) >= 0) - return 0; - } - } - - if (data->ref_filters.nr) { - struct string_list_item *item; - int matched = 0; - - /* See if any of the patterns match. */ - for_each_string_list_item(item, &data->ref_filters) { - /* - * Check all patterns even after finding a match, so - * that we can see if a match with a subpath exists. - * When a user asked for 'refs/tags/v*' and 'v1.*', - * both of which match, the user is showing her - * willingness to accept a shortened output by having - * the 'v1.*' in the acceptable refnames, so we - * shouldn't stop when seeing 'refs/tags/v1.4' matches - * 'refs/tags/v*'. We should show it as 'v1.4'. - */ - switch (subpath_matches(path, item->string)) { - case -1: /* did not match */ - break; - case 0: /* matched fully */ - matched = 1; - break; - default: /* matched subpath */ - matched = 1; - can_abbreviate_output = 1; - break; - } - } - - /* If none of the patterns matched, stop now */ - if (!matched) - return 0; - } - - while (o && o->type == OBJ_TAG) { - struct tag *t = (struct tag *) o; - if (!t->tagged) - break; /* broken repository */ - o = parse_object(the_repository, &t->tagged->oid); - deref = 1; - taggerdate = t->date; - } - if (o && o->type == OBJ_COMMIT) { - commit = (struct commit *)o; - from_tag = starts_with(path, "refs/tags/"); - if (taggerdate == TIME_MAX) - taggerdate = commit->date; - } - - add_to_tip_table(oid, path, can_abbreviate_output, commit, taggerdate, - from_tag, deref); - return 0; -} - -static void name_tips(void) -{ - int i; - - /* - * Try to set better names first, so that worse ones spread - * less. - */ - QSORT(tip_table.table, tip_table.nr, cmp_by_tag_and_age); - for (i = 0; i < tip_table.nr; i++) { - struct tip_table_entry *e = &tip_table.table[i]; - if (e->commit) { - name_rev(e->commit, e->refname, e->taggerdate, - e->from_tag, e->deref); - } - } -} - -static const unsigned char *nth_tip_table_ent(size_t ix, void *table_) -{ - struct tip_table_entry *table = table_; - return table[ix].oid.hash; -} - -static const char *get_exact_ref_match(const struct object *o) -{ - int found; - - if (!tip_table.table || !tip_table.nr) - return NULL; - - if (!tip_table.sorted) { - QSORT(tip_table.table, tip_table.nr, tipcmp); - tip_table.sorted = 1; - } - - found = sha1_pos(o->oid.hash, tip_table.table, tip_table.nr, - nth_tip_table_ent); - if (0 <= found) - return tip_table.table[found].refname; - return NULL; -} - -/* may return a constant string or use "buf" as scratch space */ -static const char *get_rev_name(const struct object *o, struct strbuf *buf) -{ - struct rev_name *n; - const struct commit *c; - - if (o->type != OBJ_COMMIT) - return get_exact_ref_match(o); - c = (const struct commit *) o; - n = get_commit_rev_name(c); - if (!n) - return NULL; - - if (!n->generation) - return n->tip_name; - else { - strbuf_reset(buf); - strbuf_addstr(buf, n->tip_name); - strbuf_strip_suffix(buf, "^0"); - strbuf_addf(buf, "~%d", n->generation); - return buf->buf; - } -} - -static void show_name(const struct object *obj, - const char *caller_name, - int always, int allow_undefined, int name_only) -{ - const char *name; - const struct object_id *oid = &obj->oid; - struct strbuf buf = STRBUF_INIT; - - if (!name_only) - printf("%s ", caller_name ? caller_name : oid_to_hex(oid)); - name = get_rev_name(obj, &buf); - if (name) - printf("%s\n", name); - else if (allow_undefined) - printf("undefined\n"); - else if (always) - printf("%s\n", find_unique_abbrev(oid, DEFAULT_ABBREV)); - else - die("cannot describe '%s'", oid_to_hex(oid)); - strbuf_release(&buf); -} - -static char const * const name_rev_usage[] = { - N_("git name-rev [<options>] <commit>..."), - N_("git name-rev [<options>] --all"), - N_("git name-rev [<options>] --stdin"), - NULL -}; - -static void name_rev_line(char *p, struct name_ref_data *data) -{ - struct strbuf buf = STRBUF_INIT; - int counter = 0; - char *p_start; - const unsigned hexsz = the_hash_algo->hexsz; - - for (p_start = p; *p; p++) { -#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f')) - if (!ishex(*p)) - counter = 0; - else if (++counter == hexsz && - !ishex(*(p+1))) { - struct object_id oid; - const char *name = NULL; - char c = *(p+1); - int p_len = p - p_start + 1; - - counter = 0; - - *(p+1) = 0; - if (!get_oid(p - (hexsz - 1), &oid)) { - struct object *o = - lookup_object(the_repository, &oid); - if (o) - name = get_rev_name(o, &buf); - } - *(p+1) = c; - - if (!name) - continue; - - if (data->name_only) - printf("%.*s%s", p_len - hexsz, p_start, name); - else - printf("%.*s (%s)", p_len, p_start, name); - p_start = p + 1; - } - } - - /* flush */ - if (p_start != p) - fwrite(p_start, p - p_start, 1, stdout); - - strbuf_release(&buf); -} - -int cmd_name_rev(int argc, const char **argv, const char *prefix) -{ - struct object_array revs = OBJECT_ARRAY_INIT; - int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0; - struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP }; - struct option opts[] = { - OPT_BOOL(0, "name-only", &data.name_only, N_("print only ref-based names (no object names)")), - OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")), - OPT_STRING_LIST(0, "refs", &data.ref_filters, N_("pattern"), - N_("only use refs matching <pattern>")), - OPT_STRING_LIST(0, "exclude", &data.exclude_filters, N_("pattern"), - N_("ignore refs matching <pattern>")), - OPT_GROUP(""), - OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")), - OPT_BOOL(0, "stdin", &transform_stdin, N_("read from stdin")), - OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")), - OPT_BOOL(0, "always", &always, - N_("show abbreviated commit object as fallback")), - { - /* A Hidden OPT_BOOL */ - OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL, - N_("dereference tags in the input (internal use)"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1, - }, - OPT_END(), - }; - - init_commit_rev_name(&rev_names); - git_config(git_default_config, NULL); - argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0); - if (all + transform_stdin + !!argc > 1) { - error("Specify either a list, or --all, not both!"); - usage_with_options(name_rev_usage, opts); - } - if (all || transform_stdin) - cutoff = 0; - - for (; argc; argc--, argv++) { - struct object_id oid; - struct object *object; - struct commit *commit; - - if (get_oid(*argv, &oid)) { - fprintf(stderr, "Could not get sha1 for %s. Skipping.\n", - *argv); - continue; - } - - commit = NULL; - object = parse_object(the_repository, &oid); - if (object) { - struct object *peeled = deref_tag(the_repository, - object, *argv, 0); - if (peeled && peeled->type == OBJ_COMMIT) - commit = (struct commit *)peeled; - } - - if (!object) { - fprintf(stderr, "Could not get object for %s. Skipping.\n", - *argv); - continue; - } - - if (commit) { - if (cutoff > commit->date) - cutoff = commit->date; - } - - if (peel_tag) { - if (!commit) { - fprintf(stderr, "Could not get commit for %s. Skipping.\n", - *argv); - continue; - } - object = (struct object *)commit; - } - add_object_array(object, *argv, &revs); - } - - if (cutoff) { - /* check for undeflow */ - if (cutoff > TIME_MIN + CUTOFF_DATE_SLOP) - cutoff = cutoff - CUTOFF_DATE_SLOP; - else - cutoff = TIME_MIN; - } - for_each_ref(name_ref, &data); - name_tips(); - - if (transform_stdin) { - char buffer[2048]; - - while (!feof(stdin)) { - char *p = fgets(buffer, sizeof(buffer), stdin); - if (!p) - break; - name_rev_line(p, &data); - } - } else if (all) { - int i, max; - - max = get_max_object_index(); - for (i = 0; i < max; i++) { - struct object *obj = get_indexed_object(i); - if (!obj || obj->type != OBJ_COMMIT) - continue; - show_name(obj, NULL, - always, allow_undefined, data.name_only); - } - } else { - int i; - for (i = 0; i < revs.nr; i++) - show_name(revs.objects[i].item, revs.objects[i].name, - always, allow_undefined, data.name_only); - } - - UNLEAK(revs); - return 0; -} diff --git a/third_party/git/builtin/notes.c b/third_party/git/builtin/notes.c deleted file mode 100644 index 2987c08a2e92..000000000000 --- a/third_party/git/builtin/notes.c +++ /dev/null @@ -1,1039 +0,0 @@ -/* - * Builtin "git notes" - * - * Copyright (c) 2010 Johan Herland <johan@herland.net> - * - * Based on git-notes.sh by Johannes Schindelin, - * and builtin/tag.c by Kristian Hรธgsberg and Carlos Rica. - */ - -#include "cache.h" -#include "config.h" -#include "builtin.h" -#include "notes.h" -#include "object-store.h" -#include "repository.h" -#include "blob.h" -#include "pretty.h" -#include "refs.h" -#include "exec-cmd.h" -#include "run-command.h" -#include "parse-options.h" -#include "string-list.h" -#include "notes-merge.h" -#include "notes-utils.h" -#include "worktree.h" - -static const char * const git_notes_usage[] = { - N_("git notes [--ref <notes-ref>] [list [<object>]]"), - N_("git notes [--ref <notes-ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"), - N_("git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>"), - N_("git notes [--ref <notes-ref>] append [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"), - N_("git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]"), - N_("git notes [--ref <notes-ref>] show [<object>]"), - N_("git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>"), - N_("git notes merge --commit [-v | -q]"), - N_("git notes merge --abort [-v | -q]"), - N_("git notes [--ref <notes-ref>] remove [<object>...]"), - N_("git notes [--ref <notes-ref>] prune [-n] [-v]"), - N_("git notes [--ref <notes-ref>] get-ref"), - NULL -}; - -static const char * const git_notes_list_usage[] = { - N_("git notes [list [<object>]]"), - NULL -}; - -static const char * const git_notes_add_usage[] = { - N_("git notes add [<options>] [<object>]"), - NULL -}; - -static const char * const git_notes_copy_usage[] = { - N_("git notes copy [<options>] <from-object> <to-object>"), - N_("git notes copy --stdin [<from-object> <to-object>]..."), - NULL -}; - -static const char * const git_notes_append_usage[] = { - N_("git notes append [<options>] [<object>]"), - NULL -}; - -static const char * const git_notes_edit_usage[] = { - N_("git notes edit [<object>]"), - NULL -}; - -static const char * const git_notes_show_usage[] = { - N_("git notes show [<object>]"), - NULL -}; - -static const char * const git_notes_merge_usage[] = { - N_("git notes merge [<options>] <notes-ref>"), - N_("git notes merge --commit [<options>]"), - N_("git notes merge --abort [<options>]"), - NULL -}; - -static const char * const git_notes_remove_usage[] = { - N_("git notes remove [<object>]"), - NULL -}; - -static const char * const git_notes_prune_usage[] = { - N_("git notes prune [<options>]"), - NULL -}; - -static const char * const git_notes_get_ref_usage[] = { - N_("git notes get-ref"), - NULL -}; - -static const char note_template[] = - N_("Write/edit the notes for the following object:"); - -struct note_data { - int given; - int use_editor; - char *edit_path; - struct strbuf buf; -}; - -static void free_note_data(struct note_data *d) -{ - if (d->edit_path) { - unlink_or_warn(d->edit_path); - free(d->edit_path); - } - strbuf_release(&d->buf); -} - -static int list_each_note(const struct object_id *object_oid, - const struct object_id *note_oid, char *note_path, - void *cb_data) -{ - printf("%s %s\n", oid_to_hex(note_oid), oid_to_hex(object_oid)); - return 0; -} - -static void copy_obj_to_fd(int fd, const struct object_id *oid) -{ - unsigned long size; - enum object_type type; - char *buf = read_object_file(oid, &type, &size); - if (buf) { - if (size) - write_or_die(fd, buf, size); - free(buf); - } -} - -static void write_commented_object(int fd, const struct object_id *object) -{ - const char *show_args[5] = - {"show", "--stat", "--no-notes", oid_to_hex(object), NULL}; - struct child_process show = CHILD_PROCESS_INIT; - struct strbuf buf = STRBUF_INIT; - struct strbuf cbuf = STRBUF_INIT; - - /* Invoke "git show --stat --no-notes $object" */ - show.argv = show_args; - show.no_stdin = 1; - show.out = -1; - show.err = 0; - show.git_cmd = 1; - if (start_command(&show)) - die(_("unable to start 'show' for object '%s'"), - oid_to_hex(object)); - - if (strbuf_read(&buf, show.out, 0) < 0) - die_errno(_("could not read 'show' output")); - strbuf_add_commented_lines(&cbuf, buf.buf, buf.len); - write_or_die(fd, cbuf.buf, cbuf.len); - - strbuf_release(&cbuf); - strbuf_release(&buf); - - if (finish_command(&show)) - die(_("failed to finish 'show' for object '%s'"), - oid_to_hex(object)); -} - -static void prepare_note_data(const struct object_id *object, struct note_data *d, - const struct object_id *old_note) -{ - if (d->use_editor || !d->given) { - int fd; - struct strbuf buf = STRBUF_INIT; - - /* write the template message before editing: */ - d->edit_path = git_pathdup("NOTES_EDITMSG"); - fd = open(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600); - if (fd < 0) - die_errno(_("could not create file '%s'"), d->edit_path); - - if (d->given) - write_or_die(fd, d->buf.buf, d->buf.len); - else if (old_note) - copy_obj_to_fd(fd, old_note); - - strbuf_addch(&buf, '\n'); - strbuf_add_commented_lines(&buf, "\n", strlen("\n")); - strbuf_add_commented_lines(&buf, _(note_template), strlen(_(note_template))); - strbuf_addch(&buf, '\n'); - write_or_die(fd, buf.buf, buf.len); - - write_commented_object(fd, object); - - close(fd); - strbuf_release(&buf); - strbuf_reset(&d->buf); - - if (launch_editor(d->edit_path, &d->buf, NULL)) { - die(_("please supply the note contents using either -m or -F option")); - } - strbuf_stripspace(&d->buf, 1); - } -} - -static void write_note_data(struct note_data *d, struct object_id *oid) -{ - if (write_object_file(d->buf.buf, d->buf.len, blob_type, oid)) { - error(_("unable to write note object")); - if (d->edit_path) - error(_("the note contents have been left in %s"), - d->edit_path); - exit(128); - } -} - -static int parse_msg_arg(const struct option *opt, const char *arg, int unset) -{ - struct note_data *d = opt->value; - - BUG_ON_OPT_NEG(unset); - - strbuf_grow(&d->buf, strlen(arg) + 2); - if (d->buf.len) - strbuf_addch(&d->buf, '\n'); - strbuf_addstr(&d->buf, arg); - strbuf_stripspace(&d->buf, 0); - - d->given = 1; - return 0; -} - -static int parse_file_arg(const struct option *opt, const char *arg, int unset) -{ - struct note_data *d = opt->value; - - BUG_ON_OPT_NEG(unset); - - if (d->buf.len) - strbuf_addch(&d->buf, '\n'); - if (!strcmp(arg, "-")) { - if (strbuf_read(&d->buf, 0, 1024) < 0) - die_errno(_("cannot read '%s'"), arg); - } else if (strbuf_read_file(&d->buf, arg, 1024) < 0) - die_errno(_("could not open or read '%s'"), arg); - strbuf_stripspace(&d->buf, 0); - - d->given = 1; - return 0; -} - -static int parse_reuse_arg(const struct option *opt, const char *arg, int unset) -{ - struct note_data *d = opt->value; - char *buf; - struct object_id object; - enum object_type type; - unsigned long len; - - BUG_ON_OPT_NEG(unset); - - if (d->buf.len) - strbuf_addch(&d->buf, '\n'); - - if (get_oid(arg, &object)) - die(_("failed to resolve '%s' as a valid ref."), arg); - if (!(buf = read_object_file(&object, &type, &len))) - die(_("failed to read object '%s'."), arg); - if (type != OBJ_BLOB) { - free(buf); - die(_("cannot read note data from non-blob object '%s'."), arg); - } - strbuf_add(&d->buf, buf, len); - free(buf); - - d->given = 1; - return 0; -} - -static int parse_reedit_arg(const struct option *opt, const char *arg, int unset) -{ - struct note_data *d = opt->value; - BUG_ON_OPT_NEG(unset); - d->use_editor = 1; - return parse_reuse_arg(opt, arg, unset); -} - -static int notes_copy_from_stdin(int force, const char *rewrite_cmd) -{ - struct strbuf buf = STRBUF_INIT; - struct notes_rewrite_cfg *c = NULL; - struct notes_tree *t = NULL; - int ret = 0; - const char *msg = "Notes added by 'git notes copy'"; - - if (rewrite_cmd) { - c = init_copy_notes_for_rewrite(rewrite_cmd); - if (!c) - return 0; - } else { - init_notes(NULL, NULL, NULL, NOTES_INIT_WRITABLE); - t = &default_notes_tree; - } - - while (strbuf_getline_lf(&buf, stdin) != EOF) { - struct object_id from_obj, to_obj; - struct strbuf **split; - int err; - - split = strbuf_split(&buf, ' '); - if (!split[0] || !split[1]) - die(_("malformed input line: '%s'."), buf.buf); - strbuf_rtrim(split[0]); - strbuf_rtrim(split[1]); - if (get_oid(split[0]->buf, &from_obj)) - die(_("failed to resolve '%s' as a valid ref."), split[0]->buf); - if (get_oid(split[1]->buf, &to_obj)) - die(_("failed to resolve '%s' as a valid ref."), split[1]->buf); - - if (rewrite_cmd) - err = copy_note_for_rewrite(c, &from_obj, &to_obj); - else - err = copy_note(t, &from_obj, &to_obj, force, - combine_notes_overwrite); - - if (err) { - error(_("failed to copy notes from '%s' to '%s'"), - split[0]->buf, split[1]->buf); - ret = 1; - } - - strbuf_list_free(split); - } - - if (!rewrite_cmd) { - commit_notes(the_repository, t, msg); - free_notes(t); - } else { - finish_copy_notes_for_rewrite(the_repository, c, msg); - } - strbuf_release(&buf); - return ret; -} - -static struct notes_tree *init_notes_check(const char *subcommand, - int flags) -{ - struct notes_tree *t; - const char *ref; - init_notes(NULL, NULL, NULL, flags); - t = &default_notes_tree; - - ref = (flags & NOTES_INIT_WRITABLE) ? t->update_ref : t->ref; - if (!starts_with(ref, "refs/notes/")) - /* - * TRANSLATORS: the first %s will be replaced by a git - * notes command: 'add', 'merge', 'remove', etc. - */ - die(_("refusing to %s notes in %s (outside of refs/notes/)"), - subcommand, ref); - return t; -} - -static int list(int argc, const char **argv, const char *prefix) -{ - struct notes_tree *t; - struct object_id object; - const struct object_id *note; - int retval = -1; - struct option options[] = { - OPT_END() - }; - - if (argc) - argc = parse_options(argc, argv, prefix, options, - git_notes_list_usage, 0); - - if (1 < argc) { - error(_("too many parameters")); - usage_with_options(git_notes_list_usage, options); - } - - t = init_notes_check("list", 0); - if (argc) { - if (get_oid(argv[0], &object)) - die(_("failed to resolve '%s' as a valid ref."), argv[0]); - note = get_note(t, &object); - if (note) { - puts(oid_to_hex(note)); - retval = 0; - } else - retval = error(_("no note found for object %s."), - oid_to_hex(&object)); - } else - retval = for_each_note(t, 0, list_each_note, NULL); - - free_notes(t); - return retval; -} - -static int append_edit(int argc, const char **argv, const char *prefix); - -static int add(int argc, const char **argv, const char *prefix) -{ - int force = 0, allow_empty = 0; - const char *object_ref; - struct notes_tree *t; - struct object_id object, new_note; - const struct object_id *note; - struct note_data d = { 0, 0, NULL, STRBUF_INIT }; - struct option options[] = { - OPT_CALLBACK_F('m', "message", &d, N_("message"), - N_("note contents as a string"), PARSE_OPT_NONEG, - parse_msg_arg), - OPT_CALLBACK_F('F', "file", &d, N_("file"), - N_("note contents in a file"), PARSE_OPT_NONEG, - parse_file_arg), - OPT_CALLBACK_F('c', "reedit-message", &d, N_("object"), - N_("reuse and edit specified note object"), PARSE_OPT_NONEG, - parse_reedit_arg), - OPT_CALLBACK_F('C', "reuse-message", &d, N_("object"), - N_("reuse specified note object"), PARSE_OPT_NONEG, - parse_reuse_arg), - OPT_BOOL(0, "allow-empty", &allow_empty, - N_("allow storing empty note")), - OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, git_notes_add_usage, - PARSE_OPT_KEEP_ARGV0); - - if (2 < argc) { - error(_("too many parameters")); - usage_with_options(git_notes_add_usage, options); - } - - object_ref = argc > 1 ? argv[1] : "HEAD"; - - if (get_oid(object_ref, &object)) - die(_("failed to resolve '%s' as a valid ref."), object_ref); - - t = init_notes_check("add", NOTES_INIT_WRITABLE); - note = get_note(t, &object); - - if (note) { - if (!force) { - free_notes(t); - if (d.given) { - free_note_data(&d); - return error(_("Cannot add notes. " - "Found existing notes for object %s. " - "Use '-f' to overwrite existing notes"), - oid_to_hex(&object)); - } - /* - * Redirect to "edit" subcommand. - * - * We only end up here if none of -m/-F/-c/-C or -f are - * given. The original args are therefore still in - * argv[0-1]. - */ - argv[0] = "edit"; - return append_edit(argc, argv, prefix); - } - fprintf(stderr, _("Overwriting existing notes for object %s\n"), - oid_to_hex(&object)); - } - - prepare_note_data(&object, &d, note); - if (d.buf.len || allow_empty) { - write_note_data(&d, &new_note); - if (add_note(t, &object, &new_note, combine_notes_overwrite)) - BUG("combine_notes_overwrite failed"); - commit_notes(the_repository, t, - "Notes added by 'git notes add'"); - } else { - fprintf(stderr, _("Removing note for object %s\n"), - oid_to_hex(&object)); - remove_note(t, object.hash); - commit_notes(the_repository, t, - "Notes removed by 'git notes add'"); - } - - free_note_data(&d); - free_notes(t); - return 0; -} - -static int copy(int argc, const char **argv, const char *prefix) -{ - int retval = 0, force = 0, from_stdin = 0; - const struct object_id *from_note, *note; - const char *object_ref; - struct object_id object, from_obj; - struct notes_tree *t; - const char *rewrite_cmd = NULL; - struct option options[] = { - OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE), - OPT_BOOL(0, "stdin", &from_stdin, N_("read objects from stdin")), - OPT_STRING(0, "for-rewrite", &rewrite_cmd, N_("command"), - N_("load rewriting config for <command> (implies " - "--stdin)")), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, git_notes_copy_usage, - 0); - - if (from_stdin || rewrite_cmd) { - if (argc) { - error(_("too many parameters")); - usage_with_options(git_notes_copy_usage, options); - } else { - return notes_copy_from_stdin(force, rewrite_cmd); - } - } - - if (argc < 1) { - error(_("too few parameters")); - usage_with_options(git_notes_copy_usage, options); - } - if (2 < argc) { - error(_("too many parameters")); - usage_with_options(git_notes_copy_usage, options); - } - - if (get_oid(argv[0], &from_obj)) - die(_("failed to resolve '%s' as a valid ref."), argv[0]); - - object_ref = 1 < argc ? argv[1] : "HEAD"; - - if (get_oid(object_ref, &object)) - die(_("failed to resolve '%s' as a valid ref."), object_ref); - - t = init_notes_check("copy", NOTES_INIT_WRITABLE); - note = get_note(t, &object); - - if (note) { - if (!force) { - retval = error(_("Cannot copy notes. Found existing " - "notes for object %s. Use '-f' to " - "overwrite existing notes"), - oid_to_hex(&object)); - goto out; - } - fprintf(stderr, _("Overwriting existing notes for object %s\n"), - oid_to_hex(&object)); - } - - from_note = get_note(t, &from_obj); - if (!from_note) { - retval = error(_("missing notes on source object %s. Cannot " - "copy."), oid_to_hex(&from_obj)); - goto out; - } - - if (add_note(t, &object, from_note, combine_notes_overwrite)) - BUG("combine_notes_overwrite failed"); - commit_notes(the_repository, t, - "Notes added by 'git notes copy'"); -out: - free_notes(t); - return retval; -} - -static int append_edit(int argc, const char **argv, const char *prefix) -{ - int allow_empty = 0; - const char *object_ref; - struct notes_tree *t; - struct object_id object, new_note; - const struct object_id *note; - char *logmsg; - const char * const *usage; - struct note_data d = { 0, 0, NULL, STRBUF_INIT }; - struct option options[] = { - OPT_CALLBACK_F('m', "message", &d, N_("message"), - N_("note contents as a string"), PARSE_OPT_NONEG, - parse_msg_arg), - OPT_CALLBACK_F('F', "file", &d, N_("file"), - N_("note contents in a file"), PARSE_OPT_NONEG, - parse_file_arg), - OPT_CALLBACK_F('c', "reedit-message", &d, N_("object"), - N_("reuse and edit specified note object"), PARSE_OPT_NONEG, - parse_reedit_arg), - OPT_CALLBACK_F('C', "reuse-message", &d, N_("object"), - N_("reuse specified note object"), PARSE_OPT_NONEG, - parse_reuse_arg), - OPT_BOOL(0, "allow-empty", &allow_empty, - N_("allow storing empty note")), - OPT_END() - }; - int edit = !strcmp(argv[0], "edit"); - - usage = edit ? git_notes_edit_usage : git_notes_append_usage; - argc = parse_options(argc, argv, prefix, options, usage, - PARSE_OPT_KEEP_ARGV0); - - if (2 < argc) { - error(_("too many parameters")); - usage_with_options(usage, options); - } - - if (d.given && edit) - fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated " - "for the 'edit' subcommand.\n" - "Please use 'git notes add -f -m/-F/-c/-C' instead.\n")); - - object_ref = 1 < argc ? argv[1] : "HEAD"; - - if (get_oid(object_ref, &object)) - die(_("failed to resolve '%s' as a valid ref."), object_ref); - - t = init_notes_check(argv[0], NOTES_INIT_WRITABLE); - note = get_note(t, &object); - - prepare_note_data(&object, &d, edit && note ? note : NULL); - - if (note && !edit) { - /* Append buf to previous note contents */ - unsigned long size; - enum object_type type; - char *prev_buf = read_object_file(note, &type, &size); - - strbuf_grow(&d.buf, size + 1); - if (d.buf.len && prev_buf && size) - strbuf_insertstr(&d.buf, 0, "\n"); - if (prev_buf && size) - strbuf_insert(&d.buf, 0, prev_buf, size); - free(prev_buf); - } - - if (d.buf.len || allow_empty) { - write_note_data(&d, &new_note); - if (add_note(t, &object, &new_note, combine_notes_overwrite)) - BUG("combine_notes_overwrite failed"); - logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]); - } else { - fprintf(stderr, _("Removing note for object %s\n"), - oid_to_hex(&object)); - remove_note(t, object.hash); - logmsg = xstrfmt("Notes removed by 'git notes %s'", argv[0]); - } - commit_notes(the_repository, t, logmsg); - - free(logmsg); - free_note_data(&d); - free_notes(t); - return 0; -} - -static int show(int argc, const char **argv, const char *prefix) -{ - const char *object_ref; - struct notes_tree *t; - struct object_id object; - const struct object_id *note; - int retval; - struct option options[] = { - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, git_notes_show_usage, - 0); - - if (1 < argc) { - error(_("too many parameters")); - usage_with_options(git_notes_show_usage, options); - } - - object_ref = argc ? argv[0] : "HEAD"; - - if (get_oid(object_ref, &object)) - die(_("failed to resolve '%s' as a valid ref."), object_ref); - - t = init_notes_check("show", 0); - note = get_note(t, &object); - - if (!note) - retval = error(_("no note found for object %s."), - oid_to_hex(&object)); - else { - const char *show_args[3] = {"show", oid_to_hex(note), NULL}; - retval = execv_git_cmd(show_args); - } - free_notes(t); - return retval; -} - -static int merge_abort(struct notes_merge_options *o) -{ - int ret = 0; - - /* - * Remove .git/NOTES_MERGE_PARTIAL and .git/NOTES_MERGE_REF, and call - * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE. - */ - - if (delete_ref(NULL, "NOTES_MERGE_PARTIAL", NULL, 0)) - ret += error(_("failed to delete ref NOTES_MERGE_PARTIAL")); - if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF)) - ret += error(_("failed to delete ref NOTES_MERGE_REF")); - if (notes_merge_abort(o)) - ret += error(_("failed to remove 'git notes merge' worktree")); - return ret; -} - -static int merge_commit(struct notes_merge_options *o) -{ - struct strbuf msg = STRBUF_INIT; - struct object_id oid, parent_oid; - struct notes_tree *t; - struct commit *partial; - struct pretty_print_context pretty_ctx; - void *local_ref_to_free; - int ret; - - /* - * Read partial merge result from .git/NOTES_MERGE_PARTIAL, - * and target notes ref from .git/NOTES_MERGE_REF. - */ - - if (get_oid("NOTES_MERGE_PARTIAL", &oid)) - die(_("failed to read ref NOTES_MERGE_PARTIAL")); - else if (!(partial = lookup_commit_reference(the_repository, &oid))) - die(_("could not find commit from NOTES_MERGE_PARTIAL.")); - else if (parse_commit(partial)) - die(_("could not parse commit from NOTES_MERGE_PARTIAL.")); - - if (partial->parents) - oidcpy(&parent_oid, &partial->parents->item->object.oid); - else - oidclr(&parent_oid); - - t = xcalloc(1, sizeof(struct notes_tree)); - init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0); - - o->local_ref = local_ref_to_free = - resolve_refdup("NOTES_MERGE_REF", 0, &oid, NULL); - if (!o->local_ref) - die(_("failed to resolve NOTES_MERGE_REF")); - - if (notes_merge_commit(o, t, partial, &oid)) - die(_("failed to finalize notes merge")); - - /* Reuse existing commit message in reflog message */ - memset(&pretty_ctx, 0, sizeof(pretty_ctx)); - format_commit_message(partial, "%s", &msg, &pretty_ctx); - strbuf_trim(&msg); - strbuf_insertstr(&msg, 0, "notes: "); - update_ref(msg.buf, o->local_ref, &oid, - is_null_oid(&parent_oid) ? NULL : &parent_oid, - 0, UPDATE_REFS_DIE_ON_ERR); - - free_notes(t); - strbuf_release(&msg); - ret = merge_abort(o); - free(local_ref_to_free); - return ret; -} - -static int git_config_get_notes_strategy(const char *key, - enum notes_merge_strategy *strategy) -{ - char *value; - - if (git_config_get_string(key, &value)) - return 1; - if (parse_notes_merge_strategy(value, strategy)) - git_die_config(key, _("unknown notes merge strategy %s"), value); - - free(value); - return 0; -} - -static int merge(int argc, const char **argv, const char *prefix) -{ - struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT; - struct object_id result_oid; - struct notes_tree *t; - struct notes_merge_options o; - int do_merge = 0, do_commit = 0, do_abort = 0; - int verbosity = 0, result; - const char *strategy = NULL; - struct option options[] = { - OPT_GROUP(N_("General options")), - OPT__VERBOSITY(&verbosity), - OPT_GROUP(N_("Merge options")), - OPT_STRING('s', "strategy", &strategy, N_("strategy"), - N_("resolve notes conflicts using the given strategy " - "(manual/ours/theirs/union/cat_sort_uniq)")), - OPT_GROUP(N_("Committing unmerged notes")), - OPT_SET_INT_F(0, "commit", &do_commit, - N_("finalize notes merge by committing unmerged notes"), - 1, PARSE_OPT_NONEG), - OPT_GROUP(N_("Aborting notes merge resolution")), - OPT_SET_INT_F(0, "abort", &do_abort, - N_("abort notes merge"), - 1, PARSE_OPT_NONEG), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, - git_notes_merge_usage, 0); - - if (strategy || do_commit + do_abort == 0) - do_merge = 1; - if (do_merge + do_commit + do_abort != 1) { - error(_("cannot mix --commit, --abort or -s/--strategy")); - usage_with_options(git_notes_merge_usage, options); - } - - if (do_merge && argc != 1) { - error(_("must specify a notes ref to merge")); - usage_with_options(git_notes_merge_usage, options); - } else if (!do_merge && argc) { - error(_("too many parameters")); - usage_with_options(git_notes_merge_usage, options); - } - - init_notes_merge_options(the_repository, &o); - o.verbosity = verbosity + NOTES_MERGE_VERBOSITY_DEFAULT; - - if (do_abort) - return merge_abort(&o); - if (do_commit) - return merge_commit(&o); - - o.local_ref = default_notes_ref(); - strbuf_addstr(&remote_ref, argv[0]); - expand_loose_notes_ref(&remote_ref); - o.remote_ref = remote_ref.buf; - - t = init_notes_check("merge", NOTES_INIT_WRITABLE); - - if (strategy) { - if (parse_notes_merge_strategy(strategy, &o.strategy)) { - error(_("unknown -s/--strategy: %s"), strategy); - usage_with_options(git_notes_merge_usage, options); - } - } else { - struct strbuf merge_key = STRBUF_INIT; - const char *short_ref = NULL; - - if (!skip_prefix(o.local_ref, "refs/notes/", &short_ref)) - BUG("local ref %s is outside of refs/notes/", - o.local_ref); - - strbuf_addf(&merge_key, "notes.%s.mergeStrategy", short_ref); - - if (git_config_get_notes_strategy(merge_key.buf, &o.strategy)) - git_config_get_notes_strategy("notes.mergeStrategy", &o.strategy); - - strbuf_release(&merge_key); - } - - strbuf_addf(&msg, "notes: Merged notes from %s into %s", - remote_ref.buf, default_notes_ref()); - strbuf_add(&(o.commit_msg), msg.buf + 7, msg.len - 7); /* skip "notes: " */ - - result = notes_merge(&o, t, &result_oid); - - if (result >= 0) /* Merge resulted (trivially) in result_oid */ - /* Update default notes ref with new commit */ - update_ref(msg.buf, default_notes_ref(), &result_oid, NULL, 0, - UPDATE_REFS_DIE_ON_ERR); - else { /* Merge has unresolved conflicts */ - const struct worktree *wt; - /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */ - update_ref(msg.buf, "NOTES_MERGE_PARTIAL", &result_oid, NULL, - 0, UPDATE_REFS_DIE_ON_ERR); - /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */ - wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref()); - if (wt) - die(_("a notes merge into %s is already in-progress at %s"), - default_notes_ref(), wt->path); - if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL)) - die(_("failed to store link to current notes ref (%s)"), - default_notes_ref()); - fprintf(stderr, _("Automatic notes merge failed. Fix conflicts in %s " - "and commit the result with 'git notes merge --commit', " - "or abort the merge with 'git notes merge --abort'.\n"), - git_path(NOTES_MERGE_WORKTREE)); - } - - free_notes(t); - strbuf_release(&remote_ref); - strbuf_release(&msg); - return result < 0; /* return non-zero on conflicts */ -} - -#define IGNORE_MISSING 1 - -static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag) -{ - int status; - struct object_id oid; - if (get_oid(name, &oid)) - return error(_("Failed to resolve '%s' as a valid ref."), name); - status = remove_note(t, oid.hash); - if (status) - fprintf(stderr, _("Object %s has no note\n"), name); - else - fprintf(stderr, _("Removing note for object %s\n"), name); - return (flag & IGNORE_MISSING) ? 0 : status; -} - -static int remove_cmd(int argc, const char **argv, const char *prefix) -{ - unsigned flag = 0; - int from_stdin = 0; - struct option options[] = { - OPT_BIT(0, "ignore-missing", &flag, - N_("attempt to remove non-existent note is not an error"), - IGNORE_MISSING), - OPT_BOOL(0, "stdin", &from_stdin, - N_("read object names from the standard input")), - OPT_END() - }; - struct notes_tree *t; - int retval = 0; - - argc = parse_options(argc, argv, prefix, options, - git_notes_remove_usage, 0); - - t = init_notes_check("remove", NOTES_INIT_WRITABLE); - - if (!argc && !from_stdin) { - retval = remove_one_note(t, "HEAD", flag); - } else { - while (*argv) { - retval |= remove_one_note(t, *argv, flag); - argv++; - } - } - if (from_stdin) { - struct strbuf sb = STRBUF_INIT; - while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) { - strbuf_rtrim(&sb); - retval |= remove_one_note(t, sb.buf, flag); - } - strbuf_release(&sb); - } - if (!retval) - commit_notes(the_repository, t, - "Notes removed by 'git notes remove'"); - free_notes(t); - return retval; -} - -static int prune(int argc, const char **argv, const char *prefix) -{ - struct notes_tree *t; - int show_only = 0, verbose = 0; - struct option options[] = { - OPT__DRY_RUN(&show_only, N_("do not remove, show only")), - OPT__VERBOSE(&verbose, N_("report pruned notes")), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, git_notes_prune_usage, - 0); - - if (argc) { - error(_("too many parameters")); - usage_with_options(git_notes_prune_usage, options); - } - - t = init_notes_check("prune", NOTES_INIT_WRITABLE); - - prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) | - (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) ); - if (!show_only) - commit_notes(the_repository, t, - "Notes removed by 'git notes prune'"); - free_notes(t); - return 0; -} - -static int get_ref(int argc, const char **argv, const char *prefix) -{ - struct option options[] = { OPT_END() }; - argc = parse_options(argc, argv, prefix, options, - git_notes_get_ref_usage, 0); - - if (argc) { - error(_("too many parameters")); - usage_with_options(git_notes_get_ref_usage, options); - } - - puts(default_notes_ref()); - return 0; -} - -int cmd_notes(int argc, const char **argv, const char *prefix) -{ - int result; - const char *override_notes_ref = NULL; - struct option options[] = { - OPT_STRING(0, "ref", &override_notes_ref, N_("notes-ref"), - N_("use notes from <notes-ref>")), - OPT_END() - }; - - git_config(git_default_config, NULL); - argc = parse_options(argc, argv, prefix, options, git_notes_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - - if (override_notes_ref) { - struct strbuf sb = STRBUF_INIT; - strbuf_addstr(&sb, override_notes_ref); - expand_notes_ref(&sb); - setenv("GIT_NOTES_REF", sb.buf, 1); - strbuf_release(&sb); - } - - if (argc < 1 || !strcmp(argv[0], "list")) - result = list(argc, argv, prefix); - else if (!strcmp(argv[0], "add")) - result = add(argc, argv, prefix); - else if (!strcmp(argv[0], "copy")) - result = copy(argc, argv, prefix); - else if (!strcmp(argv[0], "append") || !strcmp(argv[0], "edit")) - result = append_edit(argc, argv, prefix); - else if (!strcmp(argv[0], "show")) - result = show(argc, argv, prefix); - else if (!strcmp(argv[0], "merge")) - result = merge(argc, argv, prefix); - else if (!strcmp(argv[0], "remove")) - result = remove_cmd(argc, argv, prefix); - else if (!strcmp(argv[0], "prune")) - result = prune(argc, argv, prefix); - else if (!strcmp(argv[0], "get-ref")) - result = get_ref(argc, argv, prefix); - else { - result = error(_("unknown subcommand: %s"), argv[0]); - usage_with_options(git_notes_usage, options); - } - - return result ? 1 : 0; -} diff --git a/third_party/git/builtin/pack-objects.c b/third_party/git/builtin/pack-objects.c deleted file mode 100644 index 5617c01b5aae..000000000000 --- a/third_party/git/builtin/pack-objects.c +++ /dev/null @@ -1,3772 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "repository.h" -#include "config.h" -#include "attr.h" -#include "object.h" -#include "blob.h" -#include "commit.h" -#include "tag.h" -#include "tree.h" -#include "delta.h" -#include "pack.h" -#include "pack-revindex.h" -#include "csum-file.h" -#include "tree-walk.h" -#include "diff.h" -#include "revision.h" -#include "list-objects.h" -#include "list-objects-filter.h" -#include "list-objects-filter-options.h" -#include "pack-objects.h" -#include "progress.h" -#include "refs.h" -#include "streaming.h" -#include "thread-utils.h" -#include "pack-bitmap.h" -#include "delta-islands.h" -#include "reachable.h" -#include "oid-array.h" -#include "strvec.h" -#include "list.h" -#include "packfile.h" -#include "object-store.h" -#include "dir.h" -#include "midx.h" -#include "trace2.h" -#include "shallow.h" -#include "promisor-remote.h" - -#define IN_PACK(obj) oe_in_pack(&to_pack, obj) -#define SIZE(obj) oe_size(&to_pack, obj) -#define SET_SIZE(obj,size) oe_set_size(&to_pack, obj, size) -#define DELTA_SIZE(obj) oe_delta_size(&to_pack, obj) -#define DELTA(obj) oe_delta(&to_pack, obj) -#define DELTA_CHILD(obj) oe_delta_child(&to_pack, obj) -#define DELTA_SIBLING(obj) oe_delta_sibling(&to_pack, obj) -#define SET_DELTA(obj, val) oe_set_delta(&to_pack, obj, val) -#define SET_DELTA_EXT(obj, oid) oe_set_delta_ext(&to_pack, obj, oid) -#define SET_DELTA_SIZE(obj, val) oe_set_delta_size(&to_pack, obj, val) -#define SET_DELTA_CHILD(obj, val) oe_set_delta_child(&to_pack, obj, val) -#define SET_DELTA_SIBLING(obj, val) oe_set_delta_sibling(&to_pack, obj, val) - -static const char *pack_usage[] = { - N_("git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"), - N_("git pack-objects [<options>...] <base-name> [< <ref-list> | < <object-list>]"), - NULL -}; - -/* - * Objects we are going to pack are collected in the `to_pack` structure. - * It contains an array (dynamically expanded) of the object data, and a map - * that can resolve SHA1s to their position in the array. - */ -static struct packing_data to_pack; - -static struct pack_idx_entry **written_list; -static uint32_t nr_result, nr_written, nr_seen; -static struct bitmap_index *bitmap_git; -static uint32_t write_layer; - -static int non_empty; -static int reuse_delta = 1, reuse_object = 1; -static int keep_unreachable, unpack_unreachable, include_tag; -static timestamp_t unpack_unreachable_expiration; -static int pack_loose_unreachable; -static int local; -static int have_non_local_packs; -static int incremental; -static int ignore_packed_keep_on_disk; -static int ignore_packed_keep_in_core; -static int allow_ofs_delta; -static struct pack_idx_option pack_idx_opts; -static const char *base_name; -static int progress = 1; -static int window = 10; -static unsigned long pack_size_limit; -static int depth = 50; -static int delta_search_threads; -static int pack_to_stdout; -static int sparse; -static int thin; -static int num_preferred_base; -static struct progress *progress_state; - -static struct packed_git *reuse_packfile; -static uint32_t reuse_packfile_objects; -static struct bitmap *reuse_packfile_bitmap; - -static int use_bitmap_index_default = 1; -static int use_bitmap_index = -1; -static int allow_pack_reuse = 1; -static enum { - WRITE_BITMAP_FALSE = 0, - WRITE_BITMAP_QUIET, - WRITE_BITMAP_TRUE, -} write_bitmap_index; -static uint16_t write_bitmap_options = BITMAP_OPT_HASH_CACHE; - -static int exclude_promisor_objects; - -static int use_delta_islands; - -static unsigned long delta_cache_size = 0; -static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE; -static unsigned long cache_max_small_delta_size = 1000; - -static unsigned long window_memory_limit = 0; - -static struct list_objects_filter_options filter_options; - -static struct string_list uri_protocols = STRING_LIST_INIT_NODUP; - -enum missing_action { - MA_ERROR = 0, /* fail if any missing objects are encountered */ - MA_ALLOW_ANY, /* silently allow ALL missing objects */ - MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */ -}; -static enum missing_action arg_missing_action; -static show_object_fn fn_show_object; - -struct configured_exclusion { - struct oidmap_entry e; - char *pack_hash_hex; - char *uri; -}; -static struct oidmap configured_exclusions; - -static struct oidset excluded_by_config; - -/* - * stats - */ -static uint32_t written, written_delta; -static uint32_t reused, reused_delta; - -/* - * Indexed commits - */ -static struct commit **indexed_commits; -static unsigned int indexed_commits_nr; -static unsigned int indexed_commits_alloc; - -static void index_commit_for_bitmap(struct commit *commit) -{ - if (indexed_commits_nr >= indexed_commits_alloc) { - indexed_commits_alloc = (indexed_commits_alloc + 32) * 2; - REALLOC_ARRAY(indexed_commits, indexed_commits_alloc); - } - - indexed_commits[indexed_commits_nr++] = commit; -} - -static void *get_delta(struct object_entry *entry) -{ - unsigned long size, base_size, delta_size; - void *buf, *base_buf, *delta_buf; - enum object_type type; - - buf = read_object_file(&entry->idx.oid, &type, &size); - if (!buf) - die(_("unable to read %s"), oid_to_hex(&entry->idx.oid)); - base_buf = read_object_file(&DELTA(entry)->idx.oid, &type, - &base_size); - if (!base_buf) - die("unable to read %s", - oid_to_hex(&DELTA(entry)->idx.oid)); - delta_buf = diff_delta(base_buf, base_size, - buf, size, &delta_size, 0); - /* - * We successfully computed this delta once but dropped it for - * memory reasons. Something is very wrong if this time we - * recompute and create a different delta. - */ - if (!delta_buf || delta_size != DELTA_SIZE(entry)) - BUG("delta size changed"); - free(buf); - free(base_buf); - return delta_buf; -} - -static unsigned long do_compress(void **pptr, unsigned long size) -{ - git_zstream stream; - void *in, *out; - unsigned long maxsize; - - git_deflate_init(&stream, pack_compression_level); - maxsize = git_deflate_bound(&stream, size); - - in = *pptr; - out = xmalloc(maxsize); - *pptr = out; - - stream.next_in = in; - stream.avail_in = size; - stream.next_out = out; - stream.avail_out = maxsize; - while (git_deflate(&stream, Z_FINISH) == Z_OK) - ; /* nothing */ - git_deflate_end(&stream); - - free(in); - return stream.total_out; -} - -static unsigned long write_large_blob_data(struct git_istream *st, struct hashfile *f, - const struct object_id *oid) -{ - git_zstream stream; - unsigned char ibuf[1024 * 16]; - unsigned char obuf[1024 * 16]; - unsigned long olen = 0; - - git_deflate_init(&stream, pack_compression_level); - - for (;;) { - ssize_t readlen; - int zret = Z_OK; - readlen = read_istream(st, ibuf, sizeof(ibuf)); - if (readlen == -1) - die(_("unable to read %s"), oid_to_hex(oid)); - - stream.next_in = ibuf; - stream.avail_in = readlen; - while ((stream.avail_in || readlen == 0) && - (zret == Z_OK || zret == Z_BUF_ERROR)) { - stream.next_out = obuf; - stream.avail_out = sizeof(obuf); - zret = git_deflate(&stream, readlen ? 0 : Z_FINISH); - hashwrite(f, obuf, stream.next_out - obuf); - olen += stream.next_out - obuf; - } - if (stream.avail_in) - die(_("deflate error (%d)"), zret); - if (readlen == 0) { - if (zret != Z_STREAM_END) - die(_("deflate error (%d)"), zret); - break; - } - } - git_deflate_end(&stream); - return olen; -} - -/* - * we are going to reuse the existing object data as is. make - * sure it is not corrupt. - */ -static int check_pack_inflate(struct packed_git *p, - struct pack_window **w_curs, - off_t offset, - off_t len, - unsigned long expect) -{ - git_zstream stream; - unsigned char fakebuf[4096], *in; - int st; - - memset(&stream, 0, sizeof(stream)); - git_inflate_init(&stream); - do { - in = use_pack(p, w_curs, offset, &stream.avail_in); - stream.next_in = in; - stream.next_out = fakebuf; - stream.avail_out = sizeof(fakebuf); - st = git_inflate(&stream, Z_FINISH); - offset += stream.next_in - in; - } while (st == Z_OK || st == Z_BUF_ERROR); - git_inflate_end(&stream); - return (st == Z_STREAM_END && - stream.total_out == expect && - stream.total_in == len) ? 0 : -1; -} - -static void copy_pack_data(struct hashfile *f, - struct packed_git *p, - struct pack_window **w_curs, - off_t offset, - off_t len) -{ - unsigned char *in; - unsigned long avail; - - while (len) { - in = use_pack(p, w_curs, offset, &avail); - if (avail > len) - avail = (unsigned long)len; - hashwrite(f, in, avail); - offset += avail; - len -= avail; - } -} - -/* Return 0 if we will bust the pack-size limit */ -static unsigned long write_no_reuse_object(struct hashfile *f, struct object_entry *entry, - unsigned long limit, int usable_delta) -{ - unsigned long size, datalen; - unsigned char header[MAX_PACK_OBJECT_HEADER], - dheader[MAX_PACK_OBJECT_HEADER]; - unsigned hdrlen; - enum object_type type; - void *buf; - struct git_istream *st = NULL; - const unsigned hashsz = the_hash_algo->rawsz; - - if (!usable_delta) { - if (oe_type(entry) == OBJ_BLOB && - oe_size_greater_than(&to_pack, entry, big_file_threshold) && - (st = open_istream(the_repository, &entry->idx.oid, &type, - &size, NULL)) != NULL) - buf = NULL; - else { - buf = read_object_file(&entry->idx.oid, &type, &size); - if (!buf) - die(_("unable to read %s"), - oid_to_hex(&entry->idx.oid)); - } - /* - * make sure no cached delta data remains from a - * previous attempt before a pack split occurred. - */ - FREE_AND_NULL(entry->delta_data); - entry->z_delta_size = 0; - } else if (entry->delta_data) { - size = DELTA_SIZE(entry); - buf = entry->delta_data; - entry->delta_data = NULL; - type = (allow_ofs_delta && DELTA(entry)->idx.offset) ? - OBJ_OFS_DELTA : OBJ_REF_DELTA; - } else { - buf = get_delta(entry); - size = DELTA_SIZE(entry); - type = (allow_ofs_delta && DELTA(entry)->idx.offset) ? - OBJ_OFS_DELTA : OBJ_REF_DELTA; - } - - if (st) /* large blob case, just assume we don't compress well */ - datalen = size; - else if (entry->z_delta_size) - datalen = entry->z_delta_size; - else - datalen = do_compress(&buf, size); - - /* - * The object header is a byte of 'type' followed by zero or - * more bytes of length. - */ - hdrlen = encode_in_pack_object_header(header, sizeof(header), - type, size); - - if (type == OBJ_OFS_DELTA) { - /* - * Deltas with relative base contain an additional - * encoding of the relative offset for the delta - * base from this object's position in the pack. - */ - off_t ofs = entry->idx.offset - DELTA(entry)->idx.offset; - unsigned pos = sizeof(dheader) - 1; - dheader[pos] = ofs & 127; - while (ofs >>= 7) - dheader[--pos] = 128 | (--ofs & 127); - if (limit && hdrlen + sizeof(dheader) - pos + datalen + hashsz >= limit) { - if (st) - close_istream(st); - free(buf); - return 0; - } - hashwrite(f, header, hdrlen); - hashwrite(f, dheader + pos, sizeof(dheader) - pos); - hdrlen += sizeof(dheader) - pos; - } else if (type == OBJ_REF_DELTA) { - /* - * Deltas with a base reference contain - * additional bytes for the base object ID. - */ - if (limit && hdrlen + hashsz + datalen + hashsz >= limit) { - if (st) - close_istream(st); - free(buf); - return 0; - } - hashwrite(f, header, hdrlen); - hashwrite(f, DELTA(entry)->idx.oid.hash, hashsz); - hdrlen += hashsz; - } else { - if (limit && hdrlen + datalen + hashsz >= limit) { - if (st) - close_istream(st); - free(buf); - return 0; - } - hashwrite(f, header, hdrlen); - } - if (st) { - datalen = write_large_blob_data(st, f, &entry->idx.oid); - close_istream(st); - } else { - hashwrite(f, buf, datalen); - free(buf); - } - - return hdrlen + datalen; -} - -/* Return 0 if we will bust the pack-size limit */ -static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry, - unsigned long limit, int usable_delta) -{ - struct packed_git *p = IN_PACK(entry); - struct pack_window *w_curs = NULL; - struct revindex_entry *revidx; - off_t offset; - enum object_type type = oe_type(entry); - off_t datalen; - unsigned char header[MAX_PACK_OBJECT_HEADER], - dheader[MAX_PACK_OBJECT_HEADER]; - unsigned hdrlen; - const unsigned hashsz = the_hash_algo->rawsz; - unsigned long entry_size = SIZE(entry); - - if (DELTA(entry)) - type = (allow_ofs_delta && DELTA(entry)->idx.offset) ? - OBJ_OFS_DELTA : OBJ_REF_DELTA; - hdrlen = encode_in_pack_object_header(header, sizeof(header), - type, entry_size); - - offset = entry->in_pack_offset; - revidx = find_pack_revindex(p, offset); - datalen = revidx[1].offset - offset; - if (!pack_to_stdout && p->index_version > 1 && - check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) { - error(_("bad packed object CRC for %s"), - oid_to_hex(&entry->idx.oid)); - unuse_pack(&w_curs); - return write_no_reuse_object(f, entry, limit, usable_delta); - } - - offset += entry->in_pack_header_size; - datalen -= entry->in_pack_header_size; - - if (!pack_to_stdout && p->index_version == 1 && - check_pack_inflate(p, &w_curs, offset, datalen, entry_size)) { - error(_("corrupt packed object for %s"), - oid_to_hex(&entry->idx.oid)); - unuse_pack(&w_curs); - return write_no_reuse_object(f, entry, limit, usable_delta); - } - - if (type == OBJ_OFS_DELTA) { - off_t ofs = entry->idx.offset - DELTA(entry)->idx.offset; - unsigned pos = sizeof(dheader) - 1; - dheader[pos] = ofs & 127; - while (ofs >>= 7) - dheader[--pos] = 128 | (--ofs & 127); - if (limit && hdrlen + sizeof(dheader) - pos + datalen + hashsz >= limit) { - unuse_pack(&w_curs); - return 0; - } - hashwrite(f, header, hdrlen); - hashwrite(f, dheader + pos, sizeof(dheader) - pos); - hdrlen += sizeof(dheader) - pos; - reused_delta++; - } else if (type == OBJ_REF_DELTA) { - if (limit && hdrlen + hashsz + datalen + hashsz >= limit) { - unuse_pack(&w_curs); - return 0; - } - hashwrite(f, header, hdrlen); - hashwrite(f, DELTA(entry)->idx.oid.hash, hashsz); - hdrlen += hashsz; - reused_delta++; - } else { - if (limit && hdrlen + datalen + hashsz >= limit) { - unuse_pack(&w_curs); - return 0; - } - hashwrite(f, header, hdrlen); - } - copy_pack_data(f, p, &w_curs, offset, datalen); - unuse_pack(&w_curs); - reused++; - return hdrlen + datalen; -} - -/* Return 0 if we will bust the pack-size limit */ -static off_t write_object(struct hashfile *f, - struct object_entry *entry, - off_t write_offset) -{ - unsigned long limit; - off_t len; - int usable_delta, to_reuse; - - if (!pack_to_stdout) - crc32_begin(f); - - /* apply size limit if limited packsize and not first object */ - if (!pack_size_limit || !nr_written) - limit = 0; - else if (pack_size_limit <= write_offset) - /* - * the earlier object did not fit the limit; avoid - * mistaking this with unlimited (i.e. limit = 0). - */ - limit = 1; - else - limit = pack_size_limit - write_offset; - - if (!DELTA(entry)) - usable_delta = 0; /* no delta */ - else if (!pack_size_limit) - usable_delta = 1; /* unlimited packfile */ - else if (DELTA(entry)->idx.offset == (off_t)-1) - usable_delta = 0; /* base was written to another pack */ - else if (DELTA(entry)->idx.offset) - usable_delta = 1; /* base already exists in this pack */ - else - usable_delta = 0; /* base could end up in another pack */ - - if (!reuse_object) - to_reuse = 0; /* explicit */ - else if (!IN_PACK(entry)) - to_reuse = 0; /* can't reuse what we don't have */ - else if (oe_type(entry) == OBJ_REF_DELTA || - oe_type(entry) == OBJ_OFS_DELTA) - /* check_object() decided it for us ... */ - to_reuse = usable_delta; - /* ... but pack split may override that */ - else if (oe_type(entry) != entry->in_pack_type) - to_reuse = 0; /* pack has delta which is unusable */ - else if (DELTA(entry)) - to_reuse = 0; /* we want to pack afresh */ - else - to_reuse = 1; /* we have it in-pack undeltified, - * and we do not need to deltify it. - */ - - if (!to_reuse) - len = write_no_reuse_object(f, entry, limit, usable_delta); - else - len = write_reuse_object(f, entry, limit, usable_delta); - if (!len) - return 0; - - if (usable_delta) - written_delta++; - written++; - if (!pack_to_stdout) - entry->idx.crc32 = crc32_end(f); - return len; -} - -enum write_one_status { - WRITE_ONE_SKIP = -1, /* already written */ - WRITE_ONE_BREAK = 0, /* writing this will bust the limit; not written */ - WRITE_ONE_WRITTEN = 1, /* normal */ - WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */ -}; - -static enum write_one_status write_one(struct hashfile *f, - struct object_entry *e, - off_t *offset) -{ - off_t size; - int recursing; - - /* - * we set offset to 1 (which is an impossible value) to mark - * the fact that this object is involved in "write its base - * first before writing a deltified object" recursion. - */ - recursing = (e->idx.offset == 1); - if (recursing) { - warning(_("recursive delta detected for object %s"), - oid_to_hex(&e->idx.oid)); - return WRITE_ONE_RECURSIVE; - } else if (e->idx.offset || e->preferred_base) { - /* offset is non zero if object is written already. */ - return WRITE_ONE_SKIP; - } - - /* if we are deltified, write out base object first. */ - if (DELTA(e)) { - e->idx.offset = 1; /* now recurse */ - switch (write_one(f, DELTA(e), offset)) { - case WRITE_ONE_RECURSIVE: - /* we cannot depend on this one */ - SET_DELTA(e, NULL); - break; - default: - break; - case WRITE_ONE_BREAK: - e->idx.offset = recursing; - return WRITE_ONE_BREAK; - } - } - - e->idx.offset = *offset; - size = write_object(f, e, *offset); - if (!size) { - e->idx.offset = recursing; - return WRITE_ONE_BREAK; - } - written_list[nr_written++] = &e->idx; - - /* make sure off_t is sufficiently large not to wrap */ - if (signed_add_overflows(*offset, size)) - die(_("pack too large for current definition of off_t")); - *offset += size; - return WRITE_ONE_WRITTEN; -} - -static int mark_tagged(const char *path, const struct object_id *oid, int flag, - void *cb_data) -{ - struct object_id peeled; - struct object_entry *entry = packlist_find(&to_pack, oid); - - if (entry) - entry->tagged = 1; - if (!peel_ref(path, &peeled)) { - entry = packlist_find(&to_pack, &peeled); - if (entry) - entry->tagged = 1; - } - return 0; -} - -static inline void add_to_write_order(struct object_entry **wo, - unsigned int *endp, - struct object_entry *e) -{ - if (e->filled || oe_layer(&to_pack, e) != write_layer) - return; - wo[(*endp)++] = e; - e->filled = 1; -} - -static void add_descendants_to_write_order(struct object_entry **wo, - unsigned int *endp, - struct object_entry *e) -{ - int add_to_order = 1; - while (e) { - if (add_to_order) { - struct object_entry *s; - /* add this node... */ - add_to_write_order(wo, endp, e); - /* all its siblings... */ - for (s = DELTA_SIBLING(e); s; s = DELTA_SIBLING(s)) { - add_to_write_order(wo, endp, s); - } - } - /* drop down a level to add left subtree nodes if possible */ - if (DELTA_CHILD(e)) { - add_to_order = 1; - e = DELTA_CHILD(e); - } else { - add_to_order = 0; - /* our sibling might have some children, it is next */ - if (DELTA_SIBLING(e)) { - e = DELTA_SIBLING(e); - continue; - } - /* go back to our parent node */ - e = DELTA(e); - while (e && !DELTA_SIBLING(e)) { - /* we're on the right side of a subtree, keep - * going up until we can go right again */ - e = DELTA(e); - } - if (!e) { - /* done- we hit our original root node */ - return; - } - /* pass it off to sibling at this level */ - e = DELTA_SIBLING(e); - } - }; -} - -static void add_family_to_write_order(struct object_entry **wo, - unsigned int *endp, - struct object_entry *e) -{ - struct object_entry *root; - - for (root = e; DELTA(root); root = DELTA(root)) - ; /* nothing */ - add_descendants_to_write_order(wo, endp, root); -} - -static void compute_layer_order(struct object_entry **wo, unsigned int *wo_end) -{ - unsigned int i, last_untagged; - struct object_entry *objects = to_pack.objects; - - for (i = 0; i < to_pack.nr_objects; i++) { - if (objects[i].tagged) - break; - add_to_write_order(wo, wo_end, &objects[i]); - } - last_untagged = i; - - /* - * Then fill all the tagged tips. - */ - for (; i < to_pack.nr_objects; i++) { - if (objects[i].tagged) - add_to_write_order(wo, wo_end, &objects[i]); - } - - /* - * And then all remaining commits and tags. - */ - for (i = last_untagged; i < to_pack.nr_objects; i++) { - if (oe_type(&objects[i]) != OBJ_COMMIT && - oe_type(&objects[i]) != OBJ_TAG) - continue; - add_to_write_order(wo, wo_end, &objects[i]); - } - - /* - * And then all the trees. - */ - for (i = last_untagged; i < to_pack.nr_objects; i++) { - if (oe_type(&objects[i]) != OBJ_TREE) - continue; - add_to_write_order(wo, wo_end, &objects[i]); - } - - /* - * Finally all the rest in really tight order - */ - for (i = last_untagged; i < to_pack.nr_objects; i++) { - if (!objects[i].filled && oe_layer(&to_pack, &objects[i]) == write_layer) - add_family_to_write_order(wo, wo_end, &objects[i]); - } -} - -static struct object_entry **compute_write_order(void) -{ - uint32_t max_layers = 1; - unsigned int i, wo_end; - - struct object_entry **wo; - struct object_entry *objects = to_pack.objects; - - for (i = 0; i < to_pack.nr_objects; i++) { - objects[i].tagged = 0; - objects[i].filled = 0; - SET_DELTA_CHILD(&objects[i], NULL); - SET_DELTA_SIBLING(&objects[i], NULL); - } - - /* - * Fully connect delta_child/delta_sibling network. - * Make sure delta_sibling is sorted in the original - * recency order. - */ - for (i = to_pack.nr_objects; i > 0;) { - struct object_entry *e = &objects[--i]; - if (!DELTA(e)) - continue; - /* Mark me as the first child */ - e->delta_sibling_idx = DELTA(e)->delta_child_idx; - SET_DELTA_CHILD(DELTA(e), e); - } - - /* - * Mark objects that are at the tip of tags. - */ - for_each_tag_ref(mark_tagged, NULL); - - if (use_delta_islands) - max_layers = compute_pack_layers(&to_pack); - - ALLOC_ARRAY(wo, to_pack.nr_objects); - wo_end = 0; - - for (; write_layer < max_layers; ++write_layer) - compute_layer_order(wo, &wo_end); - - if (wo_end != to_pack.nr_objects) - die(_("ordered %u objects, expected %"PRIu32), - wo_end, to_pack.nr_objects); - - return wo; -} - - -/* - * A reused set of objects. All objects in a chunk have the same - * relative position in the original packfile and the generated - * packfile. - */ - -static struct reused_chunk { - /* The offset of the first object of this chunk in the original - * packfile. */ - off_t original; - /* The offset of the first object of this chunk in the generated - * packfile minus "original". */ - off_t difference; -} *reused_chunks; -static int reused_chunks_nr; -static int reused_chunks_alloc; - -static void record_reused_object(off_t where, off_t offset) -{ - if (reused_chunks_nr && reused_chunks[reused_chunks_nr-1].difference == offset) - return; - - ALLOC_GROW(reused_chunks, reused_chunks_nr + 1, - reused_chunks_alloc); - reused_chunks[reused_chunks_nr].original = where; - reused_chunks[reused_chunks_nr].difference = offset; - reused_chunks_nr++; -} - -/* - * Binary search to find the chunk that "where" is in. Note - * that we're not looking for an exact match, just the first - * chunk that contains it (which implicitly ends at the start - * of the next chunk. - */ -static off_t find_reused_offset(off_t where) -{ - int lo = 0, hi = reused_chunks_nr; - while (lo < hi) { - int mi = lo + ((hi - lo) / 2); - if (where == reused_chunks[mi].original) - return reused_chunks[mi].difference; - if (where < reused_chunks[mi].original) - hi = mi; - else - lo = mi + 1; - } - - /* - * The first chunk starts at zero, so we can't have gone below - * there. - */ - assert(lo); - return reused_chunks[lo-1].difference; -} - -static void write_reused_pack_one(size_t pos, struct hashfile *out, - struct pack_window **w_curs) -{ - off_t offset, next, cur; - enum object_type type; - unsigned long size; - - offset = reuse_packfile->revindex[pos].offset; - next = reuse_packfile->revindex[pos + 1].offset; - - record_reused_object(offset, offset - hashfile_total(out)); - - cur = offset; - type = unpack_object_header(reuse_packfile, w_curs, &cur, &size); - assert(type >= 0); - - if (type == OBJ_OFS_DELTA) { - off_t base_offset; - off_t fixup; - - unsigned char header[MAX_PACK_OBJECT_HEADER]; - unsigned len; - - base_offset = get_delta_base(reuse_packfile, w_curs, &cur, type, offset); - assert(base_offset != 0); - - /* Convert to REF_DELTA if we must... */ - if (!allow_ofs_delta) { - int base_pos = find_revindex_position(reuse_packfile, base_offset); - struct object_id base_oid; - - nth_packed_object_id(&base_oid, reuse_packfile, - reuse_packfile->revindex[base_pos].nr); - - len = encode_in_pack_object_header(header, sizeof(header), - OBJ_REF_DELTA, size); - hashwrite(out, header, len); - hashwrite(out, base_oid.hash, the_hash_algo->rawsz); - copy_pack_data(out, reuse_packfile, w_curs, cur, next - cur); - return; - } - - /* Otherwise see if we need to rewrite the offset... */ - fixup = find_reused_offset(offset) - - find_reused_offset(base_offset); - if (fixup) { - unsigned char ofs_header[10]; - unsigned i, ofs_len; - off_t ofs = offset - base_offset - fixup; - - len = encode_in_pack_object_header(header, sizeof(header), - OBJ_OFS_DELTA, size); - - i = sizeof(ofs_header) - 1; - ofs_header[i] = ofs & 127; - while (ofs >>= 7) - ofs_header[--i] = 128 | (--ofs & 127); - - ofs_len = sizeof(ofs_header) - i; - - hashwrite(out, header, len); - hashwrite(out, ofs_header + sizeof(ofs_header) - ofs_len, ofs_len); - copy_pack_data(out, reuse_packfile, w_curs, cur, next - cur); - return; - } - - /* ...otherwise we have no fixup, and can write it verbatim */ - } - - copy_pack_data(out, reuse_packfile, w_curs, offset, next - offset); -} - -static size_t write_reused_pack_verbatim(struct hashfile *out, - struct pack_window **w_curs) -{ - size_t pos = 0; - - while (pos < reuse_packfile_bitmap->word_alloc && - reuse_packfile_bitmap->words[pos] == (eword_t)~0) - pos++; - - if (pos) { - off_t to_write; - - written = (pos * BITS_IN_EWORD); - to_write = reuse_packfile->revindex[written].offset - - sizeof(struct pack_header); - - /* We're recording one chunk, not one object. */ - record_reused_object(sizeof(struct pack_header), 0); - hashflush(out); - copy_pack_data(out, reuse_packfile, w_curs, - sizeof(struct pack_header), to_write); - - display_progress(progress_state, written); - } - return pos; -} - -static void write_reused_pack(struct hashfile *f) -{ - size_t i = 0; - uint32_t offset; - struct pack_window *w_curs = NULL; - - if (allow_ofs_delta) - i = write_reused_pack_verbatim(f, &w_curs); - - for (; i < reuse_packfile_bitmap->word_alloc; ++i) { - eword_t word = reuse_packfile_bitmap->words[i]; - size_t pos = (i * BITS_IN_EWORD); - - for (offset = 0; offset < BITS_IN_EWORD; ++offset) { - if ((word >> offset) == 0) - break; - - offset += ewah_bit_ctz64(word >> offset); - write_reused_pack_one(pos + offset, f, &w_curs); - display_progress(progress_state, ++written); - } - } - - unuse_pack(&w_curs); -} - -static void write_excluded_by_configs(void) -{ - struct oidset_iter iter; - const struct object_id *oid; - - oidset_iter_init(&excluded_by_config, &iter); - while ((oid = oidset_iter_next(&iter))) { - struct configured_exclusion *ex = - oidmap_get(&configured_exclusions, oid); - - if (!ex) - BUG("configured exclusion wasn't configured"); - write_in_full(1, ex->pack_hash_hex, strlen(ex->pack_hash_hex)); - write_in_full(1, " ", 1); - write_in_full(1, ex->uri, strlen(ex->uri)); - write_in_full(1, "\n", 1); - } -} - -static const char no_split_warning[] = N_( -"disabling bitmap writing, packs are split due to pack.packSizeLimit" -); - -static void write_pack_file(void) -{ - uint32_t i = 0, j; - struct hashfile *f; - off_t offset; - uint32_t nr_remaining = nr_result; - time_t last_mtime = 0; - struct object_entry **write_order; - - if (progress > pack_to_stdout) - progress_state = start_progress(_("Writing objects"), nr_result); - ALLOC_ARRAY(written_list, to_pack.nr_objects); - write_order = compute_write_order(); - - do { - struct object_id oid; - char *pack_tmp_name = NULL; - - if (pack_to_stdout) - f = hashfd_throughput(1, "<stdout>", progress_state); - else - f = create_tmp_packfile(&pack_tmp_name); - - offset = write_pack_header(f, nr_remaining); - - if (reuse_packfile) { - assert(pack_to_stdout); - write_reused_pack(f); - offset = hashfile_total(f); - } - - nr_written = 0; - for (; i < to_pack.nr_objects; i++) { - struct object_entry *e = write_order[i]; - if (write_one(f, e, &offset) == WRITE_ONE_BREAK) - break; - display_progress(progress_state, written); - } - - /* - * Did we write the wrong # entries in the header? - * If so, rewrite it like in fast-import - */ - if (pack_to_stdout) { - finalize_hashfile(f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_CLOSE); - } else if (nr_written == nr_remaining) { - finalize_hashfile(f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE); - } else { - int fd = finalize_hashfile(f, oid.hash, 0); - fixup_pack_header_footer(fd, oid.hash, pack_tmp_name, - nr_written, oid.hash, offset); - close(fd); - if (write_bitmap_index) { - if (write_bitmap_index != WRITE_BITMAP_QUIET) - warning(_(no_split_warning)); - write_bitmap_index = 0; - } - } - - if (!pack_to_stdout) { - struct stat st; - struct strbuf tmpname = STRBUF_INIT; - - /* - * Packs are runtime accessed in their mtime - * order since newer packs are more likely to contain - * younger objects. So if we are creating multiple - * packs then we should modify the mtime of later ones - * to preserve this property. - */ - if (stat(pack_tmp_name, &st) < 0) { - warning_errno(_("failed to stat %s"), pack_tmp_name); - } else if (!last_mtime) { - last_mtime = st.st_mtime; - } else { - struct utimbuf utb; - utb.actime = st.st_atime; - utb.modtime = --last_mtime; - if (utime(pack_tmp_name, &utb) < 0) - warning_errno(_("failed utime() on %s"), pack_tmp_name); - } - - strbuf_addf(&tmpname, "%s-", base_name); - - if (write_bitmap_index) { - bitmap_writer_set_checksum(oid.hash); - bitmap_writer_build_type_index( - &to_pack, written_list, nr_written); - } - - finish_tmp_packfile(&tmpname, pack_tmp_name, - written_list, nr_written, - &pack_idx_opts, oid.hash); - - if (write_bitmap_index) { - strbuf_addf(&tmpname, "%s.bitmap", oid_to_hex(&oid)); - - stop_progress(&progress_state); - - bitmap_writer_show_progress(progress); - bitmap_writer_reuse_bitmaps(&to_pack); - bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1); - bitmap_writer_build(&to_pack); - bitmap_writer_finish(written_list, nr_written, - tmpname.buf, write_bitmap_options); - write_bitmap_index = 0; - } - - strbuf_release(&tmpname); - free(pack_tmp_name); - puts(oid_to_hex(&oid)); - } - - /* mark written objects as written to previous pack */ - for (j = 0; j < nr_written; j++) { - written_list[j]->offset = (off_t)-1; - } - nr_remaining -= nr_written; - } while (nr_remaining && i < to_pack.nr_objects); - - free(written_list); - free(write_order); - stop_progress(&progress_state); - if (written != nr_result) - die(_("wrote %"PRIu32" objects while expecting %"PRIu32), - written, nr_result); - trace2_data_intmax("pack-objects", the_repository, - "write_pack_file/wrote", nr_result); -} - -static int no_try_delta(const char *path) -{ - static struct attr_check *check; - - if (!check) - check = attr_check_initl("delta", NULL); - git_check_attr(the_repository->index, path, check); - if (ATTR_FALSE(check->items[0].value)) - return 1; - return 0; -} - -/* - * When adding an object, check whether we have already added it - * to our packing list. If so, we can skip. However, if we are - * being asked to excludei t, but the previous mention was to include - * it, make sure to adjust its flags and tweak our numbers accordingly. - * - * As an optimization, we pass out the index position where we would have - * found the item, since that saves us from having to look it up again a - * few lines later when we want to add the new entry. - */ -static int have_duplicate_entry(const struct object_id *oid, - int exclude) -{ - struct object_entry *entry; - - if (reuse_packfile_bitmap && - bitmap_walk_contains(bitmap_git, reuse_packfile_bitmap, oid)) - return 1; - - entry = packlist_find(&to_pack, oid); - if (!entry) - return 0; - - if (exclude) { - if (!entry->preferred_base) - nr_result--; - entry->preferred_base = 1; - } - - return 1; -} - -static int want_found_object(int exclude, struct packed_git *p) -{ - if (exclude) - return 1; - if (incremental) - return 0; - - /* - * When asked to do --local (do not include an object that appears in a - * pack we borrow from elsewhere) or --honor-pack-keep (do not include - * an object that appears in a pack marked with .keep), finding a pack - * that matches the criteria is sufficient for us to decide to omit it. - * However, even if this pack does not satisfy the criteria, we need to - * make sure no copy of this object appears in _any_ pack that makes us - * to omit the object, so we need to check all the packs. - * - * We can however first check whether these options can possible matter; - * if they do not matter we know we want the object in generated pack. - * Otherwise, we signal "-1" at the end to tell the caller that we do - * not know either way, and it needs to check more packs. - */ - if (!ignore_packed_keep_on_disk && - !ignore_packed_keep_in_core && - (!local || !have_non_local_packs)) - return 1; - - if (local && !p->pack_local) - return 0; - if (p->pack_local && - ((ignore_packed_keep_on_disk && p->pack_keep) || - (ignore_packed_keep_in_core && p->pack_keep_in_core))) - return 0; - - /* we don't know yet; keep looking for more packs */ - return -1; -} - -/* - * Check whether we want the object in the pack (e.g., we do not want - * objects found in non-local stores if the "--local" option was used). - * - * If the caller already knows an existing pack it wants to take the object - * from, that is passed in *found_pack and *found_offset; otherwise this - * function finds if there is any pack that has the object and returns the pack - * and its offset in these variables. - */ -static int want_object_in_pack(const struct object_id *oid, - int exclude, - struct packed_git **found_pack, - off_t *found_offset) -{ - int want; - struct list_head *pos; - struct multi_pack_index *m; - - if (!exclude && local && has_loose_object_nonlocal(oid)) - return 0; - - /* - * If we already know the pack object lives in, start checks from that - * pack - in the usual case when neither --local was given nor .keep files - * are present we will determine the answer right now. - */ - if (*found_pack) { - want = want_found_object(exclude, *found_pack); - if (want != -1) - return want; - } - - for (m = get_multi_pack_index(the_repository); m; m = m->next) { - struct pack_entry e; - if (fill_midx_entry(the_repository, oid, &e, m)) { - struct packed_git *p = e.p; - off_t offset; - - if (p == *found_pack) - offset = *found_offset; - else - offset = find_pack_entry_one(oid->hash, p); - - if (offset) { - if (!*found_pack) { - if (!is_pack_valid(p)) - continue; - *found_offset = offset; - *found_pack = p; - } - want = want_found_object(exclude, p); - if (want != -1) - return want; - } - } - } - - list_for_each(pos, get_packed_git_mru(the_repository)) { - struct packed_git *p = list_entry(pos, struct packed_git, mru); - off_t offset; - - if (p == *found_pack) - offset = *found_offset; - else - offset = find_pack_entry_one(oid->hash, p); - - if (offset) { - if (!*found_pack) { - if (!is_pack_valid(p)) - continue; - *found_offset = offset; - *found_pack = p; - } - want = want_found_object(exclude, p); - if (!exclude && want > 0) - list_move(&p->mru, - get_packed_git_mru(the_repository)); - if (want != -1) - return want; - } - } - - if (uri_protocols.nr) { - struct configured_exclusion *ex = - oidmap_get(&configured_exclusions, oid); - int i; - const char *p; - - if (ex) { - for (i = 0; i < uri_protocols.nr; i++) { - if (skip_prefix(ex->uri, - uri_protocols.items[i].string, - &p) && - *p == ':') { - oidset_insert(&excluded_by_config, oid); - return 0; - } - } - } - } - - return 1; -} - -static void create_object_entry(const struct object_id *oid, - enum object_type type, - uint32_t hash, - int exclude, - int no_try_delta, - struct packed_git *found_pack, - off_t found_offset) -{ - struct object_entry *entry; - - entry = packlist_alloc(&to_pack, oid); - entry->hash = hash; - oe_set_type(entry, type); - if (exclude) - entry->preferred_base = 1; - else - nr_result++; - if (found_pack) { - oe_set_in_pack(&to_pack, entry, found_pack); - entry->in_pack_offset = found_offset; - } - - entry->no_try_delta = no_try_delta; -} - -static const char no_closure_warning[] = N_( -"disabling bitmap writing, as some objects are not being packed" -); - -static int add_object_entry(const struct object_id *oid, enum object_type type, - const char *name, int exclude) -{ - struct packed_git *found_pack = NULL; - off_t found_offset = 0; - - display_progress(progress_state, ++nr_seen); - - if (have_duplicate_entry(oid, exclude)) - return 0; - - if (!want_object_in_pack(oid, exclude, &found_pack, &found_offset)) { - /* The pack is missing an object, so it will not have closure */ - if (write_bitmap_index) { - if (write_bitmap_index != WRITE_BITMAP_QUIET) - warning(_(no_closure_warning)); - write_bitmap_index = 0; - } - return 0; - } - - create_object_entry(oid, type, pack_name_hash(name), - exclude, name && no_try_delta(name), - found_pack, found_offset); - return 1; -} - -static int add_object_entry_from_bitmap(const struct object_id *oid, - enum object_type type, - int flags, uint32_t name_hash, - struct packed_git *pack, off_t offset) -{ - display_progress(progress_state, ++nr_seen); - - if (have_duplicate_entry(oid, 0)) - return 0; - - if (!want_object_in_pack(oid, 0, &pack, &offset)) - return 0; - - create_object_entry(oid, type, name_hash, 0, 0, pack, offset); - return 1; -} - -struct pbase_tree_cache { - struct object_id oid; - int ref; - int temporary; - void *tree_data; - unsigned long tree_size; -}; - -static struct pbase_tree_cache *(pbase_tree_cache[256]); -static int pbase_tree_cache_ix(const struct object_id *oid) -{ - return oid->hash[0] % ARRAY_SIZE(pbase_tree_cache); -} -static int pbase_tree_cache_ix_incr(int ix) -{ - return (ix+1) % ARRAY_SIZE(pbase_tree_cache); -} - -static struct pbase_tree { - struct pbase_tree *next; - /* This is a phony "cache" entry; we are not - * going to evict it or find it through _get() - * mechanism -- this is for the toplevel node that - * would almost always change with any commit. - */ - struct pbase_tree_cache pcache; -} *pbase_tree; - -static struct pbase_tree_cache *pbase_tree_get(const struct object_id *oid) -{ - struct pbase_tree_cache *ent, *nent; - void *data; - unsigned long size; - enum object_type type; - int neigh; - int my_ix = pbase_tree_cache_ix(oid); - int available_ix = -1; - - /* pbase-tree-cache acts as a limited hashtable. - * your object will be found at your index or within a few - * slots after that slot if it is cached. - */ - for (neigh = 0; neigh < 8; neigh++) { - ent = pbase_tree_cache[my_ix]; - if (ent && oideq(&ent->oid, oid)) { - ent->ref++; - return ent; - } - else if (((available_ix < 0) && (!ent || !ent->ref)) || - ((0 <= available_ix) && - (!ent && pbase_tree_cache[available_ix]))) - available_ix = my_ix; - if (!ent) - break; - my_ix = pbase_tree_cache_ix_incr(my_ix); - } - - /* Did not find one. Either we got a bogus request or - * we need to read and perhaps cache. - */ - data = read_object_file(oid, &type, &size); - if (!data) - return NULL; - if (type != OBJ_TREE) { - free(data); - return NULL; - } - - /* We need to either cache or return a throwaway copy */ - - if (available_ix < 0) - ent = NULL; - else { - ent = pbase_tree_cache[available_ix]; - my_ix = available_ix; - } - - if (!ent) { - nent = xmalloc(sizeof(*nent)); - nent->temporary = (available_ix < 0); - } - else { - /* evict and reuse */ - free(ent->tree_data); - nent = ent; - } - oidcpy(&nent->oid, oid); - nent->tree_data = data; - nent->tree_size = size; - nent->ref = 1; - if (!nent->temporary) - pbase_tree_cache[my_ix] = nent; - return nent; -} - -static void pbase_tree_put(struct pbase_tree_cache *cache) -{ - if (!cache->temporary) { - cache->ref--; - return; - } - free(cache->tree_data); - free(cache); -} - -static int name_cmp_len(const char *name) -{ - int i; - for (i = 0; name[i] && name[i] != '\n' && name[i] != '/'; i++) - ; - return i; -} - -static void add_pbase_object(struct tree_desc *tree, - const char *name, - int cmplen, - const char *fullname) -{ - struct name_entry entry; - int cmp; - - while (tree_entry(tree,&entry)) { - if (S_ISGITLINK(entry.mode)) - continue; - cmp = tree_entry_len(&entry) != cmplen ? 1 : - memcmp(name, entry.path, cmplen); - if (cmp > 0) - continue; - if (cmp < 0) - return; - if (name[cmplen] != '/') { - add_object_entry(&entry.oid, - object_type(entry.mode), - fullname, 1); - return; - } - if (S_ISDIR(entry.mode)) { - struct tree_desc sub; - struct pbase_tree_cache *tree; - const char *down = name+cmplen+1; - int downlen = name_cmp_len(down); - - tree = pbase_tree_get(&entry.oid); - if (!tree) - return; - init_tree_desc(&sub, tree->tree_data, tree->tree_size); - - add_pbase_object(&sub, down, downlen, fullname); - pbase_tree_put(tree); - } - } -} - -static unsigned *done_pbase_paths; -static int done_pbase_paths_num; -static int done_pbase_paths_alloc; -static int done_pbase_path_pos(unsigned hash) -{ - int lo = 0; - int hi = done_pbase_paths_num; - while (lo < hi) { - int mi = lo + (hi - lo) / 2; - if (done_pbase_paths[mi] == hash) - return mi; - if (done_pbase_paths[mi] < hash) - hi = mi; - else - lo = mi + 1; - } - return -lo-1; -} - -static int check_pbase_path(unsigned hash) -{ - int pos = done_pbase_path_pos(hash); - if (0 <= pos) - return 1; - pos = -pos - 1; - ALLOC_GROW(done_pbase_paths, - done_pbase_paths_num + 1, - done_pbase_paths_alloc); - done_pbase_paths_num++; - if (pos < done_pbase_paths_num) - MOVE_ARRAY(done_pbase_paths + pos + 1, done_pbase_paths + pos, - done_pbase_paths_num - pos - 1); - done_pbase_paths[pos] = hash; - return 0; -} - -static void add_preferred_base_object(const char *name) -{ - struct pbase_tree *it; - int cmplen; - unsigned hash = pack_name_hash(name); - - if (!num_preferred_base || check_pbase_path(hash)) - return; - - cmplen = name_cmp_len(name); - for (it = pbase_tree; it; it = it->next) { - if (cmplen == 0) { - add_object_entry(&it->pcache.oid, OBJ_TREE, NULL, 1); - } - else { - struct tree_desc tree; - init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size); - add_pbase_object(&tree, name, cmplen, name); - } - } -} - -static void add_preferred_base(struct object_id *oid) -{ - struct pbase_tree *it; - void *data; - unsigned long size; - struct object_id tree_oid; - - if (window <= num_preferred_base++) - return; - - data = read_object_with_reference(the_repository, oid, - tree_type, &size, &tree_oid); - if (!data) - return; - - for (it = pbase_tree; it; it = it->next) { - if (oideq(&it->pcache.oid, &tree_oid)) { - free(data); - return; - } - } - - it = xcalloc(1, sizeof(*it)); - it->next = pbase_tree; - pbase_tree = it; - - oidcpy(&it->pcache.oid, &tree_oid); - it->pcache.tree_data = data; - it->pcache.tree_size = size; -} - -static void cleanup_preferred_base(void) -{ - struct pbase_tree *it; - unsigned i; - - it = pbase_tree; - pbase_tree = NULL; - while (it) { - struct pbase_tree *tmp = it; - it = tmp->next; - free(tmp->pcache.tree_data); - free(tmp); - } - - for (i = 0; i < ARRAY_SIZE(pbase_tree_cache); i++) { - if (!pbase_tree_cache[i]) - continue; - free(pbase_tree_cache[i]->tree_data); - FREE_AND_NULL(pbase_tree_cache[i]); - } - - FREE_AND_NULL(done_pbase_paths); - done_pbase_paths_num = done_pbase_paths_alloc = 0; -} - -/* - * Return 1 iff the object specified by "delta" can be sent - * literally as a delta against the base in "base_sha1". If - * so, then *base_out will point to the entry in our packing - * list, or NULL if we must use the external-base list. - * - * Depth value does not matter - find_deltas() will - * never consider reused delta as the base object to - * deltify other objects against, in order to avoid - * circular deltas. - */ -static int can_reuse_delta(const struct object_id *base_oid, - struct object_entry *delta, - struct object_entry **base_out) -{ - struct object_entry *base; - - /* - * First see if we're already sending the base (or it's explicitly in - * our "excluded" list). - */ - base = packlist_find(&to_pack, base_oid); - if (base) { - if (!in_same_island(&delta->idx.oid, &base->idx.oid)) - return 0; - *base_out = base; - return 1; - } - - /* - * Otherwise, reachability bitmaps may tell us if the receiver has it, - * even if it was buried too deep in history to make it into the - * packing list. - */ - if (thin && bitmap_has_oid_in_uninteresting(bitmap_git, base_oid)) { - if (use_delta_islands) { - if (!in_same_island(&delta->idx.oid, base_oid)) - return 0; - } - *base_out = NULL; - return 1; - } - - return 0; -} - -static void prefetch_to_pack(uint32_t object_index_start) { - struct oid_array to_fetch = OID_ARRAY_INIT; - uint32_t i; - - for (i = object_index_start; i < to_pack.nr_objects; i++) { - struct object_entry *entry = to_pack.objects + i; - - if (!oid_object_info_extended(the_repository, - &entry->idx.oid, - NULL, - OBJECT_INFO_FOR_PREFETCH)) - continue; - oid_array_append(&to_fetch, &entry->idx.oid); - } - promisor_remote_get_direct(the_repository, - to_fetch.oid, to_fetch.nr); - oid_array_clear(&to_fetch); -} - -static void check_object(struct object_entry *entry, uint32_t object_index) -{ - unsigned long canonical_size; - enum object_type type; - struct object_info oi = {.typep = &type, .sizep = &canonical_size}; - - if (IN_PACK(entry)) { - struct packed_git *p = IN_PACK(entry); - struct pack_window *w_curs = NULL; - int have_base = 0; - struct object_id base_ref; - struct object_entry *base_entry; - unsigned long used, used_0; - unsigned long avail; - off_t ofs; - unsigned char *buf, c; - enum object_type type; - unsigned long in_pack_size; - - buf = use_pack(p, &w_curs, entry->in_pack_offset, &avail); - - /* - * We want in_pack_type even if we do not reuse delta - * since non-delta representations could still be reused. - */ - used = unpack_object_header_buffer(buf, avail, - &type, - &in_pack_size); - if (used == 0) - goto give_up; - - if (type < 0) - BUG("invalid type %d", type); - entry->in_pack_type = type; - - /* - * Determine if this is a delta and if so whether we can - * reuse it or not. Otherwise let's find out as cheaply as - * possible what the actual type and size for this object is. - */ - switch (entry->in_pack_type) { - default: - /* Not a delta hence we've already got all we need. */ - oe_set_type(entry, entry->in_pack_type); - SET_SIZE(entry, in_pack_size); - entry->in_pack_header_size = used; - if (oe_type(entry) < OBJ_COMMIT || oe_type(entry) > OBJ_BLOB) - goto give_up; - unuse_pack(&w_curs); - return; - case OBJ_REF_DELTA: - if (reuse_delta && !entry->preferred_base) { - oidread(&base_ref, - use_pack(p, &w_curs, - entry->in_pack_offset + used, - NULL)); - have_base = 1; - } - entry->in_pack_header_size = used + the_hash_algo->rawsz; - break; - case OBJ_OFS_DELTA: - buf = use_pack(p, &w_curs, - entry->in_pack_offset + used, NULL); - used_0 = 0; - c = buf[used_0++]; - ofs = c & 127; - while (c & 128) { - ofs += 1; - if (!ofs || MSB(ofs, 7)) { - error(_("delta base offset overflow in pack for %s"), - oid_to_hex(&entry->idx.oid)); - goto give_up; - } - c = buf[used_0++]; - ofs = (ofs << 7) + (c & 127); - } - ofs = entry->in_pack_offset - ofs; - if (ofs <= 0 || ofs >= entry->in_pack_offset) { - error(_("delta base offset out of bound for %s"), - oid_to_hex(&entry->idx.oid)); - goto give_up; - } - if (reuse_delta && !entry->preferred_base) { - struct revindex_entry *revidx; - revidx = find_pack_revindex(p, ofs); - if (!revidx) - goto give_up; - if (!nth_packed_object_id(&base_ref, p, revidx->nr)) - have_base = 1; - } - entry->in_pack_header_size = used + used_0; - break; - } - - if (have_base && - can_reuse_delta(&base_ref, entry, &base_entry)) { - oe_set_type(entry, entry->in_pack_type); - SET_SIZE(entry, in_pack_size); /* delta size */ - SET_DELTA_SIZE(entry, in_pack_size); - - if (base_entry) { - SET_DELTA(entry, base_entry); - entry->delta_sibling_idx = base_entry->delta_child_idx; - SET_DELTA_CHILD(base_entry, entry); - } else { - SET_DELTA_EXT(entry, &base_ref); - } - - unuse_pack(&w_curs); - return; - } - - if (oe_type(entry)) { - off_t delta_pos; - - /* - * This must be a delta and we already know what the - * final object type is. Let's extract the actual - * object size from the delta header. - */ - delta_pos = entry->in_pack_offset + entry->in_pack_header_size; - canonical_size = get_size_from_delta(p, &w_curs, delta_pos); - if (canonical_size == 0) - goto give_up; - SET_SIZE(entry, canonical_size); - unuse_pack(&w_curs); - return; - } - - /* - * No choice but to fall back to the recursive delta walk - * with oid_object_info() to find about the object type - * at this point... - */ - give_up: - unuse_pack(&w_curs); - } - - if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi, - OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0) { - if (has_promisor_remote()) { - prefetch_to_pack(object_index); - if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi, - OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0) - type = -1; - } else { - type = -1; - } - } - oe_set_type(entry, type); - if (entry->type_valid) { - SET_SIZE(entry, canonical_size); - } else { - /* - * Bad object type is checked in prepare_pack(). This is - * to permit a missing preferred base object to be ignored - * as a preferred base. Doing so can result in a larger - * pack file, but the transfer will still take place. - */ - } -} - -static int pack_offset_sort(const void *_a, const void *_b) -{ - const struct object_entry *a = *(struct object_entry **)_a; - const struct object_entry *b = *(struct object_entry **)_b; - const struct packed_git *a_in_pack = IN_PACK(a); - const struct packed_git *b_in_pack = IN_PACK(b); - - /* avoid filesystem trashing with loose objects */ - if (!a_in_pack && !b_in_pack) - return oidcmp(&a->idx.oid, &b->idx.oid); - - if (a_in_pack < b_in_pack) - return -1; - if (a_in_pack > b_in_pack) - return 1; - return a->in_pack_offset < b->in_pack_offset ? -1 : - (a->in_pack_offset > b->in_pack_offset); -} - -/* - * Drop an on-disk delta we were planning to reuse. Naively, this would - * just involve blanking out the "delta" field, but we have to deal - * with some extra book-keeping: - * - * 1. Removing ourselves from the delta_sibling linked list. - * - * 2. Updating our size/type to the non-delta representation. These were - * either not recorded initially (size) or overwritten with the delta type - * (type) when check_object() decided to reuse the delta. - * - * 3. Resetting our delta depth, as we are now a base object. - */ -static void drop_reused_delta(struct object_entry *entry) -{ - unsigned *idx = &to_pack.objects[entry->delta_idx - 1].delta_child_idx; - struct object_info oi = OBJECT_INFO_INIT; - enum object_type type; - unsigned long size; - - while (*idx) { - struct object_entry *oe = &to_pack.objects[*idx - 1]; - - if (oe == entry) - *idx = oe->delta_sibling_idx; - else - idx = &oe->delta_sibling_idx; - } - SET_DELTA(entry, NULL); - entry->depth = 0; - - oi.sizep = &size; - oi.typep = &type; - if (packed_object_info(the_repository, IN_PACK(entry), entry->in_pack_offset, &oi) < 0) { - /* - * We failed to get the info from this pack for some reason; - * fall back to oid_object_info, which may find another copy. - * And if that fails, the error will be recorded in oe_type(entry) - * and dealt with in prepare_pack(). - */ - oe_set_type(entry, - oid_object_info(the_repository, &entry->idx.oid, &size)); - } else { - oe_set_type(entry, type); - } - SET_SIZE(entry, size); -} - -/* - * Follow the chain of deltas from this entry onward, throwing away any links - * that cause us to hit a cycle (as determined by the DFS state flags in - * the entries). - * - * We also detect too-long reused chains that would violate our --depth - * limit. - */ -static void break_delta_chains(struct object_entry *entry) -{ - /* - * The actual depth of each object we will write is stored as an int, - * as it cannot exceed our int "depth" limit. But before we break - * changes based no that limit, we may potentially go as deep as the - * number of objects, which is elsewhere bounded to a uint32_t. - */ - uint32_t total_depth; - struct object_entry *cur, *next; - - for (cur = entry, total_depth = 0; - cur; - cur = DELTA(cur), total_depth++) { - if (cur->dfs_state == DFS_DONE) { - /* - * We've already seen this object and know it isn't - * part of a cycle. We do need to append its depth - * to our count. - */ - total_depth += cur->depth; - break; - } - - /* - * We break cycles before looping, so an ACTIVE state (or any - * other cruft which made its way into the state variable) - * is a bug. - */ - if (cur->dfs_state != DFS_NONE) - BUG("confusing delta dfs state in first pass: %d", - cur->dfs_state); - - /* - * Now we know this is the first time we've seen the object. If - * it's not a delta, we're done traversing, but we'll mark it - * done to save time on future traversals. - */ - if (!DELTA(cur)) { - cur->dfs_state = DFS_DONE; - break; - } - - /* - * Mark ourselves as active and see if the next step causes - * us to cycle to another active object. It's important to do - * this _before_ we loop, because it impacts where we make the - * cut, and thus how our total_depth counter works. - * E.g., We may see a partial loop like: - * - * A -> B -> C -> D -> B - * - * Cutting B->C breaks the cycle. But now the depth of A is - * only 1, and our total_depth counter is at 3. The size of the - * error is always one less than the size of the cycle we - * broke. Commits C and D were "lost" from A's chain. - * - * If we instead cut D->B, then the depth of A is correct at 3. - * We keep all commits in the chain that we examined. - */ - cur->dfs_state = DFS_ACTIVE; - if (DELTA(cur)->dfs_state == DFS_ACTIVE) { - drop_reused_delta(cur); - cur->dfs_state = DFS_DONE; - break; - } - } - - /* - * And now that we've gone all the way to the bottom of the chain, we - * need to clear the active flags and set the depth fields as - * appropriate. Unlike the loop above, which can quit when it drops a - * delta, we need to keep going to look for more depth cuts. So we need - * an extra "next" pointer to keep going after we reset cur->delta. - */ - for (cur = entry; cur; cur = next) { - next = DELTA(cur); - - /* - * We should have a chain of zero or more ACTIVE states down to - * a final DONE. We can quit after the DONE, because either it - * has no bases, or we've already handled them in a previous - * call. - */ - if (cur->dfs_state == DFS_DONE) - break; - else if (cur->dfs_state != DFS_ACTIVE) - BUG("confusing delta dfs state in second pass: %d", - cur->dfs_state); - - /* - * If the total_depth is more than depth, then we need to snip - * the chain into two or more smaller chains that don't exceed - * the maximum depth. Most of the resulting chains will contain - * (depth + 1) entries (i.e., depth deltas plus one base), and - * the last chain (i.e., the one containing entry) will contain - * whatever entries are left over, namely - * (total_depth % (depth + 1)) of them. - * - * Since we are iterating towards decreasing depth, we need to - * decrement total_depth as we go, and we need to write to the - * entry what its final depth will be after all of the - * snipping. Since we're snipping into chains of length (depth - * + 1) entries, the final depth of an entry will be its - * original depth modulo (depth + 1). Any time we encounter an - * entry whose final depth is supposed to be zero, we snip it - * from its delta base, thereby making it so. - */ - cur->depth = (total_depth--) % (depth + 1); - if (!cur->depth) - drop_reused_delta(cur); - - cur->dfs_state = DFS_DONE; - } -} - -static void get_object_details(void) -{ - uint32_t i; - struct object_entry **sorted_by_offset; - - if (progress) - progress_state = start_progress(_("Counting objects"), - to_pack.nr_objects); - - sorted_by_offset = xcalloc(to_pack.nr_objects, sizeof(struct object_entry *)); - for (i = 0; i < to_pack.nr_objects; i++) - sorted_by_offset[i] = to_pack.objects + i; - QSORT(sorted_by_offset, to_pack.nr_objects, pack_offset_sort); - - for (i = 0; i < to_pack.nr_objects; i++) { - struct object_entry *entry = sorted_by_offset[i]; - check_object(entry, i); - if (entry->type_valid && - oe_size_greater_than(&to_pack, entry, big_file_threshold)) - entry->no_try_delta = 1; - display_progress(progress_state, i + 1); - } - stop_progress(&progress_state); - - /* - * This must happen in a second pass, since we rely on the delta - * information for the whole list being completed. - */ - for (i = 0; i < to_pack.nr_objects; i++) - break_delta_chains(&to_pack.objects[i]); - - free(sorted_by_offset); -} - -/* - * We search for deltas in a list sorted by type, by filename hash, and then - * by size, so that we see progressively smaller and smaller files. - * That's because we prefer deltas to be from the bigger file - * to the smaller -- deletes are potentially cheaper, but perhaps - * more importantly, the bigger file is likely the more recent - * one. The deepest deltas are therefore the oldest objects which are - * less susceptible to be accessed often. - */ -static int type_size_sort(const void *_a, const void *_b) -{ - const struct object_entry *a = *(struct object_entry **)_a; - const struct object_entry *b = *(struct object_entry **)_b; - const enum object_type a_type = oe_type(a); - const enum object_type b_type = oe_type(b); - const unsigned long a_size = SIZE(a); - const unsigned long b_size = SIZE(b); - - if (a_type > b_type) - return -1; - if (a_type < b_type) - return 1; - if (a->hash > b->hash) - return -1; - if (a->hash < b->hash) - return 1; - if (a->preferred_base > b->preferred_base) - return -1; - if (a->preferred_base < b->preferred_base) - return 1; - if (use_delta_islands) { - const int island_cmp = island_delta_cmp(&a->idx.oid, &b->idx.oid); - if (island_cmp) - return island_cmp; - } - if (a_size > b_size) - return -1; - if (a_size < b_size) - return 1; - return a < b ? -1 : (a > b); /* newest first */ -} - -struct unpacked { - struct object_entry *entry; - void *data; - struct delta_index *index; - unsigned depth; -}; - -static int delta_cacheable(unsigned long src_size, unsigned long trg_size, - unsigned long delta_size) -{ - if (max_delta_cache_size && delta_cache_size + delta_size > max_delta_cache_size) - return 0; - - if (delta_size < cache_max_small_delta_size) - return 1; - - /* cache delta, if objects are large enough compared to delta size */ - if ((src_size >> 20) + (trg_size >> 21) > (delta_size >> 10)) - return 1; - - return 0; -} - -/* Protect delta_cache_size */ -static pthread_mutex_t cache_mutex; -#define cache_lock() pthread_mutex_lock(&cache_mutex) -#define cache_unlock() pthread_mutex_unlock(&cache_mutex) - -/* - * Protect object list partitioning (e.g. struct thread_param) and - * progress_state - */ -static pthread_mutex_t progress_mutex; -#define progress_lock() pthread_mutex_lock(&progress_mutex) -#define progress_unlock() pthread_mutex_unlock(&progress_mutex) - -/* - * Access to struct object_entry is unprotected since each thread owns - * a portion of the main object list. Just don't access object entries - * ahead in the list because they can be stolen and would need - * progress_mutex for protection. - */ - -/* - * Return the size of the object without doing any delta - * reconstruction (so non-deltas are true object sizes, but deltas - * return the size of the delta data). - */ -unsigned long oe_get_size_slow(struct packing_data *pack, - const struct object_entry *e) -{ - struct packed_git *p; - struct pack_window *w_curs; - unsigned char *buf; - enum object_type type; - unsigned long used, avail, size; - - if (e->type_ != OBJ_OFS_DELTA && e->type_ != OBJ_REF_DELTA) { - packing_data_lock(&to_pack); - if (oid_object_info(the_repository, &e->idx.oid, &size) < 0) - die(_("unable to get size of %s"), - oid_to_hex(&e->idx.oid)); - packing_data_unlock(&to_pack); - return size; - } - - p = oe_in_pack(pack, e); - if (!p) - BUG("when e->type is a delta, it must belong to a pack"); - - packing_data_lock(&to_pack); - w_curs = NULL; - buf = use_pack(p, &w_curs, e->in_pack_offset, &avail); - used = unpack_object_header_buffer(buf, avail, &type, &size); - if (used == 0) - die(_("unable to parse object header of %s"), - oid_to_hex(&e->idx.oid)); - - unuse_pack(&w_curs); - packing_data_unlock(&to_pack); - return size; -} - -static int try_delta(struct unpacked *trg, struct unpacked *src, - unsigned max_depth, unsigned long *mem_usage) -{ - struct object_entry *trg_entry = trg->entry; - struct object_entry *src_entry = src->entry; - unsigned long trg_size, src_size, delta_size, sizediff, max_size, sz; - unsigned ref_depth; - enum object_type type; - void *delta_buf; - - /* Don't bother doing diffs between different types */ - if (oe_type(trg_entry) != oe_type(src_entry)) - return -1; - - /* - * We do not bother to try a delta that we discarded on an - * earlier try, but only when reusing delta data. Note that - * src_entry that is marked as the preferred_base should always - * be considered, as even if we produce a suboptimal delta against - * it, we will still save the transfer cost, as we already know - * the other side has it and we won't send src_entry at all. - */ - if (reuse_delta && IN_PACK(trg_entry) && - IN_PACK(trg_entry) == IN_PACK(src_entry) && - !src_entry->preferred_base && - trg_entry->in_pack_type != OBJ_REF_DELTA && - trg_entry->in_pack_type != OBJ_OFS_DELTA) - return 0; - - /* Let's not bust the allowed depth. */ - if (src->depth >= max_depth) - return 0; - - /* Now some size filtering heuristics. */ - trg_size = SIZE(trg_entry); - if (!DELTA(trg_entry)) { - max_size = trg_size/2 - the_hash_algo->rawsz; - ref_depth = 1; - } else { - max_size = DELTA_SIZE(trg_entry); - ref_depth = trg->depth; - } - max_size = (uint64_t)max_size * (max_depth - src->depth) / - (max_depth - ref_depth + 1); - if (max_size == 0) - return 0; - src_size = SIZE(src_entry); - sizediff = src_size < trg_size ? trg_size - src_size : 0; - if (sizediff >= max_size) - return 0; - if (trg_size < src_size / 32) - return 0; - - if (!in_same_island(&trg->entry->idx.oid, &src->entry->idx.oid)) - return 0; - - /* Load data if not already done */ - if (!trg->data) { - packing_data_lock(&to_pack); - trg->data = read_object_file(&trg_entry->idx.oid, &type, &sz); - packing_data_unlock(&to_pack); - if (!trg->data) - die(_("object %s cannot be read"), - oid_to_hex(&trg_entry->idx.oid)); - if (sz != trg_size) - die(_("object %s inconsistent object length (%"PRIuMAX" vs %"PRIuMAX")"), - oid_to_hex(&trg_entry->idx.oid), (uintmax_t)sz, - (uintmax_t)trg_size); - *mem_usage += sz; - } - if (!src->data) { - packing_data_lock(&to_pack); - src->data = read_object_file(&src_entry->idx.oid, &type, &sz); - packing_data_unlock(&to_pack); - if (!src->data) { - if (src_entry->preferred_base) { - static int warned = 0; - if (!warned++) - warning(_("object %s cannot be read"), - oid_to_hex(&src_entry->idx.oid)); - /* - * Those objects are not included in the - * resulting pack. Be resilient and ignore - * them if they can't be read, in case the - * pack could be created nevertheless. - */ - return 0; - } - die(_("object %s cannot be read"), - oid_to_hex(&src_entry->idx.oid)); - } - if (sz != src_size) - die(_("object %s inconsistent object length (%"PRIuMAX" vs %"PRIuMAX")"), - oid_to_hex(&src_entry->idx.oid), (uintmax_t)sz, - (uintmax_t)src_size); - *mem_usage += sz; - } - if (!src->index) { - src->index = create_delta_index(src->data, src_size); - if (!src->index) { - static int warned = 0; - if (!warned++) - warning(_("suboptimal pack - out of memory")); - return 0; - } - *mem_usage += sizeof_delta_index(src->index); - } - - delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size); - if (!delta_buf) - return 0; - - if (DELTA(trg_entry)) { - /* Prefer only shallower same-sized deltas. */ - if (delta_size == DELTA_SIZE(trg_entry) && - src->depth + 1 >= trg->depth) { - free(delta_buf); - return 0; - } - } - - /* - * Handle memory allocation outside of the cache - * accounting lock. Compiler will optimize the strangeness - * away when NO_PTHREADS is defined. - */ - free(trg_entry->delta_data); - cache_lock(); - if (trg_entry->delta_data) { - delta_cache_size -= DELTA_SIZE(trg_entry); - trg_entry->delta_data = NULL; - } - if (delta_cacheable(src_size, trg_size, delta_size)) { - delta_cache_size += delta_size; - cache_unlock(); - trg_entry->delta_data = xrealloc(delta_buf, delta_size); - } else { - cache_unlock(); - free(delta_buf); - } - - SET_DELTA(trg_entry, src_entry); - SET_DELTA_SIZE(trg_entry, delta_size); - trg->depth = src->depth + 1; - - return 1; -} - -static unsigned int check_delta_limit(struct object_entry *me, unsigned int n) -{ - struct object_entry *child = DELTA_CHILD(me); - unsigned int m = n; - while (child) { - const unsigned int c = check_delta_limit(child, n + 1); - if (m < c) - m = c; - child = DELTA_SIBLING(child); - } - return m; -} - -static unsigned long free_unpacked(struct unpacked *n) -{ - unsigned long freed_mem = sizeof_delta_index(n->index); - free_delta_index(n->index); - n->index = NULL; - if (n->data) { - freed_mem += SIZE(n->entry); - FREE_AND_NULL(n->data); - } - n->entry = NULL; - n->depth = 0; - return freed_mem; -} - -static void find_deltas(struct object_entry **list, unsigned *list_size, - int window, int depth, unsigned *processed) -{ - uint32_t i, idx = 0, count = 0; - struct unpacked *array; - unsigned long mem_usage = 0; - - array = xcalloc(window, sizeof(struct unpacked)); - - for (;;) { - struct object_entry *entry; - struct unpacked *n = array + idx; - int j, max_depth, best_base = -1; - - progress_lock(); - if (!*list_size) { - progress_unlock(); - break; - } - entry = *list++; - (*list_size)--; - if (!entry->preferred_base) { - (*processed)++; - display_progress(progress_state, *processed); - } - progress_unlock(); - - mem_usage -= free_unpacked(n); - n->entry = entry; - - while (window_memory_limit && - mem_usage > window_memory_limit && - count > 1) { - const uint32_t tail = (idx + window - count) % window; - mem_usage -= free_unpacked(array + tail); - count--; - } - - /* We do not compute delta to *create* objects we are not - * going to pack. - */ - if (entry->preferred_base) - goto next; - - /* - * If the current object is at pack edge, take the depth the - * objects that depend on the current object into account - * otherwise they would become too deep. - */ - max_depth = depth; - if (DELTA_CHILD(entry)) { - max_depth -= check_delta_limit(entry, 0); - if (max_depth <= 0) - goto next; - } - - j = window; - while (--j > 0) { - int ret; - uint32_t other_idx = idx + j; - struct unpacked *m; - if (other_idx >= window) - other_idx -= window; - m = array + other_idx; - if (!m->entry) - break; - ret = try_delta(n, m, max_depth, &mem_usage); - if (ret < 0) - break; - else if (ret > 0) - best_base = other_idx; - } - - /* - * If we decided to cache the delta data, then it is best - * to compress it right away. First because we have to do - * it anyway, and doing it here while we're threaded will - * save a lot of time in the non threaded write phase, - * as well as allow for caching more deltas within - * the same cache size limit. - * ... - * But only if not writing to stdout, since in that case - * the network is most likely throttling writes anyway, - * and therefore it is best to go to the write phase ASAP - * instead, as we can afford spending more time compressing - * between writes at that moment. - */ - if (entry->delta_data && !pack_to_stdout) { - unsigned long size; - - size = do_compress(&entry->delta_data, DELTA_SIZE(entry)); - if (size < (1U << OE_Z_DELTA_BITS)) { - entry->z_delta_size = size; - cache_lock(); - delta_cache_size -= DELTA_SIZE(entry); - delta_cache_size += entry->z_delta_size; - cache_unlock(); - } else { - FREE_AND_NULL(entry->delta_data); - entry->z_delta_size = 0; - } - } - - /* if we made n a delta, and if n is already at max - * depth, leaving it in the window is pointless. we - * should evict it first. - */ - if (DELTA(entry) && max_depth <= n->depth) - continue; - - /* - * Move the best delta base up in the window, after the - * currently deltified object, to keep it longer. It will - * be the first base object to be attempted next. - */ - if (DELTA(entry)) { - struct unpacked swap = array[best_base]; - int dist = (window + idx - best_base) % window; - int dst = best_base; - while (dist--) { - int src = (dst + 1) % window; - array[dst] = array[src]; - dst = src; - } - array[dst] = swap; - } - - next: - idx++; - if (count + 1 < window) - count++; - if (idx >= window) - idx = 0; - } - - for (i = 0; i < window; ++i) { - free_delta_index(array[i].index); - free(array[i].data); - } - free(array); -} - -/* - * The main object list is split into smaller lists, each is handed to - * one worker. - * - * The main thread waits on the condition that (at least) one of the workers - * has stopped working (which is indicated in the .working member of - * struct thread_params). - * - * When a work thread has completed its work, it sets .working to 0 and - * signals the main thread and waits on the condition that .data_ready - * becomes 1. - * - * The main thread steals half of the work from the worker that has - * most work left to hand it to the idle worker. - */ - -struct thread_params { - pthread_t thread; - struct object_entry **list; - unsigned list_size; - unsigned remaining; - int window; - int depth; - int working; - int data_ready; - pthread_mutex_t mutex; - pthread_cond_t cond; - unsigned *processed; -}; - -static pthread_cond_t progress_cond; - -/* - * Mutex and conditional variable can't be statically-initialized on Windows. - */ -static void init_threaded_search(void) -{ - pthread_mutex_init(&cache_mutex, NULL); - pthread_mutex_init(&progress_mutex, NULL); - pthread_cond_init(&progress_cond, NULL); -} - -static void cleanup_threaded_search(void) -{ - pthread_cond_destroy(&progress_cond); - pthread_mutex_destroy(&cache_mutex); - pthread_mutex_destroy(&progress_mutex); -} - -static void *threaded_find_deltas(void *arg) -{ - struct thread_params *me = arg; - - progress_lock(); - while (me->remaining) { - progress_unlock(); - - find_deltas(me->list, &me->remaining, - me->window, me->depth, me->processed); - - progress_lock(); - me->working = 0; - pthread_cond_signal(&progress_cond); - progress_unlock(); - - /* - * We must not set ->data_ready before we wait on the - * condition because the main thread may have set it to 1 - * before we get here. In order to be sure that new - * work is available if we see 1 in ->data_ready, it - * was initialized to 0 before this thread was spawned - * and we reset it to 0 right away. - */ - pthread_mutex_lock(&me->mutex); - while (!me->data_ready) - pthread_cond_wait(&me->cond, &me->mutex); - me->data_ready = 0; - pthread_mutex_unlock(&me->mutex); - - progress_lock(); - } - progress_unlock(); - /* leave ->working 1 so that this doesn't get more work assigned */ - return NULL; -} - -static void ll_find_deltas(struct object_entry **list, unsigned list_size, - int window, int depth, unsigned *processed) -{ - struct thread_params *p; - int i, ret, active_threads = 0; - - init_threaded_search(); - - if (delta_search_threads <= 1) { - find_deltas(list, &list_size, window, depth, processed); - cleanup_threaded_search(); - return; - } - if (progress > pack_to_stdout) - fprintf_ln(stderr, _("Delta compression using up to %d threads"), - delta_search_threads); - p = xcalloc(delta_search_threads, sizeof(*p)); - - /* Partition the work amongst work threads. */ - for (i = 0; i < delta_search_threads; i++) { - unsigned sub_size = list_size / (delta_search_threads - i); - - /* don't use too small segments or no deltas will be found */ - if (sub_size < 2*window && i+1 < delta_search_threads) - sub_size = 0; - - p[i].window = window; - p[i].depth = depth; - p[i].processed = processed; - p[i].working = 1; - p[i].data_ready = 0; - - /* try to split chunks on "path" boundaries */ - while (sub_size && sub_size < list_size && - list[sub_size]->hash && - list[sub_size]->hash == list[sub_size-1]->hash) - sub_size++; - - p[i].list = list; - p[i].list_size = sub_size; - p[i].remaining = sub_size; - - list += sub_size; - list_size -= sub_size; - } - - /* Start work threads. */ - for (i = 0; i < delta_search_threads; i++) { - if (!p[i].list_size) - continue; - pthread_mutex_init(&p[i].mutex, NULL); - pthread_cond_init(&p[i].cond, NULL); - ret = pthread_create(&p[i].thread, NULL, - threaded_find_deltas, &p[i]); - if (ret) - die(_("unable to create thread: %s"), strerror(ret)); - active_threads++; - } - - /* - * Now let's wait for work completion. Each time a thread is done - * with its work, we steal half of the remaining work from the - * thread with the largest number of unprocessed objects and give - * it to that newly idle thread. This ensure good load balancing - * until the remaining object list segments are simply too short - * to be worth splitting anymore. - */ - while (active_threads) { - struct thread_params *target = NULL; - struct thread_params *victim = NULL; - unsigned sub_size = 0; - - progress_lock(); - for (;;) { - for (i = 0; !target && i < delta_search_threads; i++) - if (!p[i].working) - target = &p[i]; - if (target) - break; - pthread_cond_wait(&progress_cond, &progress_mutex); - } - - for (i = 0; i < delta_search_threads; i++) - if (p[i].remaining > 2*window && - (!victim || victim->remaining < p[i].remaining)) - victim = &p[i]; - if (victim) { - sub_size = victim->remaining / 2; - list = victim->list + victim->list_size - sub_size; - while (sub_size && list[0]->hash && - list[0]->hash == list[-1]->hash) { - list++; - sub_size--; - } - if (!sub_size) { - /* - * It is possible for some "paths" to have - * so many objects that no hash boundary - * might be found. Let's just steal the - * exact half in that case. - */ - sub_size = victim->remaining / 2; - list -= sub_size; - } - target->list = list; - victim->list_size -= sub_size; - victim->remaining -= sub_size; - } - target->list_size = sub_size; - target->remaining = sub_size; - target->working = 1; - progress_unlock(); - - pthread_mutex_lock(&target->mutex); - target->data_ready = 1; - pthread_cond_signal(&target->cond); - pthread_mutex_unlock(&target->mutex); - - if (!sub_size) { - pthread_join(target->thread, NULL); - pthread_cond_destroy(&target->cond); - pthread_mutex_destroy(&target->mutex); - active_threads--; - } - } - cleanup_threaded_search(); - free(p); -} - -static int obj_is_packed(const struct object_id *oid) -{ - return packlist_find(&to_pack, oid) || - (reuse_packfile_bitmap && - bitmap_walk_contains(bitmap_git, reuse_packfile_bitmap, oid)); -} - -static void add_tag_chain(const struct object_id *oid) -{ - struct tag *tag; - - /* - * We catch duplicates already in add_object_entry(), but we'd - * prefer to do this extra check to avoid having to parse the - * tag at all if we already know that it's being packed (e.g., if - * it was included via bitmaps, we would not have parsed it - * previously). - */ - if (obj_is_packed(oid)) - return; - - tag = lookup_tag(the_repository, oid); - while (1) { - if (!tag || parse_tag(tag) || !tag->tagged) - die(_("unable to pack objects reachable from tag %s"), - oid_to_hex(oid)); - - add_object_entry(&tag->object.oid, OBJ_TAG, NULL, 0); - - if (tag->tagged->type != OBJ_TAG) - return; - - tag = (struct tag *)tag->tagged; - } -} - -static int add_ref_tag(const char *path, const struct object_id *oid, int flag, void *cb_data) -{ - struct object_id peeled; - - if (starts_with(path, "refs/tags/") && /* is a tag? */ - !peel_ref(path, &peeled) && /* peelable? */ - obj_is_packed(&peeled)) /* object packed? */ - add_tag_chain(oid); - return 0; -} - -static void prepare_pack(int window, int depth) -{ - struct object_entry **delta_list; - uint32_t i, nr_deltas; - unsigned n; - - if (use_delta_islands) - resolve_tree_islands(the_repository, progress, &to_pack); - - get_object_details(); - - /* - * If we're locally repacking then we need to be doubly careful - * from now on in order to make sure no stealth corruption gets - * propagated to the new pack. Clients receiving streamed packs - * should validate everything they get anyway so no need to incur - * the additional cost here in that case. - */ - if (!pack_to_stdout) - do_check_packed_object_crc = 1; - - if (!to_pack.nr_objects || !window || !depth) - return; - - ALLOC_ARRAY(delta_list, to_pack.nr_objects); - nr_deltas = n = 0; - - for (i = 0; i < to_pack.nr_objects; i++) { - struct object_entry *entry = to_pack.objects + i; - - if (DELTA(entry)) - /* This happens if we decided to reuse existing - * delta from a pack. "reuse_delta &&" is implied. - */ - continue; - - if (!entry->type_valid || - oe_size_less_than(&to_pack, entry, 50)) - continue; - - if (entry->no_try_delta) - continue; - - if (!entry->preferred_base) { - nr_deltas++; - if (oe_type(entry) < 0) - die(_("unable to get type of object %s"), - oid_to_hex(&entry->idx.oid)); - } else { - if (oe_type(entry) < 0) { - /* - * This object is not found, but we - * don't have to include it anyway. - */ - continue; - } - } - - delta_list[n++] = entry; - } - - if (nr_deltas && n > 1) { - unsigned nr_done = 0; - - if (progress) - progress_state = start_progress(_("Compressing objects"), - nr_deltas); - QSORT(delta_list, n, type_size_sort); - ll_find_deltas(delta_list, n, window+1, depth, &nr_done); - stop_progress(&progress_state); - if (nr_done != nr_deltas) - die(_("inconsistency with delta count")); - } - free(delta_list); -} - -static int git_pack_config(const char *k, const char *v, void *cb) -{ - if (!strcmp(k, "pack.window")) { - window = git_config_int(k, v); - return 0; - } - if (!strcmp(k, "pack.windowmemory")) { - window_memory_limit = git_config_ulong(k, v); - return 0; - } - if (!strcmp(k, "pack.depth")) { - depth = git_config_int(k, v); - return 0; - } - if (!strcmp(k, "pack.deltacachesize")) { - max_delta_cache_size = git_config_int(k, v); - return 0; - } - if (!strcmp(k, "pack.deltacachelimit")) { - cache_max_small_delta_size = git_config_int(k, v); - return 0; - } - if (!strcmp(k, "pack.writebitmaphashcache")) { - if (git_config_bool(k, v)) - write_bitmap_options |= BITMAP_OPT_HASH_CACHE; - else - write_bitmap_options &= ~BITMAP_OPT_HASH_CACHE; - } - if (!strcmp(k, "pack.usebitmaps")) { - use_bitmap_index_default = git_config_bool(k, v); - return 0; - } - if (!strcmp(k, "pack.allowpackreuse")) { - allow_pack_reuse = git_config_bool(k, v); - return 0; - } - if (!strcmp(k, "pack.threads")) { - delta_search_threads = git_config_int(k, v); - if (delta_search_threads < 0) - die(_("invalid number of threads specified (%d)"), - delta_search_threads); - if (!HAVE_THREADS && delta_search_threads != 1) { - warning(_("no threads support, ignoring %s"), k); - delta_search_threads = 0; - } - return 0; - } - if (!strcmp(k, "pack.indexversion")) { - pack_idx_opts.version = git_config_int(k, v); - if (pack_idx_opts.version > 2) - die(_("bad pack.indexversion=%"PRIu32), - pack_idx_opts.version); - return 0; - } - if (!strcmp(k, "uploadpack.blobpackfileuri")) { - struct configured_exclusion *ex = xmalloc(sizeof(*ex)); - const char *oid_end, *pack_end; - /* - * Stores the pack hash. This is not a true object ID, but is - * of the same form. - */ - struct object_id pack_hash; - - if (parse_oid_hex(v, &ex->e.oid, &oid_end) || - *oid_end != ' ' || - parse_oid_hex(oid_end + 1, &pack_hash, &pack_end) || - *pack_end != ' ') - die(_("value of uploadpack.blobpackfileuri must be " - "of the form '<object-hash> <pack-hash> <uri>' (got '%s')"), v); - if (oidmap_get(&configured_exclusions, &ex->e.oid)) - die(_("object already configured in another " - "uploadpack.blobpackfileuri (got '%s')"), v); - ex->pack_hash_hex = xcalloc(1, pack_end - oid_end); - memcpy(ex->pack_hash_hex, oid_end + 1, pack_end - oid_end - 1); - ex->uri = xstrdup(pack_end + 1); - oidmap_put(&configured_exclusions, ex); - } - return git_default_config(k, v, cb); -} - -static void read_object_list_from_stdin(void) -{ - char line[GIT_MAX_HEXSZ + 1 + PATH_MAX + 2]; - struct object_id oid; - const char *p; - - for (;;) { - if (!fgets(line, sizeof(line), stdin)) { - if (feof(stdin)) - break; - if (!ferror(stdin)) - die("BUG: fgets returned NULL, not EOF, not error!"); - if (errno != EINTR) - die_errno("fgets"); - clearerr(stdin); - continue; - } - if (line[0] == '-') { - if (get_oid_hex(line+1, &oid)) - die(_("expected edge object ID, got garbage:\n %s"), - line); - add_preferred_base(&oid); - continue; - } - if (parse_oid_hex(line, &oid, &p)) - die(_("expected object ID, got garbage:\n %s"), line); - - add_preferred_base_object(p + 1); - add_object_entry(&oid, OBJ_NONE, p + 1, 0); - } -} - -/* Remember to update object flag allocation in object.h */ -#define OBJECT_ADDED (1u<<20) - -static void show_commit(struct commit *commit, void *data) -{ - add_object_entry(&commit->object.oid, OBJ_COMMIT, NULL, 0); - commit->object.flags |= OBJECT_ADDED; - - if (write_bitmap_index) - index_commit_for_bitmap(commit); - - if (use_delta_islands) - propagate_island_marks(commit); -} - -static void show_object(struct object *obj, const char *name, void *data) -{ - add_preferred_base_object(name); - add_object_entry(&obj->oid, obj->type, name, 0); - obj->flags |= OBJECT_ADDED; - - if (use_delta_islands) { - const char *p; - unsigned depth; - struct object_entry *ent; - - /* the empty string is a root tree, which is depth 0 */ - depth = *name ? 1 : 0; - for (p = strchr(name, '/'); p; p = strchr(p + 1, '/')) - depth++; - - ent = packlist_find(&to_pack, &obj->oid); - if (ent && depth > oe_tree_depth(&to_pack, ent)) - oe_set_tree_depth(&to_pack, ent, depth); - } -} - -static void show_object__ma_allow_any(struct object *obj, const char *name, void *data) -{ - assert(arg_missing_action == MA_ALLOW_ANY); - - /* - * Quietly ignore ALL missing objects. This avoids problems with - * staging them now and getting an odd error later. - */ - if (!has_object(the_repository, &obj->oid, 0)) - return; - - show_object(obj, name, data); -} - -static void show_object__ma_allow_promisor(struct object *obj, const char *name, void *data) -{ - assert(arg_missing_action == MA_ALLOW_PROMISOR); - - /* - * Quietly ignore EXPECTED missing objects. This avoids problems with - * staging them now and getting an odd error later. - */ - if (!has_object(the_repository, &obj->oid, 0) && is_promisor_object(&obj->oid)) - return; - - show_object(obj, name, data); -} - -static int option_parse_missing_action(const struct option *opt, - const char *arg, int unset) -{ - assert(arg); - assert(!unset); - - if (!strcmp(arg, "error")) { - arg_missing_action = MA_ERROR; - fn_show_object = show_object; - return 0; - } - - if (!strcmp(arg, "allow-any")) { - arg_missing_action = MA_ALLOW_ANY; - fetch_if_missing = 0; - fn_show_object = show_object__ma_allow_any; - return 0; - } - - if (!strcmp(arg, "allow-promisor")) { - arg_missing_action = MA_ALLOW_PROMISOR; - fetch_if_missing = 0; - fn_show_object = show_object__ma_allow_promisor; - return 0; - } - - die(_("invalid value for --missing")); - return 0; -} - -static void show_edge(struct commit *commit) -{ - add_preferred_base(&commit->object.oid); -} - -struct in_pack_object { - off_t offset; - struct object *object; -}; - -struct in_pack { - unsigned int alloc; - unsigned int nr; - struct in_pack_object *array; -}; - -static void mark_in_pack_object(struct object *object, struct packed_git *p, struct in_pack *in_pack) -{ - in_pack->array[in_pack->nr].offset = find_pack_entry_one(object->oid.hash, p); - in_pack->array[in_pack->nr].object = object; - in_pack->nr++; -} - -/* - * Compare the objects in the offset order, in order to emulate the - * "git rev-list --objects" output that produced the pack originally. - */ -static int ofscmp(const void *a_, const void *b_) -{ - struct in_pack_object *a = (struct in_pack_object *)a_; - struct in_pack_object *b = (struct in_pack_object *)b_; - - if (a->offset < b->offset) - return -1; - else if (a->offset > b->offset) - return 1; - else - return oidcmp(&a->object->oid, &b->object->oid); -} - -static void add_objects_in_unpacked_packs(void) -{ - struct packed_git *p; - struct in_pack in_pack; - uint32_t i; - - memset(&in_pack, 0, sizeof(in_pack)); - - for (p = get_all_packs(the_repository); p; p = p->next) { - struct object_id oid; - struct object *o; - - if (!p->pack_local || p->pack_keep || p->pack_keep_in_core) - continue; - if (open_pack_index(p)) - die(_("cannot open pack index")); - - ALLOC_GROW(in_pack.array, - in_pack.nr + p->num_objects, - in_pack.alloc); - - for (i = 0; i < p->num_objects; i++) { - nth_packed_object_id(&oid, p, i); - o = lookup_unknown_object(&oid); - if (!(o->flags & OBJECT_ADDED)) - mark_in_pack_object(o, p, &in_pack); - o->flags |= OBJECT_ADDED; - } - } - - if (in_pack.nr) { - QSORT(in_pack.array, in_pack.nr, ofscmp); - for (i = 0; i < in_pack.nr; i++) { - struct object *o = in_pack.array[i].object; - add_object_entry(&o->oid, o->type, "", 0); - } - } - free(in_pack.array); -} - -static int add_loose_object(const struct object_id *oid, const char *path, - void *data) -{ - enum object_type type = oid_object_info(the_repository, oid, NULL); - - if (type < 0) { - warning(_("loose object at %s could not be examined"), path); - return 0; - } - - add_object_entry(oid, type, "", 0); - return 0; -} - -/* - * We actually don't even have to worry about reachability here. - * add_object_entry will weed out duplicates, so we just add every - * loose object we find. - */ -static void add_unreachable_loose_objects(void) -{ - for_each_loose_file_in_objdir(get_object_directory(), - add_loose_object, - NULL, NULL, NULL); -} - -static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid) -{ - static struct packed_git *last_found = (void *)1; - struct packed_git *p; - - p = (last_found != (void *)1) ? last_found : - get_all_packs(the_repository); - - while (p) { - if ((!p->pack_local || p->pack_keep || - p->pack_keep_in_core) && - find_pack_entry_one(oid->hash, p)) { - last_found = p; - return 1; - } - if (p == last_found) - p = get_all_packs(the_repository); - else - p = p->next; - if (p == last_found) - p = p->next; - } - return 0; -} - -/* - * Store a list of sha1s that are should not be discarded - * because they are either written too recently, or are - * reachable from another object that was. - * - * This is filled by get_object_list. - */ -static struct oid_array recent_objects; - -static int loosened_object_can_be_discarded(const struct object_id *oid, - timestamp_t mtime) -{ - if (!unpack_unreachable_expiration) - return 0; - if (mtime > unpack_unreachable_expiration) - return 0; - if (oid_array_lookup(&recent_objects, oid) >= 0) - return 0; - return 1; -} - -static void loosen_unused_packed_objects(void) -{ - struct packed_git *p; - uint32_t i; - struct object_id oid; - - for (p = get_all_packs(the_repository); p; p = p->next) { - if (!p->pack_local || p->pack_keep || p->pack_keep_in_core) - continue; - - if (open_pack_index(p)) - die(_("cannot open pack index")); - - for (i = 0; i < p->num_objects; i++) { - nth_packed_object_id(&oid, p, i); - if (!packlist_find(&to_pack, &oid) && - !has_sha1_pack_kept_or_nonlocal(&oid) && - !loosened_object_can_be_discarded(&oid, p->mtime)) - if (force_object_loose(&oid, p->mtime)) - die(_("unable to force loose object")); - } - } -} - -/* - * This tracks any options which pack-reuse code expects to be on, or which a - * reader of the pack might not understand, and which would therefore prevent - * blind reuse of what we have on disk. - */ -static int pack_options_allow_reuse(void) -{ - return allow_pack_reuse && - pack_to_stdout && - !ignore_packed_keep_on_disk && - !ignore_packed_keep_in_core && - (!local || !have_non_local_packs) && - !incremental; -} - -static int get_object_list_from_bitmap(struct rev_info *revs) -{ - if (!(bitmap_git = prepare_bitmap_walk(revs, &filter_options))) - return -1; - - if (pack_options_allow_reuse() && - !reuse_partial_packfile_from_bitmap( - bitmap_git, - &reuse_packfile, - &reuse_packfile_objects, - &reuse_packfile_bitmap)) { - assert(reuse_packfile_objects); - nr_result += reuse_packfile_objects; - display_progress(progress_state, nr_result); - } - - traverse_bitmap_commit_list(bitmap_git, revs, - &add_object_entry_from_bitmap); - return 0; -} - -static void record_recent_object(struct object *obj, - const char *name, - void *data) -{ - oid_array_append(&recent_objects, &obj->oid); -} - -static void record_recent_commit(struct commit *commit, void *data) -{ - oid_array_append(&recent_objects, &commit->object.oid); -} - -static void get_object_list(int ac, const char **av) -{ - struct rev_info revs; - struct setup_revision_opt s_r_opt = { - .allow_exclude_promisor_objects = 1, - }; - char line[1000]; - int flags = 0; - int save_warning; - - repo_init_revisions(the_repository, &revs, NULL); - save_commit_buffer = 0; - setup_revisions(ac, av, &revs, &s_r_opt); - - /* make sure shallows are read */ - is_repository_shallow(the_repository); - - save_warning = warn_on_object_refname_ambiguity; - warn_on_object_refname_ambiguity = 0; - - while (fgets(line, sizeof(line), stdin) != NULL) { - int len = strlen(line); - if (len && line[len - 1] == '\n') - line[--len] = 0; - if (!len) - break; - if (*line == '-') { - if (!strcmp(line, "--not")) { - flags ^= UNINTERESTING; - write_bitmap_index = 0; - continue; - } - if (starts_with(line, "--shallow ")) { - struct object_id oid; - if (get_oid_hex(line + 10, &oid)) - die("not an object name '%s'", line + 10); - register_shallow(the_repository, &oid); - use_bitmap_index = 0; - continue; - } - die(_("not a rev '%s'"), line); - } - if (handle_revision_arg(line, &revs, flags, REVARG_CANNOT_BE_FILENAME)) - die(_("bad revision '%s'"), line); - } - - warn_on_object_refname_ambiguity = save_warning; - - if (use_bitmap_index && !get_object_list_from_bitmap(&revs)) - return; - - if (use_delta_islands) - load_delta_islands(the_repository, progress); - - if (prepare_revision_walk(&revs)) - die(_("revision walk setup failed")); - mark_edges_uninteresting(&revs, show_edge, sparse); - - if (!fn_show_object) - fn_show_object = show_object; - traverse_commit_list_filtered(&filter_options, &revs, - show_commit, fn_show_object, NULL, - NULL); - - if (unpack_unreachable_expiration) { - revs.ignore_missing_links = 1; - if (add_unseen_recent_objects_to_traversal(&revs, - unpack_unreachable_expiration)) - die(_("unable to add recent objects")); - if (prepare_revision_walk(&revs)) - die(_("revision walk setup failed")); - traverse_commit_list(&revs, record_recent_commit, - record_recent_object, NULL); - } - - if (keep_unreachable) - add_objects_in_unpacked_packs(); - if (pack_loose_unreachable) - add_unreachable_loose_objects(); - if (unpack_unreachable) - loosen_unused_packed_objects(); - - oid_array_clear(&recent_objects); -} - -static void add_extra_kept_packs(const struct string_list *names) -{ - struct packed_git *p; - - if (!names->nr) - return; - - for (p = get_all_packs(the_repository); p; p = p->next) { - const char *name = basename(p->pack_name); - int i; - - if (!p->pack_local) - continue; - - for (i = 0; i < names->nr; i++) - if (!fspathcmp(name, names->items[i].string)) - break; - - if (i < names->nr) { - p->pack_keep_in_core = 1; - ignore_packed_keep_in_core = 1; - continue; - } - } -} - -static int option_parse_index_version(const struct option *opt, - const char *arg, int unset) -{ - char *c; - const char *val = arg; - - BUG_ON_OPT_NEG(unset); - - pack_idx_opts.version = strtoul(val, &c, 10); - if (pack_idx_opts.version > 2) - die(_("unsupported index version %s"), val); - if (*c == ',' && c[1]) - pack_idx_opts.off32_limit = strtoul(c+1, &c, 0); - if (*c || pack_idx_opts.off32_limit & 0x80000000) - die(_("bad index version '%s'"), val); - return 0; -} - -static int option_parse_unpack_unreachable(const struct option *opt, - const char *arg, int unset) -{ - if (unset) { - unpack_unreachable = 0; - unpack_unreachable_expiration = 0; - } - else { - unpack_unreachable = 1; - if (arg) - unpack_unreachable_expiration = approxidate(arg); - } - return 0; -} - -int cmd_pack_objects(int argc, const char **argv, const char *prefix) -{ - int use_internal_rev_list = 0; - int shallow = 0; - int all_progress_implied = 0; - struct strvec rp = STRVEC_INIT; - int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0; - int rev_list_index = 0; - struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; - struct option pack_objects_options[] = { - OPT_SET_INT('q', "quiet", &progress, - N_("do not show progress meter"), 0), - OPT_SET_INT(0, "progress", &progress, - N_("show progress meter"), 1), - OPT_SET_INT(0, "all-progress", &progress, - N_("show progress meter during object writing phase"), 2), - OPT_BOOL(0, "all-progress-implied", - &all_progress_implied, - N_("similar to --all-progress when progress meter is shown")), - OPT_CALLBACK_F(0, "index-version", NULL, N_("<version>[,<offset>]"), - N_("write the pack index file in the specified idx format version"), - PARSE_OPT_NONEG, option_parse_index_version), - OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit, - N_("maximum size of each output pack file")), - OPT_BOOL(0, "local", &local, - N_("ignore borrowed objects from alternate object store")), - OPT_BOOL(0, "incremental", &incremental, - N_("ignore packed objects")), - OPT_INTEGER(0, "window", &window, - N_("limit pack window by objects")), - OPT_MAGNITUDE(0, "window-memory", &window_memory_limit, - N_("limit pack window by memory in addition to object limit")), - OPT_INTEGER(0, "depth", &depth, - N_("maximum length of delta chain allowed in the resulting pack")), - OPT_BOOL(0, "reuse-delta", &reuse_delta, - N_("reuse existing deltas")), - OPT_BOOL(0, "reuse-object", &reuse_object, - N_("reuse existing objects")), - OPT_BOOL(0, "delta-base-offset", &allow_ofs_delta, - N_("use OFS_DELTA objects")), - OPT_INTEGER(0, "threads", &delta_search_threads, - N_("use threads when searching for best delta matches")), - OPT_BOOL(0, "non-empty", &non_empty, - N_("do not create an empty pack output")), - OPT_BOOL(0, "revs", &use_internal_rev_list, - N_("read revision arguments from standard input")), - OPT_SET_INT_F(0, "unpacked", &rev_list_unpacked, - N_("limit the objects to those that are not yet packed"), - 1, PARSE_OPT_NONEG), - OPT_SET_INT_F(0, "all", &rev_list_all, - N_("include objects reachable from any reference"), - 1, PARSE_OPT_NONEG), - OPT_SET_INT_F(0, "reflog", &rev_list_reflog, - N_("include objects referred by reflog entries"), - 1, PARSE_OPT_NONEG), - OPT_SET_INT_F(0, "indexed-objects", &rev_list_index, - N_("include objects referred to by the index"), - 1, PARSE_OPT_NONEG), - OPT_BOOL(0, "stdout", &pack_to_stdout, - N_("output pack to stdout")), - OPT_BOOL(0, "include-tag", &include_tag, - N_("include tag objects that refer to objects to be packed")), - OPT_BOOL(0, "keep-unreachable", &keep_unreachable, - N_("keep unreachable objects")), - OPT_BOOL(0, "pack-loose-unreachable", &pack_loose_unreachable, - N_("pack loose unreachable objects")), - OPT_CALLBACK_F(0, "unpack-unreachable", NULL, N_("time"), - N_("unpack unreachable objects newer than <time>"), - PARSE_OPT_OPTARG, option_parse_unpack_unreachable), - OPT_BOOL(0, "sparse", &sparse, - N_("use the sparse reachability algorithm")), - OPT_BOOL(0, "thin", &thin, - N_("create thin packs")), - OPT_BOOL(0, "shallow", &shallow, - N_("create packs suitable for shallow fetches")), - OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep_on_disk, - N_("ignore packs that have companion .keep file")), - OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"), - N_("ignore this pack")), - OPT_INTEGER(0, "compression", &pack_compression_level, - N_("pack compression level")), - OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents, - N_("do not hide commits by grafts"), 0), - OPT_BOOL(0, "use-bitmap-index", &use_bitmap_index, - N_("use a bitmap index if available to speed up counting objects")), - OPT_SET_INT(0, "write-bitmap-index", &write_bitmap_index, - N_("write a bitmap index together with the pack index"), - WRITE_BITMAP_TRUE), - OPT_SET_INT_F(0, "write-bitmap-index-quiet", - &write_bitmap_index, - N_("write a bitmap index if possible"), - WRITE_BITMAP_QUIET, PARSE_OPT_HIDDEN), - OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), - OPT_CALLBACK_F(0, "missing", NULL, N_("action"), - N_("handling for missing objects"), PARSE_OPT_NONEG, - option_parse_missing_action), - OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects, - N_("do not pack objects in promisor packfiles")), - OPT_BOOL(0, "delta-islands", &use_delta_islands, - N_("respect islands during delta compression")), - OPT_STRING_LIST(0, "uri-protocol", &uri_protocols, - N_("protocol"), - N_("exclude any configured uploadpack.blobpackfileuri with this protocol")), - OPT_END(), - }; - - if (DFS_NUM_STATES > (1 << OE_DFS_STATE_BITS)) - BUG("too many dfs states, increase OE_DFS_STATE_BITS"); - - read_replace_refs = 0; - - sparse = git_env_bool("GIT_TEST_PACK_SPARSE", -1); - prepare_repo_settings(the_repository); - if (sparse < 0) - sparse = the_repository->settings.pack_use_sparse; - - reset_pack_idx_option(&pack_idx_opts); - git_config(git_pack_config, NULL); - - progress = isatty(2); - argc = parse_options(argc, argv, prefix, pack_objects_options, - pack_usage, 0); - - if (argc) { - base_name = argv[0]; - argc--; - } - if (pack_to_stdout != !base_name || argc) - usage_with_options(pack_usage, pack_objects_options); - - if (depth >= (1 << OE_DEPTH_BITS)) { - warning(_("delta chain depth %d is too deep, forcing %d"), - depth, (1 << OE_DEPTH_BITS) - 1); - depth = (1 << OE_DEPTH_BITS) - 1; - } - if (cache_max_small_delta_size >= (1U << OE_Z_DELTA_BITS)) { - warning(_("pack.deltaCacheLimit is too high, forcing %d"), - (1U << OE_Z_DELTA_BITS) - 1); - cache_max_small_delta_size = (1U << OE_Z_DELTA_BITS) - 1; - } - - strvec_push(&rp, "pack-objects"); - if (thin) { - use_internal_rev_list = 1; - strvec_push(&rp, shallow - ? "--objects-edge-aggressive" - : "--objects-edge"); - } else - strvec_push(&rp, "--objects"); - - if (rev_list_all) { - use_internal_rev_list = 1; - strvec_push(&rp, "--all"); - } - if (rev_list_reflog) { - use_internal_rev_list = 1; - strvec_push(&rp, "--reflog"); - } - if (rev_list_index) { - use_internal_rev_list = 1; - strvec_push(&rp, "--indexed-objects"); - } - if (rev_list_unpacked) { - use_internal_rev_list = 1; - strvec_push(&rp, "--unpacked"); - } - - if (exclude_promisor_objects) { - use_internal_rev_list = 1; - fetch_if_missing = 0; - strvec_push(&rp, "--exclude-promisor-objects"); - } - if (unpack_unreachable || keep_unreachable || pack_loose_unreachable) - use_internal_rev_list = 1; - - if (!reuse_object) - reuse_delta = 0; - if (pack_compression_level == -1) - pack_compression_level = Z_DEFAULT_COMPRESSION; - else if (pack_compression_level < 0 || pack_compression_level > Z_BEST_COMPRESSION) - die(_("bad pack compression level %d"), pack_compression_level); - - if (!delta_search_threads) /* --threads=0 means autodetect */ - delta_search_threads = online_cpus(); - - if (!HAVE_THREADS && delta_search_threads != 1) - warning(_("no threads support, ignoring --threads")); - if (!pack_to_stdout && !pack_size_limit) - pack_size_limit = pack_size_limit_cfg; - if (pack_to_stdout && pack_size_limit) - die(_("--max-pack-size cannot be used to build a pack for transfer")); - if (pack_size_limit && pack_size_limit < 1024*1024) { - warning(_("minimum pack size limit is 1 MiB")); - pack_size_limit = 1024*1024; - } - - if (!pack_to_stdout && thin) - die(_("--thin cannot be used to build an indexable pack")); - - if (keep_unreachable && unpack_unreachable) - die(_("--keep-unreachable and --unpack-unreachable are incompatible")); - if (!rev_list_all || !rev_list_reflog || !rev_list_index) - unpack_unreachable_expiration = 0; - - if (filter_options.choice) { - if (!pack_to_stdout) - die(_("cannot use --filter without --stdout")); - } - - /* - * "soft" reasons not to use bitmaps - for on-disk repack by default we want - * - * - to produce good pack (with bitmap index not-yet-packed objects are - * packed in suboptimal order). - * - * - to use more robust pack-generation codepath (avoiding possible - * bugs in bitmap code and possible bitmap index corruption). - */ - if (!pack_to_stdout) - use_bitmap_index_default = 0; - - if (use_bitmap_index < 0) - use_bitmap_index = use_bitmap_index_default; - - /* "hard" reasons not to use bitmaps; these just won't work at all */ - if (!use_internal_rev_list || (!pack_to_stdout && write_bitmap_index) || is_repository_shallow(the_repository)) - use_bitmap_index = 0; - - if (pack_to_stdout || !rev_list_all) - write_bitmap_index = 0; - - if (use_delta_islands) - strvec_push(&rp, "--topo-order"); - - if (progress && all_progress_implied) - progress = 2; - - add_extra_kept_packs(&keep_pack_list); - if (ignore_packed_keep_on_disk) { - struct packed_git *p; - for (p = get_all_packs(the_repository); p; p = p->next) - if (p->pack_local && p->pack_keep) - break; - if (!p) /* no keep-able packs found */ - ignore_packed_keep_on_disk = 0; - } - if (local) { - /* - * unlike ignore_packed_keep_on_disk above, we do not - * want to unset "local" based on looking at packs, as - * it also covers non-local objects - */ - struct packed_git *p; - for (p = get_all_packs(the_repository); p; p = p->next) { - if (!p->pack_local) { - have_non_local_packs = 1; - break; - } - } - } - - trace2_region_enter("pack-objects", "enumerate-objects", - the_repository); - prepare_packing_data(the_repository, &to_pack); - - if (progress) - progress_state = start_progress(_("Enumerating objects"), 0); - if (!use_internal_rev_list) - read_object_list_from_stdin(); - else { - get_object_list(rp.nr, rp.v); - strvec_clear(&rp); - } - cleanup_preferred_base(); - if (include_tag && nr_result) - for_each_ref(add_ref_tag, NULL); - stop_progress(&progress_state); - trace2_region_leave("pack-objects", "enumerate-objects", - the_repository); - - if (non_empty && !nr_result) - return 0; - if (nr_result) { - trace2_region_enter("pack-objects", "prepare-pack", - the_repository); - prepare_pack(window, depth); - trace2_region_leave("pack-objects", "prepare-pack", - the_repository); - } - - trace2_region_enter("pack-objects", "write-pack-file", the_repository); - write_excluded_by_configs(); - write_pack_file(); - trace2_region_leave("pack-objects", "write-pack-file", the_repository); - - if (progress) - fprintf_ln(stderr, - _("Total %"PRIu32" (delta %"PRIu32")," - " reused %"PRIu32" (delta %"PRIu32")," - " pack-reused %"PRIu32), - written, written_delta, reused, reused_delta, - reuse_packfile_objects); - return 0; -} diff --git a/third_party/git/builtin/pack-redundant.c b/third_party/git/builtin/pack-redundant.c deleted file mode 100644 index 178e3409b7f8..000000000000 --- a/third_party/git/builtin/pack-redundant.c +++ /dev/null @@ -1,653 +0,0 @@ -/* -* -* Copyright 2005, Lukas Sandstrom <lukass@etek.chalmers.se> -* -* This file is licensed under the GPL v2. -* -*/ - -#include "builtin.h" -#include "repository.h" -#include "packfile.h" -#include "object-store.h" - -#define BLKSIZE 512 - -static const char pack_redundant_usage[] = -"git pack-redundant [--verbose] [--alt-odb] (--all | <filename.pack>...)"; - -static int load_all_packs, verbose, alt_odb; - -struct llist_item { - struct llist_item *next; - const struct object_id *oid; -}; -static struct llist { - struct llist_item *front; - struct llist_item *back; - size_t size; -} *all_objects; /* all objects which must be present in local packfiles */ - -static struct pack_list { - struct pack_list *next; - struct packed_git *pack; - struct llist *unique_objects; - struct llist *remaining_objects; - size_t all_objects_size; -} *local_packs = NULL, *altodb_packs = NULL; - -static struct llist_item *free_nodes; - -static inline void llist_item_put(struct llist_item *item) -{ - item->next = free_nodes; - free_nodes = item; -} - -static inline struct llist_item *llist_item_get(void) -{ - struct llist_item *new_item; - if ( free_nodes ) { - new_item = free_nodes; - free_nodes = free_nodes->next; - } else { - int i = 1; - ALLOC_ARRAY(new_item, BLKSIZE); - for (; i < BLKSIZE; i++) - llist_item_put(&new_item[i]); - } - return new_item; -} - -static inline void llist_init(struct llist **list) -{ - *list = xmalloc(sizeof(struct llist)); - (*list)->front = (*list)->back = NULL; - (*list)->size = 0; -} - -static struct llist * llist_copy(struct llist *list) -{ - struct llist *ret; - struct llist_item *new_item, *old_item, *prev; - - llist_init(&ret); - - if ((ret->size = list->size) == 0) - return ret; - - new_item = ret->front = llist_item_get(); - new_item->oid = list->front->oid; - - old_item = list->front->next; - while (old_item) { - prev = new_item; - new_item = llist_item_get(); - prev->next = new_item; - new_item->oid = old_item->oid; - old_item = old_item->next; - } - new_item->next = NULL; - ret->back = new_item; - - return ret; -} - -static inline struct llist_item *llist_insert(struct llist *list, - struct llist_item *after, - const struct object_id *oid) -{ - struct llist_item *new_item = llist_item_get(); - new_item->oid = oid; - new_item->next = NULL; - - if (after != NULL) { - new_item->next = after->next; - after->next = new_item; - if (after == list->back) - list->back = new_item; - } else {/* insert in front */ - if (list->size == 0) - list->back = new_item; - else - new_item->next = list->front; - list->front = new_item; - } - list->size++; - return new_item; -} - -static inline struct llist_item *llist_insert_back(struct llist *list, - const struct object_id *oid) -{ - return llist_insert(list, list->back, oid); -} - -static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, - const struct object_id *oid, struct llist_item *hint) -{ - struct llist_item *prev = NULL, *l; - - l = (hint == NULL) ? list->front : hint; - while (l) { - int cmp = oidcmp(l->oid, oid); - if (cmp > 0) { /* we insert before this entry */ - return llist_insert(list, prev, oid); - } - if (!cmp) { /* already exists */ - return l; - } - prev = l; - l = l->next; - } - /* insert at the end */ - return llist_insert_back(list, oid); -} - -/* returns a pointer to an item in front of sha1 */ -static inline struct llist_item * llist_sorted_remove(struct llist *list, const struct object_id *oid, struct llist_item *hint) -{ - struct llist_item *prev, *l; - -redo_from_start: - l = (hint == NULL) ? list->front : hint; - prev = NULL; - while (l) { - const int cmp = oidcmp(l->oid, oid); - if (cmp > 0) /* not in list, since sorted */ - return prev; - if (!cmp) { /* found */ - if (prev == NULL) { - if (hint != NULL && hint != list->front) { - /* we don't know the previous element */ - hint = NULL; - goto redo_from_start; - } - list->front = l->next; - } else - prev->next = l->next; - if (l == list->back) - list->back = prev; - llist_item_put(l); - list->size--; - return prev; - } - prev = l; - l = l->next; - } - return prev; -} - -/* computes A\B */ -static void llist_sorted_difference_inplace(struct llist *A, - struct llist *B) -{ - struct llist_item *hint, *b; - - hint = NULL; - b = B->front; - - while (b) { - hint = llist_sorted_remove(A, b->oid, hint); - b = b->next; - } -} - -static inline struct pack_list * pack_list_insert(struct pack_list **pl, - struct pack_list *entry) -{ - struct pack_list *p = xmalloc(sizeof(struct pack_list)); - memcpy(p, entry, sizeof(struct pack_list)); - p->next = *pl; - *pl = p; - return p; -} - -static inline size_t pack_list_size(struct pack_list *pl) -{ - size_t ret = 0; - while (pl) { - ret++; - pl = pl->next; - } - return ret; -} - -static struct pack_list * pack_list_difference(const struct pack_list *A, - const struct pack_list *B) -{ - struct pack_list *ret; - const struct pack_list *pl; - - if (A == NULL) - return NULL; - - pl = B; - while (pl != NULL) { - if (A->pack == pl->pack) - return pack_list_difference(A->next, B); - pl = pl->next; - } - ret = xmalloc(sizeof(struct pack_list)); - memcpy(ret, A, sizeof(struct pack_list)); - ret->next = pack_list_difference(A->next, B); - return ret; -} - -static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) -{ - unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step; - const unsigned char *p1_base, *p2_base; - struct llist_item *p1_hint = NULL, *p2_hint = NULL; - const unsigned int hashsz = the_hash_algo->rawsz; - - if (!p1->unique_objects) - p1->unique_objects = llist_copy(p1->remaining_objects); - if (!p2->unique_objects) - p2->unique_objects = llist_copy(p2->remaining_objects); - - p1_base = p1->pack->index_data; - p2_base = p2->pack->index_data; - p1_base += 256 * 4 + ((p1->pack->index_version < 2) ? 4 : 8); - p2_base += 256 * 4 + ((p2->pack->index_version < 2) ? 4 : 8); - p1_step = hashsz + ((p1->pack->index_version < 2) ? 4 : 0); - p2_step = hashsz + ((p2->pack->index_version < 2) ? 4 : 0); - - while (p1_off < p1->pack->num_objects * p1_step && - p2_off < p2->pack->num_objects * p2_step) - { - const int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off); - /* cmp ~ p1 - p2 */ - if (cmp == 0) { - p1_hint = llist_sorted_remove(p1->unique_objects, - (const struct object_id *)(p1_base + p1_off), - p1_hint); - p2_hint = llist_sorted_remove(p2->unique_objects, - (const struct object_id *)(p1_base + p1_off), - p2_hint); - p1_off += p1_step; - p2_off += p2_step; - continue; - } - if (cmp < 0) { /* p1 has the object, p2 doesn't */ - p1_off += p1_step; - } else { /* p2 has the object, p1 doesn't */ - p2_off += p2_step; - } - } -} - -static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2) -{ - size_t ret = 0; - unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step; - const unsigned char *p1_base, *p2_base; - const unsigned int hashsz = the_hash_algo->rawsz; - - p1_base = p1->index_data; - p2_base = p2->index_data; - p1_base += 256 * 4 + ((p1->index_version < 2) ? 4 : 8); - p2_base += 256 * 4 + ((p2->index_version < 2) ? 4 : 8); - p1_step = hashsz + ((p1->index_version < 2) ? 4 : 0); - p2_step = hashsz + ((p2->index_version < 2) ? 4 : 0); - - while (p1_off < p1->num_objects * p1_step && - p2_off < p2->num_objects * p2_step) - { - int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off); - /* cmp ~ p1 - p2 */ - if (cmp == 0) { - ret++; - p1_off += p1_step; - p2_off += p2_step; - continue; - } - if (cmp < 0) { /* p1 has the object, p2 doesn't */ - p1_off += p1_step; - } else { /* p2 has the object, p1 doesn't */ - p2_off += p2_step; - } - } - return ret; -} - -/* another O(n^2) function ... */ -static size_t get_pack_redundancy(struct pack_list *pl) -{ - struct pack_list *subset; - size_t ret = 0; - - if (pl == NULL) - return 0; - - while ((subset = pl->next)) { - while (subset) { - ret += sizeof_union(pl->pack, subset->pack); - subset = subset->next; - } - pl = pl->next; - } - return ret; -} - -static inline off_t pack_set_bytecount(struct pack_list *pl) -{ - off_t ret = 0; - while (pl) { - ret += pl->pack->pack_size; - ret += pl->pack->index_size; - pl = pl->next; - } - return ret; -} - -static int cmp_remaining_objects(const void *a, const void *b) -{ - struct pack_list *pl_a = *((struct pack_list **)a); - struct pack_list *pl_b = *((struct pack_list **)b); - - if (pl_a->remaining_objects->size == pl_b->remaining_objects->size) { - /* have the same remaining_objects, big pack first */ - if (pl_a->all_objects_size == pl_b->all_objects_size) - return 0; - else if (pl_a->all_objects_size < pl_b->all_objects_size) - return 1; - else - return -1; - } else if (pl_a->remaining_objects->size < pl_b->remaining_objects->size) { - /* sort by remaining objects, more objects first */ - return 1; - } else { - return -1; - } -} - -/* Sort pack_list, greater size of remaining_objects first */ -static void sort_pack_list(struct pack_list **pl) -{ - struct pack_list **ary, *p; - int i; - size_t n = pack_list_size(*pl); - - if (n < 2) - return; - - /* prepare an array of packed_list for easier sorting */ - ary = xcalloc(n, sizeof(struct pack_list *)); - for (n = 0, p = *pl; p; p = p->next) - ary[n++] = p; - - QSORT(ary, n, cmp_remaining_objects); - - /* link them back again */ - for (i = 0; i < n - 1; i++) - ary[i]->next = ary[i + 1]; - ary[n - 1]->next = NULL; - *pl = ary[0]; - - free(ary); -} - - -static void minimize(struct pack_list **min) -{ - struct pack_list *pl, *unique = NULL, *non_unique = NULL; - struct llist *missing, *unique_pack_objects; - - pl = local_packs; - while (pl) { - if (pl->unique_objects->size) - pack_list_insert(&unique, pl); - else - pack_list_insert(&non_unique, pl); - pl = pl->next; - } - /* find out which objects are missing from the set of unique packs */ - missing = llist_copy(all_objects); - pl = unique; - while (pl) { - llist_sorted_difference_inplace(missing, pl->remaining_objects); - pl = pl->next; - } - - *min = unique; - - /* return if there are no objects missing from the unique set */ - if (missing->size == 0) { - free(missing); - return; - } - - unique_pack_objects = llist_copy(all_objects); - llist_sorted_difference_inplace(unique_pack_objects, missing); - - /* remove unique pack objects from the non_unique packs */ - pl = non_unique; - while (pl) { - llist_sorted_difference_inplace(pl->remaining_objects, unique_pack_objects); - pl = pl->next; - } - - while (non_unique) { - /* sort the non_unique packs, greater size of remaining_objects first */ - sort_pack_list(&non_unique); - if (non_unique->remaining_objects->size == 0) - break; - - pack_list_insert(min, non_unique); - - for (pl = non_unique->next; pl && pl->remaining_objects->size > 0; pl = pl->next) - llist_sorted_difference_inplace(pl->remaining_objects, non_unique->remaining_objects); - - non_unique = non_unique->next; - } -} - -static void load_all_objects(void) -{ - struct pack_list *pl = local_packs; - struct llist_item *hint, *l; - - llist_init(&all_objects); - - while (pl) { - hint = NULL; - l = pl->remaining_objects->front; - while (l) { - hint = llist_insert_sorted_unique(all_objects, - l->oid, hint); - l = l->next; - } - pl = pl->next; - } - /* remove objects present in remote packs */ - pl = altodb_packs; - while (pl) { - llist_sorted_difference_inplace(all_objects, pl->remaining_objects); - pl = pl->next; - } -} - -/* this scales like O(n^2) */ -static void cmp_local_packs(void) -{ - struct pack_list *subset, *pl = local_packs; - - while ((subset = pl)) { - while ((subset = subset->next)) - cmp_two_packs(pl, subset); - pl = pl->next; - } -} - -static void scan_alt_odb_packs(void) -{ - struct pack_list *local, *alt; - - alt = altodb_packs; - while (alt) { - local = local_packs; - while (local) { - llist_sorted_difference_inplace(local->remaining_objects, - alt->remaining_objects); - local = local->next; - } - alt = alt->next; - } -} - -static struct pack_list * add_pack(struct packed_git *p) -{ - struct pack_list l; - unsigned long off = 0, step; - const unsigned char *base; - - if (!p->pack_local && !(alt_odb || verbose)) - return NULL; - - l.pack = p; - llist_init(&l.remaining_objects); - - if (open_pack_index(p)) - return NULL; - - base = p->index_data; - base += 256 * 4 + ((p->index_version < 2) ? 4 : 8); - step = the_hash_algo->rawsz + ((p->index_version < 2) ? 4 : 0); - while (off < p->num_objects * step) { - llist_insert_back(l.remaining_objects, (const struct object_id *)(base + off)); - off += step; - } - l.all_objects_size = l.remaining_objects->size; - l.unique_objects = NULL; - if (p->pack_local) - return pack_list_insert(&local_packs, &l); - else - return pack_list_insert(&altodb_packs, &l); -} - -static struct pack_list * add_pack_file(const char *filename) -{ - struct packed_git *p = get_all_packs(the_repository); - - if (strlen(filename) < 40) - die("Bad pack filename: %s", filename); - - while (p) { - if (strstr(p->pack_name, filename)) - return add_pack(p); - p = p->next; - } - die("Filename %s not found in packed_git", filename); -} - -static void load_all(void) -{ - struct packed_git *p = get_all_packs(the_repository); - - while (p) { - add_pack(p); - p = p->next; - } -} - -int cmd_pack_redundant(int argc, const char **argv, const char *prefix) -{ - int i; - struct pack_list *min = NULL, *red, *pl; - struct llist *ignore; - struct object_id *oid; - char buf[GIT_MAX_HEXSZ + 2]; /* hex hash + \n + \0 */ - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage(pack_redundant_usage); - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "--all")) { - load_all_packs = 1; - continue; - } - if (!strcmp(arg, "--verbose")) { - verbose = 1; - continue; - } - if (!strcmp(arg, "--alt-odb")) { - alt_odb = 1; - continue; - } - if (*arg == '-') - usage(pack_redundant_usage); - else - break; - } - - if (load_all_packs) - load_all(); - else - while (*(argv + i) != NULL) - add_pack_file(*(argv + i++)); - - if (local_packs == NULL) - die("Zero packs found!"); - - load_all_objects(); - - if (alt_odb) - scan_alt_odb_packs(); - - /* ignore objects given on stdin */ - llist_init(&ignore); - if (!isatty(0)) { - while (fgets(buf, sizeof(buf), stdin)) { - oid = xmalloc(sizeof(*oid)); - if (get_oid_hex(buf, oid)) - die("Bad object ID on stdin: %s", buf); - llist_insert_sorted_unique(ignore, oid, NULL); - } - } - llist_sorted_difference_inplace(all_objects, ignore); - pl = local_packs; - while (pl) { - llist_sorted_difference_inplace(pl->remaining_objects, ignore); - pl = pl->next; - } - - cmp_local_packs(); - - minimize(&min); - - if (verbose) { - fprintf(stderr, "There are %lu packs available in alt-odbs.\n", - (unsigned long)pack_list_size(altodb_packs)); - fprintf(stderr, "The smallest (bytewise) set of packs is:\n"); - pl = min; - while (pl) { - fprintf(stderr, "\t%s\n", pl->pack->pack_name); - pl = pl->next; - } - fprintf(stderr, "containing %lu duplicate objects " - "with a total size of %lukb.\n", - (unsigned long)get_pack_redundancy(min), - (unsigned long)pack_set_bytecount(min)/1024); - fprintf(stderr, "A total of %lu unique objects were considered.\n", - (unsigned long)all_objects->size); - fprintf(stderr, "Redundant packs (with indexes):\n"); - } - pl = red = pack_list_difference(local_packs, min); - while (pl) { - printf("%s\n%s\n", - sha1_pack_index_name(pl->pack->hash), - pl->pack->pack_name); - pl = pl->next; - } - if (verbose) - fprintf(stderr, "%luMB of redundant packs in total.\n", - (unsigned long)pack_set_bytecount(red)/(1024*1024)); - - return 0; -} diff --git a/third_party/git/builtin/pack-refs.c b/third_party/git/builtin/pack-refs.c deleted file mode 100644 index cfbd5c36c764..000000000000 --- a/third_party/git/builtin/pack-refs.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "builtin.h" -#include "config.h" -#include "parse-options.h" -#include "refs.h" -#include "repository.h" - -static char const * const pack_refs_usage[] = { - N_("git pack-refs [<options>]"), - NULL -}; - -int cmd_pack_refs(int argc, const char **argv, const char *prefix) -{ - unsigned int flags = PACK_REFS_PRUNE; - struct option opts[] = { - OPT_BIT(0, "all", &flags, N_("pack everything"), PACK_REFS_ALL), - OPT_BIT(0, "prune", &flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE), - OPT_END(), - }; - git_config(git_default_config, NULL); - if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0)) - usage_with_options(pack_refs_usage, opts); - return refs_pack_refs(get_main_ref_store(the_repository), flags); -} diff --git a/third_party/git/builtin/patch-id.c b/third_party/git/builtin/patch-id.c deleted file mode 100644 index 822ffff51fbd..000000000000 --- a/third_party/git/builtin/patch-id.c +++ /dev/null @@ -1,180 +0,0 @@ -#include "cache.h" -#include "builtin.h" -#include "config.h" -#include "diff.h" - -static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result) -{ - if (patchlen) - printf("%s %s\n", oid_to_hex(result), oid_to_hex(id)); -} - -static int remove_space(char *line) -{ - char *src = line; - char *dst = line; - unsigned char c; - - while ((c = *src++) != '\0') { - if (!isspace(c)) - *dst++ = c; - } - return dst - line; -} - -static int scan_hunk_header(const char *p, int *p_before, int *p_after) -{ - static const char digits[] = "0123456789"; - const char *q, *r; - int n; - - q = p + 4; - n = strspn(q, digits); - if (q[n] == ',') { - q += n + 1; - n = strspn(q, digits); - } - if (n == 0 || q[n] != ' ' || q[n+1] != '+') - return 0; - - r = q + n + 2; - n = strspn(r, digits); - if (r[n] == ',') { - r += n + 1; - n = strspn(r, digits); - } - if (n == 0) - return 0; - - *p_before = atoi(q); - *p_after = atoi(r); - return 1; -} - -static int get_one_patchid(struct object_id *next_oid, struct object_id *result, - struct strbuf *line_buf, int stable) -{ - int patchlen = 0, found_next = 0; - int before = -1, after = -1; - git_hash_ctx ctx; - - the_hash_algo->init_fn(&ctx); - oidclr(result); - - while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) { - char *line = line_buf->buf; - const char *p = line; - int len; - - if (!skip_prefix(line, "diff-tree ", &p) && - !skip_prefix(line, "commit ", &p) && - !skip_prefix(line, "From ", &p) && - starts_with(line, "\\ ") && 12 < strlen(line)) - continue; - - if (!get_oid_hex(p, next_oid)) { - found_next = 1; - break; - } - - /* Ignore commit comments */ - if (!patchlen && !starts_with(line, "diff ")) - continue; - - /* Parsing diff header? */ - if (before == -1) { - if (starts_with(line, "index ")) - continue; - else if (starts_with(line, "--- ")) - before = after = 1; - else if (!isalpha(line[0])) - break; - } - - /* Looking for a valid hunk header? */ - if (before == 0 && after == 0) { - if (starts_with(line, "@@ -")) { - /* Parse next hunk, but ignore line numbers. */ - scan_hunk_header(line, &before, &after); - continue; - } - - /* Split at the end of the patch. */ - if (!starts_with(line, "diff ")) - break; - - /* Else we're parsing another header. */ - if (stable) - flush_one_hunk(result, &ctx); - before = after = -1; - } - - /* If we get here, we're inside a hunk. */ - if (line[0] == '-' || line[0] == ' ') - before--; - if (line[0] == '+' || line[0] == ' ') - after--; - - /* Compute the sha without whitespace */ - len = remove_space(line); - patchlen += len; - the_hash_algo->update_fn(&ctx, line, len); - } - - if (!found_next) - oidclr(next_oid); - - flush_one_hunk(result, &ctx); - - return patchlen; -} - -static void generate_id_list(int stable) -{ - struct object_id oid, n, result; - int patchlen; - struct strbuf line_buf = STRBUF_INIT; - - oidclr(&oid); - while (!feof(stdin)) { - patchlen = get_one_patchid(&n, &result, &line_buf, stable); - flush_current_id(patchlen, &oid, &result); - oidcpy(&oid, &n); - } - strbuf_release(&line_buf); -} - -static const char patch_id_usage[] = "git patch-id [--stable | --unstable]"; - -static int git_patch_id_config(const char *var, const char *value, void *cb) -{ - int *stable = cb; - - if (!strcmp(var, "patchid.stable")) { - *stable = git_config_bool(var, value); - return 0; - } - - return git_default_config(var, value, cb); -} - -int cmd_patch_id(int argc, const char **argv, const char *prefix) -{ - int stable = -1; - - git_config(git_patch_id_config, &stable); - - /* If nothing is set, default to unstable. */ - if (stable < 0) - stable = 0; - - if (argc == 2 && !strcmp(argv[1], "--stable")) - stable = 1; - else if (argc == 2 && !strcmp(argv[1], "--unstable")) - stable = 0; - else if (argc != 1) - usage(patch_id_usage); - - generate_id_list(stable); - return 0; -} diff --git a/third_party/git/builtin/prune-packed.c b/third_party/git/builtin/prune-packed.c deleted file mode 100644 index b7b9281a8cea..000000000000 --- a/third_party/git/builtin/prune-packed.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "builtin.h" -#include "parse-options.h" -#include "prune-packed.h" - -static const char * const prune_packed_usage[] = { - N_("git prune-packed [-n | --dry-run] [-q | --quiet]"), - NULL -}; - -int cmd_prune_packed(int argc, const char **argv, const char *prefix) -{ - int opts = isatty(2) ? PRUNE_PACKED_VERBOSE : 0; - const struct option prune_packed_options[] = { - OPT_BIT('n', "dry-run", &opts, N_("dry run"), - PRUNE_PACKED_DRY_RUN), - OPT_NEGBIT('q', "quiet", &opts, N_("be quiet"), - PRUNE_PACKED_VERBOSE), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, prune_packed_options, - prune_packed_usage, 0); - - if (argc > 0) - usage_msg_opt(_("too many arguments"), - prune_packed_usage, - prune_packed_options); - - prune_packed_objects(opts); - return 0; -} diff --git a/third_party/git/builtin/prune.c b/third_party/git/builtin/prune.c deleted file mode 100644 index 02c6ab7cbaaf..000000000000 --- a/third_party/git/builtin/prune.c +++ /dev/null @@ -1,189 +0,0 @@ -#include "cache.h" -#include "commit.h" -#include "diff.h" -#include "revision.h" -#include "builtin.h" -#include "reachable.h" -#include "parse-options.h" -#include "progress.h" -#include "prune-packed.h" -#include "object-store.h" -#include "shallow.h" - -static const char * const prune_usage[] = { - N_("git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]"), - NULL -}; -static int show_only; -static int verbose; -static timestamp_t expire; -static int show_progress = -1; - -static int prune_tmp_file(const char *fullpath) -{ - struct stat st; - if (lstat(fullpath, &st)) - return error("Could not stat '%s'", fullpath); - if (st.st_mtime > expire) - return 0; - if (show_only || verbose) - printf("Removing stale temporary file %s\n", fullpath); - if (!show_only) - unlink_or_warn(fullpath); - return 0; -} - -static void perform_reachability_traversal(struct rev_info *revs) -{ - static int initialized; - struct progress *progress = NULL; - - if (initialized) - return; - - if (show_progress) - progress = start_delayed_progress(_("Checking connectivity"), 0); - mark_reachable_objects(revs, 1, expire, progress); - stop_progress(&progress); - initialized = 1; -} - -static int is_object_reachable(const struct object_id *oid, - struct rev_info *revs) -{ - struct object *obj; - - perform_reachability_traversal(revs); - - obj = lookup_object(the_repository, oid); - return obj && (obj->flags & SEEN); -} - -static int prune_object(const struct object_id *oid, const char *fullpath, - void *data) -{ - struct rev_info *revs = data; - struct stat st; - - if (is_object_reachable(oid, revs)) - return 0; - - if (lstat(fullpath, &st)) { - /* report errors, but do not stop pruning */ - error("Could not stat '%s'", fullpath); - return 0; - } - if (st.st_mtime > expire) - return 0; - if (show_only || verbose) { - enum object_type type = oid_object_info(the_repository, oid, - NULL); - printf("%s %s\n", oid_to_hex(oid), - (type > 0) ? type_name(type) : "unknown"); - } - if (!show_only) - unlink_or_warn(fullpath); - return 0; -} - -static int prune_cruft(const char *basename, const char *path, void *data) -{ - if (starts_with(basename, "tmp_obj_")) - prune_tmp_file(path); - else - fprintf(stderr, "bad sha1 file: %s\n", path); - return 0; -} - -static int prune_subdir(unsigned int nr, const char *path, void *data) -{ - if (!show_only) - rmdir(path); - return 0; -} - -/* - * Write errors (particularly out of space) can result in - * failed temporary packs (and more rarely indexes and other - * files beginning with "tmp_") accumulating in the object - * and the pack directories. - */ -static void remove_temporary_files(const char *path) -{ - DIR *dir; - struct dirent *de; - - dir = opendir(path); - if (!dir) { - fprintf(stderr, "Unable to open directory %s\n", path); - return; - } - while ((de = readdir(dir)) != NULL) - if (starts_with(de->d_name, "tmp_")) - prune_tmp_file(mkpath("%s/%s", path, de->d_name)); - closedir(dir); -} - -int cmd_prune(int argc, const char **argv, const char *prefix) -{ - struct rev_info revs; - int exclude_promisor_objects = 0; - const struct option options[] = { - OPT__DRY_RUN(&show_only, N_("do not remove, show only")), - OPT__VERBOSE(&verbose, N_("report pruned objects")), - OPT_BOOL(0, "progress", &show_progress, N_("show progress")), - OPT_EXPIRY_DATE(0, "expire", &expire, - N_("expire objects older than <time>")), - OPT_BOOL(0, "exclude-promisor-objects", &exclude_promisor_objects, - N_("limit traversal to objects outside promisor packfiles")), - OPT_END() - }; - char *s; - - expire = TIME_MAX; - save_commit_buffer = 0; - read_replace_refs = 0; - ref_paranoia = 1; - repo_init_revisions(the_repository, &revs, prefix); - - argc = parse_options(argc, argv, prefix, options, prune_usage, 0); - - if (repository_format_precious_objects) - die(_("cannot prune in a precious-objects repo")); - - while (argc--) { - struct object_id oid; - const char *name = *argv++; - - if (!get_oid(name, &oid)) { - struct object *object = parse_object_or_die(&oid, - name); - add_pending_object(&revs, object, ""); - } - else - die("unrecognized argument: %s", name); - } - - if (show_progress == -1) - show_progress = isatty(2); - if (exclude_promisor_objects) { - fetch_if_missing = 0; - revs.exclude_promisor_objects = 1; - } - - for_each_loose_file_in_objdir(get_object_directory(), prune_object, - prune_cruft, prune_subdir, &revs); - - prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0); - remove_temporary_files(get_object_directory()); - s = mkpathdup("%s/pack", get_object_directory()); - remove_temporary_files(s); - free(s); - - if (is_repository_shallow(the_repository)) { - perform_reachability_traversal(&revs); - prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0); - } - - return 0; -} diff --git a/third_party/git/builtin/pull.c b/third_party/git/builtin/pull.c deleted file mode 100644 index 425950f46973..000000000000 --- a/third_party/git/builtin/pull.c +++ /dev/null @@ -1,1051 +0,0 @@ -/* - * Builtin "git pull" - * - * Based on git-pull.sh by Junio C Hamano - * - * Fetch one or more remote refs and merge it/them into the current HEAD. - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "builtin.h" -#include "parse-options.h" -#include "exec-cmd.h" -#include "run-command.h" -#include "oid-array.h" -#include "remote.h" -#include "dir.h" -#include "rebase.h" -#include "refs.h" -#include "refspec.h" -#include "revision.h" -#include "submodule.h" -#include "submodule-config.h" -#include "tempfile.h" -#include "lockfile.h" -#include "wt-status.h" -#include "commit-reach.h" -#include "sequencer.h" - -/** - * Parses the value of --rebase. If value is a false value, returns - * REBASE_FALSE. If value is a true value, returns REBASE_TRUE. If value is - * "merges", returns REBASE_MERGES. If value is "preserve", returns - * REBASE_PRESERVE. If value is a invalid value, dies with a fatal error if - * fatal is true, otherwise returns REBASE_INVALID. - */ -static enum rebase_type parse_config_rebase(const char *key, const char *value, - int fatal) -{ - enum rebase_type v = rebase_parse_value(value); - if (v != REBASE_INVALID) - return v; - - if (fatal) - die(_("Invalid value for %s: %s"), key, value); - else - error(_("Invalid value for %s: %s"), key, value); - - return REBASE_INVALID; -} - -/** - * Callback for --rebase, which parses arg with parse_config_rebase(). - */ -static int parse_opt_rebase(const struct option *opt, const char *arg, int unset) -{ - enum rebase_type *value = opt->value; - - if (arg) - *value = parse_config_rebase("--rebase", arg, 0); - else - *value = unset ? REBASE_FALSE : REBASE_TRUE; - return *value == REBASE_INVALID ? -1 : 0; -} - -static const char * const pull_usage[] = { - N_("git pull [<options>] [<repository> [<refspec>...]]"), - NULL -}; - -/* Shared options */ -static int opt_verbosity; -static char *opt_progress; -static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; - -/* Options passed to git-merge or git-rebase */ -static enum rebase_type opt_rebase = -1; -static char *opt_diffstat; -static char *opt_log; -static char *opt_signoff; -static char *opt_squash; -static char *opt_commit; -static char *opt_edit; -static char *cleanup_arg; -static char *opt_ff; -static char *opt_verify_signatures; -static int opt_autostash = -1; -static int config_autostash; -static int check_trust_level = 1; -static struct strvec opt_strategies = STRVEC_INIT; -static struct strvec opt_strategy_opts = STRVEC_INIT; -static char *opt_gpg_sign; -static int opt_allow_unrelated_histories; - -/* Options passed to git-fetch */ -static char *opt_all; -static char *opt_append; -static char *opt_upload_pack; -static int opt_force; -static char *opt_tags; -static char *opt_prune; -static char *max_children; -static int opt_dry_run; -static char *opt_keep; -static char *opt_depth; -static char *opt_unshallow; -static char *opt_update_shallow; -static char *opt_refmap; -static char *opt_ipv4; -static char *opt_ipv6; -static int opt_show_forced_updates = -1; -static char *set_upstream; -static struct strvec opt_fetch = STRVEC_INIT; - -static struct option pull_options[] = { - /* Shared options */ - OPT__VERBOSITY(&opt_verbosity), - OPT_PASSTHRU(0, "progress", &opt_progress, NULL, - N_("force progress reporting"), - PARSE_OPT_NOARG), - OPT_CALLBACK_F(0, "recurse-submodules", - &recurse_submodules, N_("on-demand"), - N_("control for recursive fetching of submodules"), - PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules), - - /* Options passed to git-merge or git-rebase */ - OPT_GROUP(N_("Options related to merging")), - OPT_CALLBACK_F('r', "rebase", &opt_rebase, - "(false|true|merges|preserve|interactive)", - N_("incorporate changes by rebasing rather than merging"), - PARSE_OPT_OPTARG, parse_opt_rebase), - OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL, - N_("do not show a diffstat at the end of the merge"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG), - OPT_PASSTHRU(0, "stat", &opt_diffstat, NULL, - N_("show a diffstat at the end of the merge"), - PARSE_OPT_NOARG), - OPT_PASSTHRU(0, "summary", &opt_diffstat, NULL, - N_("(synonym to --stat)"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN), - OPT_PASSTHRU(0, "log", &opt_log, N_("n"), - N_("add (at most <n>) entries from shortlog to merge commit message"), - PARSE_OPT_OPTARG), - OPT_PASSTHRU(0, "signoff", &opt_signoff, NULL, - N_("add Signed-off-by:"), - PARSE_OPT_OPTARG), - OPT_PASSTHRU(0, "squash", &opt_squash, NULL, - N_("create a single commit instead of doing a merge"), - PARSE_OPT_NOARG), - OPT_PASSTHRU(0, "commit", &opt_commit, NULL, - N_("perform a commit if the merge succeeds (default)"), - PARSE_OPT_NOARG), - OPT_PASSTHRU(0, "edit", &opt_edit, NULL, - N_("edit message before committing"), - PARSE_OPT_NOARG), - OPT_CLEANUP(&cleanup_arg), - OPT_PASSTHRU(0, "ff", &opt_ff, NULL, - N_("allow fast-forward"), - PARSE_OPT_NOARG), - OPT_PASSTHRU(0, "ff-only", &opt_ff, NULL, - N_("abort if fast-forward is not possible"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG), - OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL, - N_("verify that the named commit has a valid GPG signature"), - PARSE_OPT_NOARG), - OPT_BOOL(0, "autostash", &opt_autostash, - N_("automatically stash/stash pop before and after")), - OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"), - N_("merge strategy to use"), - 0), - OPT_PASSTHRU_ARGV('X', "strategy-option", &opt_strategy_opts, - N_("option=value"), - N_("option for selected merge strategy"), - 0), - OPT_PASSTHRU('S', "gpg-sign", &opt_gpg_sign, N_("key-id"), - N_("GPG sign commit"), - PARSE_OPT_OPTARG), - OPT_SET_INT(0, "allow-unrelated-histories", - &opt_allow_unrelated_histories, - N_("allow merging unrelated histories"), 1), - - /* Options passed to git-fetch */ - OPT_GROUP(N_("Options related to fetching")), - OPT_PASSTHRU(0, "all", &opt_all, NULL, - N_("fetch from all remotes"), - PARSE_OPT_NOARG), - OPT_PASSTHRU('a', "append", &opt_append, NULL, - N_("append to .git/FETCH_HEAD instead of overwriting"), - PARSE_OPT_NOARG), - OPT_PASSTHRU(0, "upload-pack", &opt_upload_pack, N_("path"), - N_("path to upload pack on remote end"), - 0), - OPT__FORCE(&opt_force, N_("force overwrite of local branch"), 0), - OPT_PASSTHRU('t', "tags", &opt_tags, NULL, - N_("fetch all tags and associated objects"), - PARSE_OPT_NOARG), - OPT_PASSTHRU('p', "prune", &opt_prune, NULL, - N_("prune remote-tracking branches no longer on remote"), - PARSE_OPT_NOARG), - OPT_PASSTHRU('j', "jobs", &max_children, N_("n"), - N_("number of submodules pulled in parallel"), - PARSE_OPT_OPTARG), - OPT_BOOL(0, "dry-run", &opt_dry_run, - N_("dry run")), - OPT_PASSTHRU('k', "keep", &opt_keep, NULL, - N_("keep downloaded pack"), - PARSE_OPT_NOARG), - OPT_PASSTHRU(0, "depth", &opt_depth, N_("depth"), - N_("deepen history of shallow clone"), - 0), - OPT_PASSTHRU_ARGV(0, "shallow-since", &opt_fetch, N_("time"), - N_("deepen history of shallow repository based on time"), - 0), - OPT_PASSTHRU_ARGV(0, "shallow-exclude", &opt_fetch, N_("revision"), - N_("deepen history of shallow clone, excluding rev"), - 0), - OPT_PASSTHRU_ARGV(0, "deepen", &opt_fetch, N_("n"), - N_("deepen history of shallow clone"), - 0), - OPT_PASSTHRU(0, "unshallow", &opt_unshallow, NULL, - N_("convert to a complete repository"), - PARSE_OPT_NONEG | PARSE_OPT_NOARG), - OPT_PASSTHRU(0, "update-shallow", &opt_update_shallow, NULL, - N_("accept refs that update .git/shallow"), - PARSE_OPT_NOARG), - OPT_PASSTHRU(0, "refmap", &opt_refmap, N_("refmap"), - N_("specify fetch refmap"), - PARSE_OPT_NONEG), - OPT_PASSTHRU_ARGV('o', "server-option", &opt_fetch, - N_("server-specific"), - N_("option to transmit"), - 0), - OPT_PASSTHRU('4', "ipv4", &opt_ipv4, NULL, - N_("use IPv4 addresses only"), - PARSE_OPT_NOARG), - OPT_PASSTHRU('6', "ipv6", &opt_ipv6, NULL, - N_("use IPv6 addresses only"), - PARSE_OPT_NOARG), - OPT_PASSTHRU_ARGV(0, "negotiation-tip", &opt_fetch, N_("revision"), - N_("report that we have only objects reachable from this object"), - 0), - OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates, - N_("check for forced-updates on all updated branches")), - OPT_PASSTHRU(0, "set-upstream", &set_upstream, NULL, - N_("set upstream for git pull/fetch"), - PARSE_OPT_NOARG), - - OPT_END() -}; - -/** - * Pushes "-q" or "-v" switches into arr to match the opt_verbosity level. - */ -static void argv_push_verbosity(struct strvec *arr) -{ - int verbosity; - - for (verbosity = opt_verbosity; verbosity > 0; verbosity--) - strvec_push(arr, "-v"); - - for (verbosity = opt_verbosity; verbosity < 0; verbosity++) - strvec_push(arr, "-q"); -} - -/** - * Pushes "-f" switches into arr to match the opt_force level. - */ -static void argv_push_force(struct strvec *arr) -{ - int force = opt_force; - while (force-- > 0) - strvec_push(arr, "-f"); -} - -/** - * Sets the GIT_REFLOG_ACTION environment variable to the concatenation of argv - */ -static void set_reflog_message(int argc, const char **argv) -{ - int i; - struct strbuf msg = STRBUF_INIT; - - for (i = 0; i < argc; i++) { - if (i) - strbuf_addch(&msg, ' '); - strbuf_addstr(&msg, argv[i]); - } - - setenv("GIT_REFLOG_ACTION", msg.buf, 0); - - strbuf_release(&msg); -} - -/** - * If pull.ff is unset, returns NULL. If pull.ff is "true", returns "--ff". If - * pull.ff is "false", returns "--no-ff". If pull.ff is "only", returns - * "--ff-only". Otherwise, if pull.ff is set to an invalid value, die with an - * error. - */ -static const char *config_get_ff(void) -{ - const char *value; - - if (git_config_get_value("pull.ff", &value)) - return NULL; - - switch (git_parse_maybe_bool(value)) { - case 0: - return "--no-ff"; - case 1: - return "--ff"; - } - - if (!strcmp(value, "only")) - return "--ff-only"; - - die(_("Invalid value for pull.ff: %s"), value); -} - -/** - * Returns the default configured value for --rebase. It first looks for the - * value of "branch.$curr_branch.rebase", where $curr_branch is the current - * branch, and if HEAD is detached or the configuration key does not exist, - * looks for the value of "pull.rebase". If both configuration keys do not - * exist, returns REBASE_FALSE. - */ -static enum rebase_type config_get_rebase(void) -{ - struct branch *curr_branch = branch_get("HEAD"); - const char *value; - - if (curr_branch) { - char *key = xstrfmt("branch.%s.rebase", curr_branch->name); - - if (!git_config_get_value(key, &value)) { - enum rebase_type ret = parse_config_rebase(key, value, 1); - free(key); - return ret; - } - - free(key); - } - - if (!git_config_get_value("pull.rebase", &value)) - return parse_config_rebase("pull.rebase", value, 1); - - if (opt_verbosity >= 0 && !opt_ff) { - warning(_("Pulling without specifying how to reconcile divergent branches is\n" - "discouraged. You can squelch this message by running one of the following\n" - "commands sometime before your next pull:\n" - "\n" - " git config pull.rebase false # merge (the default strategy)\n" - " git config pull.rebase true # rebase\n" - " git config pull.ff only # fast-forward only\n" - "\n" - "You can replace \"git config\" with \"git config --global\" to set a default\n" - "preference for all repositories. You can also pass --rebase, --no-rebase,\n" - "or --ff-only on the command line to override the configured default per\n" - "invocation.\n")); - } - - return REBASE_FALSE; -} - -/** - * Read config variables. - */ -static int git_pull_config(const char *var, const char *value, void *cb) -{ - int status; - - if (!strcmp(var, "rebase.autostash")) { - config_autostash = git_config_bool(var, value); - return 0; - } else if (!strcmp(var, "submodule.recurse")) { - recurse_submodules = git_config_bool(var, value) ? - RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF; - return 0; - } else if (!strcmp(var, "gpg.mintrustlevel")) { - check_trust_level = 0; - } - - status = git_gpg_config(var, value, cb); - if (status) - return status; - - return git_default_config(var, value, cb); -} - -/** - * Appends merge candidates from FETCH_HEAD that are not marked not-for-merge - * into merge_heads. - */ -static void get_merge_heads(struct oid_array *merge_heads) -{ - const char *filename = git_path_fetch_head(the_repository); - FILE *fp; - struct strbuf sb = STRBUF_INIT; - struct object_id oid; - - fp = xfopen(filename, "r"); - while (strbuf_getline_lf(&sb, fp) != EOF) { - const char *p; - if (parse_oid_hex(sb.buf, &oid, &p)) - continue; /* invalid line: does not start with object ID */ - if (starts_with(p, "\tnot-for-merge\t")) - continue; /* ref is not-for-merge */ - oid_array_append(merge_heads, &oid); - } - fclose(fp); - strbuf_release(&sb); -} - -/** - * Used by die_no_merge_candidates() as a for_each_remote() callback to - * retrieve the name of the remote if the repository only has one remote. - */ -static int get_only_remote(struct remote *remote, void *cb_data) -{ - const char **remote_name = cb_data; - - if (*remote_name) - return -1; - - *remote_name = remote->name; - return 0; -} - -/** - * Dies with the appropriate reason for why there are no merge candidates: - * - * 1. We fetched from a specific remote, and a refspec was given, but it ended - * up not fetching anything. This is usually because the user provided a - * wildcard refspec which had no matches on the remote end. - * - * 2. We fetched from a non-default remote, but didn't specify a branch to - * merge. We can't use the configured one because it applies to the default - * remote, thus the user must specify the branches to merge. - * - * 3. We fetched from the branch's or repo's default remote, but: - * - * a. We are not on a branch, so there will never be a configured branch to - * merge with. - * - * b. We are on a branch, but there is no configured branch to merge with. - * - * 4. We fetched from the branch's or repo's default remote, but the configured - * branch to merge didn't get fetched. (Either it doesn't exist, or wasn't - * part of the configured fetch refspec.) - */ -static void NORETURN die_no_merge_candidates(const char *repo, const char **refspecs) -{ - struct branch *curr_branch = branch_get("HEAD"); - const char *remote = curr_branch ? curr_branch->remote_name : NULL; - - if (*refspecs) { - if (opt_rebase) - fprintf_ln(stderr, _("There is no candidate for rebasing against among the refs that you just fetched.")); - else - fprintf_ln(stderr, _("There are no candidates for merging among the refs that you just fetched.")); - fprintf_ln(stderr, _("Generally this means that you provided a wildcard refspec which had no\n" - "matches on the remote end.")); - } else if (repo && curr_branch && (!remote || strcmp(repo, remote))) { - fprintf_ln(stderr, _("You asked to pull from the remote '%s', but did not specify\n" - "a branch. Because this is not the default configured remote\n" - "for your current branch, you must specify a branch on the command line."), - repo); - } else if (!curr_branch) { - fprintf_ln(stderr, _("You are not currently on a branch.")); - if (opt_rebase) - fprintf_ln(stderr, _("Please specify which branch you want to rebase against.")); - else - fprintf_ln(stderr, _("Please specify which branch you want to merge with.")); - fprintf_ln(stderr, _("See git-pull(1) for details.")); - fprintf(stderr, "\n"); - fprintf_ln(stderr, " git pull %s %s", _("<remote>"), _("<branch>")); - fprintf(stderr, "\n"); - } else if (!curr_branch->merge_nr) { - const char *remote_name = NULL; - - if (for_each_remote(get_only_remote, &remote_name) || !remote_name) - remote_name = _("<remote>"); - - fprintf_ln(stderr, _("There is no tracking information for the current branch.")); - if (opt_rebase) - fprintf_ln(stderr, _("Please specify which branch you want to rebase against.")); - else - fprintf_ln(stderr, _("Please specify which branch you want to merge with.")); - fprintf_ln(stderr, _("See git-pull(1) for details.")); - fprintf(stderr, "\n"); - fprintf_ln(stderr, " git pull %s %s", _("<remote>"), _("<branch>")); - fprintf(stderr, "\n"); - fprintf_ln(stderr, _("If you wish to set tracking information for this branch you can do so with:")); - fprintf(stderr, "\n"); - fprintf_ln(stderr, " git branch --set-upstream-to=%s/%s %s\n", - remote_name, _("<branch>"), curr_branch->name); - } else - fprintf_ln(stderr, _("Your configuration specifies to merge with the ref '%s'\n" - "from the remote, but no such ref was fetched."), - *curr_branch->merge_name); - exit(1); -} - -/** - * Parses argv into [<repo> [<refspecs>...]], returning their values in `repo` - * as a string and `refspecs` as a null-terminated array of strings. If `repo` - * is not provided in argv, it is set to NULL. - */ -static void parse_repo_refspecs(int argc, const char **argv, const char **repo, - const char ***refspecs) -{ - if (argc > 0) { - *repo = *argv++; - argc--; - } else - *repo = NULL; - *refspecs = argv; -} - -/** - * Runs git-fetch, returning its exit status. `repo` and `refspecs` are the - * repository and refspecs to fetch, or NULL if they are not provided. - */ -static int run_fetch(const char *repo, const char **refspecs) -{ - struct strvec args = STRVEC_INIT; - int ret; - - strvec_pushl(&args, "fetch", "--update-head-ok", NULL); - - /* Shared options */ - argv_push_verbosity(&args); - if (opt_progress) - strvec_push(&args, opt_progress); - - /* Options passed to git-fetch */ - if (opt_all) - strvec_push(&args, opt_all); - if (opt_append) - strvec_push(&args, opt_append); - if (opt_upload_pack) - strvec_push(&args, opt_upload_pack); - argv_push_force(&args); - if (opt_tags) - strvec_push(&args, opt_tags); - if (opt_prune) - strvec_push(&args, opt_prune); - if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT) - switch (recurse_submodules) { - case RECURSE_SUBMODULES_ON: - strvec_push(&args, "--recurse-submodules=on"); - break; - case RECURSE_SUBMODULES_OFF: - strvec_push(&args, "--recurse-submodules=no"); - break; - case RECURSE_SUBMODULES_ON_DEMAND: - strvec_push(&args, "--recurse-submodules=on-demand"); - break; - default: - BUG("submodule recursion option not understood"); - } - if (max_children) - strvec_push(&args, max_children); - if (opt_dry_run) - strvec_push(&args, "--dry-run"); - if (opt_keep) - strvec_push(&args, opt_keep); - if (opt_depth) - strvec_push(&args, opt_depth); - if (opt_unshallow) - strvec_push(&args, opt_unshallow); - if (opt_update_shallow) - strvec_push(&args, opt_update_shallow); - if (opt_refmap) - strvec_push(&args, opt_refmap); - if (opt_ipv4) - strvec_push(&args, opt_ipv4); - if (opt_ipv6) - strvec_push(&args, opt_ipv6); - if (opt_show_forced_updates > 0) - strvec_push(&args, "--show-forced-updates"); - else if (opt_show_forced_updates == 0) - strvec_push(&args, "--no-show-forced-updates"); - if (set_upstream) - strvec_push(&args, set_upstream); - strvec_pushv(&args, opt_fetch.v); - - if (repo) { - strvec_push(&args, repo); - strvec_pushv(&args, refspecs); - } else if (*refspecs) - BUG("refspecs without repo?"); - ret = run_command_v_opt(args.v, RUN_GIT_CMD); - strvec_clear(&args); - return ret; -} - -/** - * "Pulls into void" by branching off merge_head. - */ -static int pull_into_void(const struct object_id *merge_head, - const struct object_id *curr_head) -{ - if (opt_verify_signatures) { - struct commit *commit; - - commit = lookup_commit(the_repository, merge_head); - if (!commit) - die(_("unable to access commit %s"), - oid_to_hex(merge_head)); - - verify_merge_signature(commit, opt_verbosity, - check_trust_level); - } - - /* - * Two-way merge: we treat the index as based on an empty tree, - * and try to fast-forward to HEAD. This ensures we will not lose - * index/worktree changes that the user already made on the unborn - * branch. - */ - if (checkout_fast_forward(the_repository, - the_hash_algo->empty_tree, - merge_head, 0)) - return 1; - - if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR)) - return 1; - - return 0; -} - -static int rebase_submodules(void) -{ - struct child_process cp = CHILD_PROCESS_INIT; - - cp.git_cmd = 1; - cp.no_stdin = 1; - strvec_pushl(&cp.args, "submodule", "update", - "--recursive", "--rebase", NULL); - argv_push_verbosity(&cp.args); - - return run_command(&cp); -} - -static int update_submodules(void) -{ - struct child_process cp = CHILD_PROCESS_INIT; - - cp.git_cmd = 1; - cp.no_stdin = 1; - strvec_pushl(&cp.args, "submodule", "update", - "--recursive", "--checkout", NULL); - argv_push_verbosity(&cp.args); - - return run_command(&cp); -} - -/** - * Runs git-merge, returning its exit status. - */ -static int run_merge(void) -{ - int ret; - struct strvec args = STRVEC_INIT; - - strvec_pushl(&args, "merge", NULL); - - /* Shared options */ - argv_push_verbosity(&args); - if (opt_progress) - strvec_push(&args, opt_progress); - - /* Options passed to git-merge */ - if (opt_diffstat) - strvec_push(&args, opt_diffstat); - if (opt_log) - strvec_push(&args, opt_log); - if (opt_signoff) - strvec_push(&args, opt_signoff); - if (opt_squash) - strvec_push(&args, opt_squash); - if (opt_commit) - strvec_push(&args, opt_commit); - if (opt_edit) - strvec_push(&args, opt_edit); - if (cleanup_arg) - strvec_pushf(&args, "--cleanup=%s", cleanup_arg); - if (opt_ff) - strvec_push(&args, opt_ff); - if (opt_verify_signatures) - strvec_push(&args, opt_verify_signatures); - strvec_pushv(&args, opt_strategies.v); - strvec_pushv(&args, opt_strategy_opts.v); - if (opt_gpg_sign) - strvec_push(&args, opt_gpg_sign); - if (opt_autostash == 0) - strvec_push(&args, "--no-autostash"); - else if (opt_autostash == 1) - strvec_push(&args, "--autostash"); - if (opt_allow_unrelated_histories > 0) - strvec_push(&args, "--allow-unrelated-histories"); - - strvec_push(&args, "FETCH_HEAD"); - ret = run_command_v_opt(args.v, RUN_GIT_CMD); - strvec_clear(&args); - return ret; -} - -/** - * Returns remote's upstream branch for the current branch. If remote is NULL, - * the current branch's configured default remote is used. Returns NULL if - * `remote` does not name a valid remote, HEAD does not point to a branch, - * remote is not the branch's configured remote or the branch does not have any - * configured upstream branch. - */ -static const char *get_upstream_branch(const char *remote) -{ - struct remote *rm; - struct branch *curr_branch; - const char *curr_branch_remote; - - rm = remote_get(remote); - if (!rm) - return NULL; - - curr_branch = branch_get("HEAD"); - if (!curr_branch) - return NULL; - - curr_branch_remote = remote_for_branch(curr_branch, NULL); - assert(curr_branch_remote); - - if (strcmp(curr_branch_remote, rm->name)) - return NULL; - - return branch_get_upstream(curr_branch, NULL); -} - -/** - * Derives the remote-tracking branch from the remote and refspec. - * - * FIXME: The current implementation assumes the default mapping of - * refs/heads/<branch_name> to refs/remotes/<remote_name>/<branch_name>. - */ -static const char *get_tracking_branch(const char *remote, const char *refspec) -{ - struct refspec_item spec; - const char *spec_src; - const char *merge_branch; - - refspec_item_init_or_die(&spec, refspec, REFSPEC_FETCH); - spec_src = spec.src; - if (!*spec_src || !strcmp(spec_src, "HEAD")) - spec_src = "HEAD"; - else if (skip_prefix(spec_src, "heads/", &spec_src)) - ; - else if (skip_prefix(spec_src, "refs/heads/", &spec_src)) - ; - else if (starts_with(spec_src, "refs/") || - starts_with(spec_src, "tags/") || - starts_with(spec_src, "remotes/")) - spec_src = ""; - - if (*spec_src) { - if (!strcmp(remote, ".")) - merge_branch = mkpath("refs/heads/%s", spec_src); - else - merge_branch = mkpath("refs/remotes/%s/%s", remote, spec_src); - } else - merge_branch = NULL; - - refspec_item_clear(&spec); - return merge_branch; -} - -/** - * Given the repo and refspecs, sets fork_point to the point at which the - * current branch forked from its remote-tracking branch. Returns 0 on success, - * -1 on failure. - */ -static int get_rebase_fork_point(struct object_id *fork_point, const char *repo, - const char *refspec) -{ - int ret; - struct branch *curr_branch; - const char *remote_branch; - struct child_process cp = CHILD_PROCESS_INIT; - struct strbuf sb = STRBUF_INIT; - - curr_branch = branch_get("HEAD"); - if (!curr_branch) - return -1; - - if (refspec) - remote_branch = get_tracking_branch(repo, refspec); - else - remote_branch = get_upstream_branch(repo); - - if (!remote_branch) - return -1; - - strvec_pushl(&cp.args, "merge-base", "--fork-point", - remote_branch, curr_branch->name, NULL); - cp.no_stdin = 1; - cp.no_stderr = 1; - cp.git_cmd = 1; - - ret = capture_command(&cp, &sb, GIT_MAX_HEXSZ); - if (ret) - goto cleanup; - - ret = get_oid_hex(sb.buf, fork_point); - if (ret) - goto cleanup; - -cleanup: - strbuf_release(&sb); - return ret ? -1 : 0; -} - -/** - * Sets merge_base to the octopus merge base of curr_head, merge_head and - * fork_point. Returns 0 if a merge base is found, 1 otherwise. - */ -static int get_octopus_merge_base(struct object_id *merge_base, - const struct object_id *curr_head, - const struct object_id *merge_head, - const struct object_id *fork_point) -{ - struct commit_list *revs = NULL, *result; - - commit_list_insert(lookup_commit_reference(the_repository, curr_head), - &revs); - commit_list_insert(lookup_commit_reference(the_repository, merge_head), - &revs); - if (!is_null_oid(fork_point)) - commit_list_insert(lookup_commit_reference(the_repository, fork_point), - &revs); - - result = get_octopus_merge_bases(revs); - free_commit_list(revs); - reduce_heads_replace(&result); - - if (!result) - return 1; - - oidcpy(merge_base, &result->item->object.oid); - free_commit_list(result); - return 0; -} - -/** - * Given the current HEAD oid, the merge head returned from git-fetch and the - * fork point calculated by get_rebase_fork_point(), runs git-rebase with the - * appropriate arguments and returns its exit status. - */ -static int run_rebase(const struct object_id *curr_head, - const struct object_id *merge_head, - const struct object_id *fork_point) -{ - int ret; - struct object_id oct_merge_base; - struct strvec args = STRVEC_INIT; - - if (!get_octopus_merge_base(&oct_merge_base, curr_head, merge_head, fork_point)) - if (!is_null_oid(fork_point) && oideq(&oct_merge_base, fork_point)) - fork_point = NULL; - - strvec_push(&args, "rebase"); - - /* Shared options */ - argv_push_verbosity(&args); - - /* Options passed to git-rebase */ - if (opt_rebase == REBASE_MERGES) - strvec_push(&args, "--rebase-merges"); - else if (opt_rebase == REBASE_PRESERVE) - strvec_push(&args, "--preserve-merges"); - else if (opt_rebase == REBASE_INTERACTIVE) - strvec_push(&args, "--interactive"); - if (opt_diffstat) - strvec_push(&args, opt_diffstat); - strvec_pushv(&args, opt_strategies.v); - strvec_pushv(&args, opt_strategy_opts.v); - if (opt_gpg_sign) - strvec_push(&args, opt_gpg_sign); - if (opt_autostash == 0) - strvec_push(&args, "--no-autostash"); - else if (opt_autostash == 1) - strvec_push(&args, "--autostash"); - if (opt_verify_signatures && - !strcmp(opt_verify_signatures, "--verify-signatures")) - warning(_("ignoring --verify-signatures for rebase")); - - strvec_push(&args, "--onto"); - strvec_push(&args, oid_to_hex(merge_head)); - - if (fork_point && !is_null_oid(fork_point)) - strvec_push(&args, oid_to_hex(fork_point)); - else - strvec_push(&args, oid_to_hex(merge_head)); - - ret = run_command_v_opt(args.v, RUN_GIT_CMD); - strvec_clear(&args); - return ret; -} - -int cmd_pull(int argc, const char **argv, const char *prefix) -{ - const char *repo, **refspecs; - struct oid_array merge_heads = OID_ARRAY_INIT; - struct object_id orig_head, curr_head; - struct object_id rebase_fork_point; - int autostash; - - if (!getenv("GIT_REFLOG_ACTION")) - set_reflog_message(argc, argv); - - git_config(git_pull_config, NULL); - - argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0); - - if (cleanup_arg) - /* - * this only checks the validity of cleanup_arg; we don't need - * a valid value for use_editor - */ - get_cleanup_mode(cleanup_arg, 0); - - parse_repo_refspecs(argc, argv, &repo, &refspecs); - - if (!opt_ff) - opt_ff = xstrdup_or_null(config_get_ff()); - - if (opt_rebase < 0) - opt_rebase = config_get_rebase(); - - if (read_cache_unmerged()) - die_resolve_conflict("pull"); - - if (file_exists(git_path_merge_head(the_repository))) - die_conclude_merge(); - - if (get_oid("HEAD", &orig_head)) - oidclr(&orig_head); - - autostash = config_autostash; - if (opt_rebase) { - if (opt_autostash != -1) - autostash = opt_autostash; - - if (is_null_oid(&orig_head) && !is_cache_unborn()) - die(_("Updating an unborn branch with changes added to the index.")); - - if (!autostash) - require_clean_work_tree(the_repository, - N_("pull with rebase"), - _("please commit or stash them."), 1, 0); - - if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs)) - oidclr(&rebase_fork_point); - } - - if (run_fetch(repo, refspecs)) - return 1; - - if (opt_dry_run) - return 0; - - if (get_oid("HEAD", &curr_head)) - oidclr(&curr_head); - - if (!is_null_oid(&orig_head) && !is_null_oid(&curr_head) && - !oideq(&orig_head, &curr_head)) { - /* - * The fetch involved updating the current branch. - * - * The working tree and the index file are still based on - * orig_head commit, but we are merging into curr_head. - * Update the working tree to match curr_head. - */ - - warning(_("fetch updated the current branch head.\n" - "fast-forwarding your working tree from\n" - "commit %s."), oid_to_hex(&orig_head)); - - if (checkout_fast_forward(the_repository, &orig_head, - &curr_head, 0)) - die(_("Cannot fast-forward your working tree.\n" - "After making sure that you saved anything precious from\n" - "$ git diff %s\n" - "output, run\n" - "$ git reset --hard\n" - "to recover."), oid_to_hex(&orig_head)); - } - - get_merge_heads(&merge_heads); - - if (!merge_heads.nr) - die_no_merge_candidates(repo, refspecs); - - if (is_null_oid(&orig_head)) { - if (merge_heads.nr > 1) - die(_("Cannot merge multiple branches into empty head.")); - return pull_into_void(merge_heads.oid, &curr_head); - } - if (opt_rebase && merge_heads.nr > 1) - die(_("Cannot rebase onto multiple branches.")); - - if (opt_rebase) { - int ret = 0; - int ran_ff = 0; - if ((recurse_submodules == RECURSE_SUBMODULES_ON || - recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) && - submodule_touches_in_range(the_repository, &rebase_fork_point, &curr_head)) - die(_("cannot rebase with locally recorded submodule modifications")); - if (!autostash) { - struct commit_list *list = NULL; - struct commit *merge_head, *head; - - head = lookup_commit_reference(the_repository, - &orig_head); - commit_list_insert(head, &list); - merge_head = lookup_commit_reference(the_repository, - &merge_heads.oid[0]); - if (repo_is_descendant_of(the_repository, - merge_head, list)) { - /* we can fast-forward this without invoking rebase */ - opt_ff = "--ff-only"; - ran_ff = 1; - ret = run_merge(); - } - free_commit_list(list); - } - if (!ran_ff) - ret = run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point); - - if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON || - recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)) - ret = rebase_submodules(); - - return ret; - } else { - int ret = run_merge(); - if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON || - recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)) - ret = update_submodules(); - return ret; - } -} diff --git a/third_party/git/builtin/push.c b/third_party/git/builtin/push.c deleted file mode 100644 index 6da3a8e5d30a..000000000000 --- a/third_party/git/builtin/push.c +++ /dev/null @@ -1,639 +0,0 @@ -/* - * "git push" - */ -#include "cache.h" -#include "config.h" -#include "refs.h" -#include "refspec.h" -#include "run-command.h" -#include "builtin.h" -#include "remote.h" -#include "transport.h" -#include "parse-options.h" -#include "submodule.h" -#include "submodule-config.h" -#include "send-pack.h" -#include "color.h" - -static const char * const push_usage[] = { - N_("git push [<options>] [<repository> [<refspec>...]]"), - NULL, -}; - -static int push_use_color = -1; -static char push_colors[][COLOR_MAXLEN] = { - GIT_COLOR_RESET, - GIT_COLOR_RED, /* ERROR */ -}; - -enum color_push { - PUSH_COLOR_RESET = 0, - PUSH_COLOR_ERROR = 1 -}; - -static int parse_push_color_slot(const char *slot) -{ - if (!strcasecmp(slot, "reset")) - return PUSH_COLOR_RESET; - if (!strcasecmp(slot, "error")) - return PUSH_COLOR_ERROR; - return -1; -} - -static const char *push_get_color(enum color_push ix) -{ - if (want_color_stderr(push_use_color)) - return push_colors[ix]; - return ""; -} - -static int thin = 1; -static int deleterefs; -static const char *receivepack; -static int verbosity; -static int progress = -1; -static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT; -static enum transport_family family; - -static struct push_cas_option cas; - -static struct refspec rs = REFSPEC_INIT_PUSH; - -static struct string_list push_options_config = STRING_LIST_INIT_DUP; - -static void refspec_append_mapped(struct refspec *refspec, const char *ref, - struct remote *remote, struct ref *local_refs) -{ - const char *branch_name; - struct ref *matched = NULL; - - /* Does "ref" uniquely name our ref? */ - if (count_refspec_match(ref, local_refs, &matched) != 1) { - refspec_append(refspec, ref); - return; - } - - if (remote->push.nr) { - struct refspec_item query; - memset(&query, 0, sizeof(struct refspec_item)); - query.src = matched->name; - if (!query_refspecs(&remote->push, &query) && query.dst) { - refspec_appendf(refspec, "%s%s:%s", - query.force ? "+" : "", - query.src, query.dst); - return; - } - } - - if (push_default == PUSH_DEFAULT_UPSTREAM && - skip_prefix(matched->name, "refs/heads/", &branch_name)) { - struct branch *branch = branch_get(branch_name); - if (branch->merge_nr == 1 && branch->merge[0]->src) { - refspec_appendf(refspec, "%s:%s", - ref, branch->merge[0]->src); - return; - } - } - - refspec_append(refspec, ref); -} - -static void set_refspecs(const char **refs, int nr, const char *repo) -{ - struct remote *remote = NULL; - struct ref *local_refs = NULL; - int i; - - for (i = 0; i < nr; i++) { - const char *ref = refs[i]; - if (!strcmp("tag", ref)) { - if (nr <= ++i) - die(_("tag shorthand without <tag>")); - ref = refs[i]; - if (deleterefs) - refspec_appendf(&rs, ":refs/tags/%s", ref); - else - refspec_appendf(&rs, "refs/tags/%s", ref); - } else if (deleterefs) { - if (strchr(ref, ':')) - die(_("--delete only accepts plain target ref names")); - refspec_appendf(&rs, ":%s", ref); - } else if (!strchr(ref, ':')) { - if (!remote) { - /* lazily grab remote and local_refs */ - remote = remote_get(repo); - local_refs = get_local_heads(); - } - refspec_append_mapped(&rs, ref, remote, local_refs); - } else - refspec_append(&rs, ref); - } -} - -static int push_url_of_remote(struct remote *remote, const char ***url_p) -{ - if (remote->pushurl_nr) { - *url_p = remote->pushurl; - return remote->pushurl_nr; - } - *url_p = remote->url; - return remote->url_nr; -} - -static NORETURN void die_push_simple(struct branch *branch, - struct remote *remote) -{ - /* - * There's no point in using shorten_unambiguous_ref here, - * as the ambiguity would be on the remote side, not what - * we have locally. Plus, this is supposed to be the simple - * mode. If the user is doing something crazy like setting - * upstream to a non-branch, we should probably be showing - * them the big ugly fully qualified ref. - */ - const char *advice_maybe = ""; - const char *short_upstream = branch->merge[0]->src; - - skip_prefix(short_upstream, "refs/heads/", &short_upstream); - - /* - * Don't show advice for people who explicitly set - * push.default. - */ - if (push_default == PUSH_DEFAULT_UNSPECIFIED) - advice_maybe = _("\n" - "To choose either option permanently, " - "see push.default in 'git help config'."); - die(_("The upstream branch of your current branch does not match\n" - "the name of your current branch. To push to the upstream branch\n" - "on the remote, use\n" - "\n" - " git push %s HEAD:%s\n" - "\n" - "To push to the branch of the same name on the remote, use\n" - "\n" - " git push %s HEAD\n" - "%s"), - remote->name, short_upstream, - remote->name, advice_maybe); -} - -static const char message_detached_head_die[] = - N_("You are not currently on a branch.\n" - "To push the history leading to the current (detached HEAD)\n" - "state now, use\n" - "\n" - " git push %s HEAD:<name-of-remote-branch>\n"); - -static void setup_push_upstream(struct remote *remote, struct branch *branch, - int triangular, int simple) -{ - if (!branch) - die(_(message_detached_head_die), remote->name); - if (!branch->merge_nr || !branch->merge || !branch->remote_name) - die(_("The current branch %s has no upstream branch.\n" - "To push the current branch and set the remote as upstream, use\n" - "\n" - " git push --set-upstream %s %s\n"), - branch->name, - remote->name, - branch->name); - if (branch->merge_nr != 1) - die(_("The current branch %s has multiple upstream branches, " - "refusing to push."), branch->name); - if (triangular) - die(_("You are pushing to remote '%s', which is not the upstream of\n" - "your current branch '%s', without telling me what to push\n" - "to update which remote branch."), - remote->name, branch->name); - - if (simple) { - /* Additional safety */ - if (strcmp(branch->refname, branch->merge[0]->src)) - die_push_simple(branch, remote); - } - - refspec_appendf(&rs, "%s:%s", branch->refname, branch->merge[0]->src); -} - -static void setup_push_current(struct remote *remote, struct branch *branch) -{ - if (!branch) - die(_(message_detached_head_die), remote->name); - refspec_appendf(&rs, "%s:%s", branch->refname, branch->refname); -} - -static int is_workflow_triangular(struct remote *remote) -{ - struct remote *fetch_remote = remote_get(NULL); - return (fetch_remote && fetch_remote != remote); -} - -static void setup_default_push_refspecs(struct remote *remote) -{ - struct branch *branch = branch_get(NULL); - int triangular = is_workflow_triangular(remote); - - switch (push_default) { - default: - case PUSH_DEFAULT_MATCHING: - refspec_append(&rs, ":"); - break; - - case PUSH_DEFAULT_UNSPECIFIED: - case PUSH_DEFAULT_SIMPLE: - if (triangular) - setup_push_current(remote, branch); - else - setup_push_upstream(remote, branch, triangular, 1); - break; - - case PUSH_DEFAULT_UPSTREAM: - setup_push_upstream(remote, branch, triangular, 0); - break; - - case PUSH_DEFAULT_CURRENT: - setup_push_current(remote, branch); - break; - - case PUSH_DEFAULT_NOTHING: - die(_("You didn't specify any refspecs to push, and " - "push.default is \"nothing\".")); - break; - } -} - -static const char message_advice_pull_before_push[] = - N_("Updates were rejected because the tip of your current branch is behind\n" - "its remote counterpart. Integrate the remote changes (e.g.\n" - "'git pull ...') before pushing again.\n" - "See the 'Note about fast-forwards' in 'git push --help' for details."); - -static const char message_advice_checkout_pull_push[] = - N_("Updates were rejected because a pushed branch tip is behind its remote\n" - "counterpart. Check out this branch and integrate the remote changes\n" - "(e.g. 'git pull ...') before pushing again.\n" - "See the 'Note about fast-forwards' in 'git push --help' for details."); - -static const char message_advice_ref_fetch_first[] = - N_("Updates were rejected because the remote contains work that you do\n" - "not have locally. This is usually caused by another repository pushing\n" - "to the same ref. You may want to first integrate the remote changes\n" - "(e.g., 'git pull ...') before pushing again.\n" - "See the 'Note about fast-forwards' in 'git push --help' for details."); - -static const char message_advice_ref_already_exists[] = - N_("Updates were rejected because the tag already exists in the remote."); - -static const char message_advice_ref_needs_force[] = - N_("You cannot update a remote ref that points at a non-commit object,\n" - "or update a remote ref to make it point at a non-commit object,\n" - "without using the '--force' option.\n"); - -static void advise_pull_before_push(void) -{ - if (!advice_push_non_ff_current || !advice_push_update_rejected) - return; - advise(_(message_advice_pull_before_push)); -} - -static void advise_checkout_pull_push(void) -{ - if (!advice_push_non_ff_matching || !advice_push_update_rejected) - return; - advise(_(message_advice_checkout_pull_push)); -} - -static void advise_ref_already_exists(void) -{ - if (!advice_push_already_exists || !advice_push_update_rejected) - return; - advise(_(message_advice_ref_already_exists)); -} - -static void advise_ref_fetch_first(void) -{ - if (!advice_push_fetch_first || !advice_push_update_rejected) - return; - advise(_(message_advice_ref_fetch_first)); -} - -static void advise_ref_needs_force(void) -{ - if (!advice_push_needs_force || !advice_push_update_rejected) - return; - advise(_(message_advice_ref_needs_force)); -} - -static int push_with_options(struct transport *transport, struct refspec *rs, - int flags) -{ - int err; - unsigned int reject_reasons; - char *anon_url = transport_anonymize_url(transport->url); - - transport_set_verbosity(transport, verbosity, progress); - transport->family = family; - - if (receivepack) - transport_set_option(transport, - TRANS_OPT_RECEIVEPACK, receivepack); - transport_set_option(transport, TRANS_OPT_THIN, thin ? "yes" : NULL); - - if (!is_empty_cas(&cas)) { - if (!transport->smart_options) - die("underlying transport does not support --%s option", - CAS_OPT_NAME); - transport->smart_options->cas = &cas; - } - - if (verbosity > 0) - fprintf(stderr, _("Pushing to %s\n"), anon_url); - trace2_region_enter("push", "transport_push", the_repository); - err = transport_push(the_repository, transport, - rs, flags, &reject_reasons); - trace2_region_leave("push", "transport_push", the_repository); - if (err != 0) { - fprintf(stderr, "%s", push_get_color(PUSH_COLOR_ERROR)); - error(_("failed to push some refs to '%s'"), anon_url); - fprintf(stderr, "%s", push_get_color(PUSH_COLOR_RESET)); - } - - err |= transport_disconnect(transport); - free(anon_url); - if (!err) - return 0; - - if (reject_reasons & REJECT_NON_FF_HEAD) { - advise_pull_before_push(); - } else if (reject_reasons & REJECT_NON_FF_OTHER) { - advise_checkout_pull_push(); - } else if (reject_reasons & REJECT_ALREADY_EXISTS) { - advise_ref_already_exists(); - } else if (reject_reasons & REJECT_FETCH_FIRST) { - advise_ref_fetch_first(); - } else if (reject_reasons & REJECT_NEEDS_FORCE) { - advise_ref_needs_force(); - } - - return 1; -} - -static int do_push(int flags, - const struct string_list *push_options, - struct remote *remote) -{ - int i, errs; - const char **url; - int url_nr; - struct refspec *push_refspec = &rs; - - if (push_options->nr) - flags |= TRANSPORT_PUSH_OPTIONS; - - if (!push_refspec->nr && !(flags & TRANSPORT_PUSH_ALL)) { - if (remote->push.nr) { - push_refspec = &remote->push; - } else if (!(flags & TRANSPORT_PUSH_MIRROR)) - setup_default_push_refspecs(remote); - } - errs = 0; - url_nr = push_url_of_remote(remote, &url); - if (url_nr) { - for (i = 0; i < url_nr; i++) { - struct transport *transport = - transport_get(remote, url[i]); - if (flags & TRANSPORT_PUSH_OPTIONS) - transport->push_options = push_options; - if (push_with_options(transport, push_refspec, flags)) - errs++; - } - } else { - struct transport *transport = - transport_get(remote, NULL); - if (flags & TRANSPORT_PUSH_OPTIONS) - transport->push_options = push_options; - if (push_with_options(transport, push_refspec, flags)) - errs++; - } - return !!errs; -} - -static int option_parse_recurse_submodules(const struct option *opt, - const char *arg, int unset) -{ - int *recurse_submodules = opt->value; - - if (unset) - *recurse_submodules = RECURSE_SUBMODULES_OFF; - else - *recurse_submodules = parse_push_recurse_submodules_arg(opt->long_name, arg); - - return 0; -} - -static void set_push_cert_flags(int *flags, int v) -{ - switch (v) { - case SEND_PACK_PUSH_CERT_NEVER: - *flags &= ~(TRANSPORT_PUSH_CERT_ALWAYS | TRANSPORT_PUSH_CERT_IF_ASKED); - break; - case SEND_PACK_PUSH_CERT_ALWAYS: - *flags |= TRANSPORT_PUSH_CERT_ALWAYS; - *flags &= ~TRANSPORT_PUSH_CERT_IF_ASKED; - break; - case SEND_PACK_PUSH_CERT_IF_ASKED: - *flags |= TRANSPORT_PUSH_CERT_IF_ASKED; - *flags &= ~TRANSPORT_PUSH_CERT_ALWAYS; - break; - } -} - - -static int git_push_config(const char *k, const char *v, void *cb) -{ - const char *slot_name; - int *flags = cb; - int status; - - status = git_gpg_config(k, v, NULL); - if (status) - return status; - - if (!strcmp(k, "push.followtags")) { - if (git_config_bool(k, v)) - *flags |= TRANSPORT_PUSH_FOLLOW_TAGS; - else - *flags &= ~TRANSPORT_PUSH_FOLLOW_TAGS; - return 0; - } else if (!strcmp(k, "push.gpgsign")) { - const char *value; - if (!git_config_get_value("push.gpgsign", &value)) { - switch (git_parse_maybe_bool(value)) { - case 0: - set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_NEVER); - break; - case 1: - set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_ALWAYS); - break; - default: - if (value && !strcasecmp(value, "if-asked")) - set_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED); - else - return error("Invalid value for '%s'", k); - } - } - } else if (!strcmp(k, "push.recursesubmodules")) { - const char *value; - if (!git_config_get_value("push.recursesubmodules", &value)) - recurse_submodules = parse_push_recurse_submodules_arg(k, value); - } else if (!strcmp(k, "submodule.recurse")) { - int val = git_config_bool(k, v) ? - RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF; - recurse_submodules = val; - } else if (!strcmp(k, "push.pushoption")) { - if (!v) - return config_error_nonbool(k); - else - if (!*v) - string_list_clear(&push_options_config, 0); - else - string_list_append(&push_options_config, v); - return 0; - } else if (!strcmp(k, "color.push")) { - push_use_color = git_config_colorbool(k, v); - return 0; - } else if (skip_prefix(k, "color.push.", &slot_name)) { - int slot = parse_push_color_slot(slot_name); - if (slot < 0) - return 0; - if (!v) - return config_error_nonbool(k); - return color_parse(v, push_colors[slot]); - } - - return git_default_config(k, v, NULL); -} - -int cmd_push(int argc, const char **argv, const char *prefix) -{ - int flags = 0; - int tags = 0; - int push_cert = -1; - int rc; - const char *repo = NULL; /* default repository */ - struct string_list push_options_cmdline = STRING_LIST_INIT_DUP; - struct string_list *push_options; - const struct string_list_item *item; - struct remote *remote; - - struct option options[] = { - OPT__VERBOSITY(&verbosity), - OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")), - OPT_BIT( 0 , "all", &flags, N_("push all refs"), TRANSPORT_PUSH_ALL), - OPT_BIT( 0 , "mirror", &flags, N_("mirror all refs"), - (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)), - OPT_BOOL('d', "delete", &deleterefs, N_("delete refs")), - OPT_BOOL( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")), - OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN), - OPT_BIT( 0, "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN), - OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE), - OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"), - N_("require old value of ref to be at this value"), - PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parseopt_push_cas_option), - OPT_CALLBACK(0, "recurse-submodules", &recurse_submodules, "(check|on-demand|no)", - N_("control recursive pushing of submodules"), option_parse_recurse_submodules), - OPT_BOOL_F( 0 , "thin", &thin, N_("use thin pack"), PARSE_OPT_NOCOMPLETE), - OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", N_("receive pack program")), - OPT_STRING( 0 , "exec", &receivepack, "receive-pack", N_("receive pack program")), - OPT_BIT('u', "set-upstream", &flags, N_("set upstream for git pull/status"), - TRANSPORT_PUSH_SET_UPSTREAM), - OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), - OPT_BIT(0, "prune", &flags, N_("prune locally removed refs"), - TRANSPORT_PUSH_PRUNE), - OPT_BIT(0, "no-verify", &flags, N_("bypass pre-push hook"), TRANSPORT_PUSH_NO_HOOK), - OPT_BIT(0, "follow-tags", &flags, N_("push missing but relevant tags"), - TRANSPORT_PUSH_FOLLOW_TAGS), - OPT_CALLBACK_F(0, "signed", &push_cert, "(yes|no|if-asked)", N_("GPG sign the push"), - PARSE_OPT_OPTARG, option_parse_push_signed), - OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC), - OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")), - OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), - TRANSPORT_FAMILY_IPV4), - OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), - TRANSPORT_FAMILY_IPV6), - OPT_END() - }; - - packet_trace_identity("push"); - git_config(git_push_config, &flags); - argc = parse_options(argc, argv, prefix, options, push_usage, 0); - push_options = (push_options_cmdline.nr - ? &push_options_cmdline - : &push_options_config); - set_push_cert_flags(&flags, push_cert); - - if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR)))) - die(_("--delete is incompatible with --all, --mirror and --tags")); - if (deleterefs && argc < 2) - die(_("--delete doesn't make sense without any refs")); - - if (recurse_submodules == RECURSE_SUBMODULES_CHECK) - flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK; - else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) - flags |= TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND; - else if (recurse_submodules == RECURSE_SUBMODULES_ONLY) - flags |= TRANSPORT_RECURSE_SUBMODULES_ONLY; - - if (tags) - refspec_append(&rs, "refs/tags/*"); - - if (argc > 0) { - repo = argv[0]; - set_refspecs(argv + 1, argc - 1, repo); - } - - remote = pushremote_get(repo); - if (!remote) { - if (repo) - die(_("bad repository '%s'"), repo); - die(_("No configured push destination.\n" - "Either specify the URL from the command-line or configure a remote repository using\n" - "\n" - " git remote add <name> <url>\n" - "\n" - "and then push using the remote name\n" - "\n" - " git push <name>\n")); - } - - if (remote->mirror) - flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE); - - if (flags & TRANSPORT_PUSH_ALL) { - if (tags) - die(_("--all and --tags are incompatible")); - if (argc >= 2) - die(_("--all can't be combined with refspecs")); - } - if (flags & TRANSPORT_PUSH_MIRROR) { - if (tags) - die(_("--mirror and --tags are incompatible")); - if (argc >= 2) - die(_("--mirror can't be combined with refspecs")); - } - if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR)) - die(_("--all and --mirror are incompatible")); - - for_each_string_list_item(item, push_options) - if (strchr(item->string, '\n')) - die(_("push options must not have new line characters")); - - rc = do_push(flags, push_options, remote); - string_list_clear(&push_options_cmdline, 0); - string_list_clear(&push_options_config, 0); - if (rc == -1) - usage_with_options(push_usage, options); - else - return rc; -} diff --git a/third_party/git/builtin/range-diff.c b/third_party/git/builtin/range-diff.c deleted file mode 100644 index 24c4162f7446..000000000000 --- a/third_party/git/builtin/range-diff.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "cache.h" -#include "builtin.h" -#include "parse-options.h" -#include "range-diff.h" -#include "config.h" - -static const char * const builtin_range_diff_usage[] = { -N_("git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>"), -N_("git range-diff [<options>] <old-tip>...<new-tip>"), -N_("git range-diff [<options>] <base> <old-tip> <new-tip>"), -NULL -}; - -int cmd_range_diff(int argc, const char **argv, const char *prefix) -{ - int creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT; - struct diff_options diffopt = { NULL }; - struct strvec other_arg = STRVEC_INIT; - int simple_color = -1; - struct option range_diff_options[] = { - OPT_INTEGER(0, "creation-factor", &creation_factor, - N_("Percentage by which creation is weighted")), - OPT_BOOL(0, "no-dual-color", &simple_color, - N_("use simple diff colors")), - OPT_PASSTHRU_ARGV(0, "notes", &other_arg, - N_("notes"), N_("passed to 'git log'"), - PARSE_OPT_OPTARG), - OPT_END() - }; - struct option *options; - int res = 0; - struct strbuf range1 = STRBUF_INIT, range2 = STRBUF_INIT; - - git_config(git_diff_ui_config, NULL); - - repo_diff_setup(the_repository, &diffopt); - - options = parse_options_concat(range_diff_options, diffopt.parseopts); - argc = parse_options(argc, argv, prefix, options, - builtin_range_diff_usage, 0); - - diff_setup_done(&diffopt); - - /* force color when --dual-color was used */ - if (!simple_color) - diffopt.use_color = 1; - - if (argc == 2) { - if (!strstr(argv[0], "..")) - die(_("no .. in range: '%s'"), argv[0]); - strbuf_addstr(&range1, argv[0]); - - if (!strstr(argv[1], "..")) - die(_("no .. in range: '%s'"), argv[1]); - strbuf_addstr(&range2, argv[1]); - } else if (argc == 3) { - strbuf_addf(&range1, "%s..%s", argv[0], argv[1]); - strbuf_addf(&range2, "%s..%s", argv[0], argv[2]); - } else if (argc == 1) { - const char *b = strstr(argv[0], "..."), *a = argv[0]; - int a_len; - - if (!b) { - error(_("single arg format must be symmetric range")); - usage_with_options(builtin_range_diff_usage, options); - } - - a_len = (int)(b - a); - if (!a_len) { - a = "HEAD"; - a_len = strlen(a); - } - b += 3; - if (!*b) - b = "HEAD"; - strbuf_addf(&range1, "%s..%.*s", b, a_len, a); - strbuf_addf(&range2, "%.*s..%s", a_len, a, b); - } else { - error(_("need two commit ranges")); - usage_with_options(builtin_range_diff_usage, options); - } - FREE_AND_NULL(options); - - res = show_range_diff(range1.buf, range2.buf, creation_factor, - simple_color < 1, &diffopt, &other_arg); - - strvec_clear(&other_arg); - strbuf_release(&range1); - strbuf_release(&range2); - - return res; -} diff --git a/third_party/git/builtin/read-tree.c b/third_party/git/builtin/read-tree.c deleted file mode 100644 index 485e7b047948..000000000000 --- a/third_party/git/builtin/read-tree.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * GIT - The information manager from hell - * - * Copyright (C) Linus Torvalds, 2005 - */ - -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "lockfile.h" -#include "object.h" -#include "tree.h" -#include "tree-walk.h" -#include "cache-tree.h" -#include "unpack-trees.h" -#include "dir.h" -#include "builtin.h" -#include "parse-options.h" -#include "resolve-undo.h" -#include "submodule.h" -#include "submodule-config.h" - -static int nr_trees; -static int read_empty; -static struct tree *trees[MAX_UNPACK_TREES]; - -static int list_tree(struct object_id *oid) -{ - struct tree *tree; - - if (nr_trees >= MAX_UNPACK_TREES) - die("I cannot read more than %d trees", MAX_UNPACK_TREES); - tree = parse_tree_indirect(oid); - if (!tree) - return -1; - trees[nr_trees++] = tree; - return 0; -} - -static const char * const read_tree_usage[] = { - N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"), - NULL -}; - -static int index_output_cb(const struct option *opt, const char *arg, - int unset) -{ - BUG_ON_OPT_NEG(unset); - set_alternate_index_output(arg); - return 0; -} - -static int exclude_per_directory_cb(const struct option *opt, const char *arg, - int unset) -{ - struct dir_struct *dir; - struct unpack_trees_options *opts; - - BUG_ON_OPT_NEG(unset); - - opts = (struct unpack_trees_options *)opt->value; - - if (opts->dir) - die("more than one --exclude-per-directory given."); - - dir = xcalloc(1, sizeof(*opts->dir)); - dir->flags |= DIR_SHOW_IGNORED; - dir->exclude_per_dir = arg; - opts->dir = dir; - /* We do not need to nor want to do read-directory - * here; we are merely interested in reusing the - * per directory ignore stack mechanism. - */ - return 0; -} - -static void debug_stage(const char *label, const struct cache_entry *ce, - struct unpack_trees_options *o) -{ - printf("%s ", label); - if (!ce) - printf("(missing)\n"); - else if (ce == o->df_conflict_entry) - printf("(conflict)\n"); - else - printf("%06o #%d %s %.8s\n", - ce->ce_mode, ce_stage(ce), ce->name, - oid_to_hex(&ce->oid)); -} - -static int debug_merge(const struct cache_entry * const *stages, - struct unpack_trees_options *o) -{ - int i; - - printf("* %d-way merge\n", o->merge_size); - debug_stage("index", stages[0], o); - for (i = 1; i <= o->merge_size; i++) { - char buf[24]; - xsnprintf(buf, sizeof(buf), "ent#%d", i); - debug_stage(buf, stages[i], o); - } - return 0; -} - -static int git_read_tree_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "submodule.recurse")) - return git_default_submodule_config(var, value, cb); - - return git_default_config(var, value, cb); -} - -int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix) -{ - int i, stage = 0; - struct object_id oid; - struct tree_desc t[MAX_UNPACK_TREES]; - struct unpack_trees_options opts; - int prefix_set = 0; - struct lock_file lock_file = LOCK_INIT; - const struct option read_tree_options[] = { - OPT_CALLBACK_F(0, "index-output", NULL, N_("file"), - N_("write resulting index to <file>"), - PARSE_OPT_NONEG, index_output_cb), - OPT_BOOL(0, "empty", &read_empty, - N_("only empty the index")), - OPT__VERBOSE(&opts.verbose_update, N_("be verbose")), - OPT_GROUP(N_("Merging")), - OPT_BOOL('m', NULL, &opts.merge, - N_("perform a merge in addition to a read")), - OPT_BOOL(0, "trivial", &opts.trivial_merges_only, - N_("3-way merge if no file level merging required")), - OPT_BOOL(0, "aggressive", &opts.aggressive, - N_("3-way merge in presence of adds and removes")), - OPT_BOOL(0, "reset", &opts.reset, - N_("same as -m, but discard unmerged entries")), - { OPTION_STRING, 0, "prefix", &opts.prefix, N_("<subdirectory>/"), - N_("read the tree into the index under <subdirectory>/"), - PARSE_OPT_NONEG }, - OPT_BOOL('u', NULL, &opts.update, - N_("update working tree with merge result")), - OPT_CALLBACK_F(0, "exclude-per-directory", &opts, - N_("gitignore"), - N_("allow explicitly ignored files to be overwritten"), - PARSE_OPT_NONEG, exclude_per_directory_cb), - OPT_BOOL('i', NULL, &opts.index_only, - N_("don't check the working tree after merging")), - OPT__DRY_RUN(&opts.dry_run, N_("don't update the index or the work tree")), - OPT_BOOL(0, "no-sparse-checkout", &opts.skip_sparse_checkout, - N_("skip applying sparse checkout filter")), - OPT_BOOL(0, "debug-unpack", &opts.debug_unpack, - N_("debug unpack-trees")), - OPT_CALLBACK_F(0, "recurse-submodules", NULL, - "checkout", "control recursive updating of submodules", - PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), - OPT__QUIET(&opts.quiet, N_("suppress feedback messages")), - OPT_END() - }; - - memset(&opts, 0, sizeof(opts)); - opts.head_idx = -1; - opts.src_index = &the_index; - opts.dst_index = &the_index; - - git_config(git_read_tree_config, NULL); - - argc = parse_options(argc, argv, cmd_prefix, read_tree_options, - read_tree_usage, 0); - - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); - - prefix_set = opts.prefix ? 1 : 0; - if (1 < opts.merge + opts.reset + prefix_set) - die("Which one? -m, --reset, or --prefix?"); - - /* - * NEEDSWORK - * - * The old index should be read anyway even if we're going to - * destroy all index entries because we still need to preserve - * certain information such as index version or split-index - * mode. - */ - - if (opts.reset || opts.merge || opts.prefix) { - if (read_cache_unmerged() && (opts.prefix || opts.merge)) - die(_("You need to resolve your current index first")); - stage = opts.merge = 1; - } - resolve_undo_clear(); - - for (i = 0; i < argc; i++) { - const char *arg = argv[i]; - - if (get_oid(arg, &oid)) - die("Not a valid object name %s", arg); - if (list_tree(&oid) < 0) - die("failed to unpack tree object %s", arg); - stage++; - } - if (!nr_trees && !read_empty && !opts.merge) - warning("read-tree: emptying the index with no arguments is deprecated; use --empty"); - else if (nr_trees > 0 && read_empty) - die("passing trees as arguments contradicts --empty"); - - if (1 < opts.index_only + opts.update) - die("-u and -i at the same time makes no sense"); - if ((opts.update || opts.index_only) && !opts.merge) - die("%s is meaningless without -m, --reset, or --prefix", - opts.update ? "-u" : "-i"); - if ((opts.dir && !opts.update)) - die("--exclude-per-directory is meaningless unless -u"); - if (opts.merge && !opts.index_only) - setup_work_tree(); - - if (opts.merge) { - switch (stage - 1) { - case 0: - die("you must specify at least one tree to merge"); - break; - case 1: - opts.fn = opts.prefix ? bind_merge : oneway_merge; - break; - case 2: - opts.fn = twoway_merge; - opts.initial_checkout = is_cache_unborn(); - break; - case 3: - default: - opts.fn = threeway_merge; - break; - } - - if (stage - 1 >= 3) - opts.head_idx = stage - 2; - else - opts.head_idx = 1; - } - - if (opts.debug_unpack) - opts.fn = debug_merge; - - cache_tree_free(&active_cache_tree); - for (i = 0; i < nr_trees; i++) { - struct tree *tree = trees[i]; - parse_tree(tree); - init_tree_desc(t+i, tree->buffer, tree->size); - } - if (unpack_trees(nr_trees, t, &opts)) - return 128; - - if (opts.debug_unpack || opts.dry_run) - return 0; /* do not write the index out */ - - /* - * When reading only one tree (either the most basic form, - * "-m ent" or "--reset ent" form), we can obtain a fully - * valid cache-tree because the index must match exactly - * what came from the tree. - */ - if (nr_trees == 1 && !opts.prefix) - prime_cache_tree(the_repository, - the_repository->index, - trees[0]); - - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) - die("unable to write new index file"); - return 0; -} diff --git a/third_party/git/builtin/rebase.c b/third_party/git/builtin/rebase.c deleted file mode 100644 index eeca53382f79..000000000000 --- a/third_party/git/builtin/rebase.c +++ /dev/null @@ -1,2105 +0,0 @@ -/* - * "git rebase" builtin command - * - * Copyright (c) 2018 Pratik Karki - */ - -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "run-command.h" -#include "exec-cmd.h" -#include "strvec.h" -#include "dir.h" -#include "packfile.h" -#include "refs.h" -#include "quote.h" -#include "config.h" -#include "cache-tree.h" -#include "unpack-trees.h" -#include "lockfile.h" -#include "parse-options.h" -#include "commit.h" -#include "diff.h" -#include "wt-status.h" -#include "revision.h" -#include "commit-reach.h" -#include "rerere.h" -#include "branch.h" -#include "sequencer.h" -#include "rebase-interactive.h" -#include "reset.h" - -#define DEFAULT_REFLOG_ACTION "rebase" - -static char const * const builtin_rebase_usage[] = { - N_("git rebase [-i] [options] [--exec <cmd>] " - "[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"), - N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] " - "--root [<branch>]"), - N_("git rebase --continue | --abort | --skip | --edit-todo"), - NULL -}; - -static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto") -static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive") -static GIT_PATH_FUNC(apply_dir, "rebase-apply") -static GIT_PATH_FUNC(merge_dir, "rebase-merge") - -enum rebase_type { - REBASE_UNSPECIFIED = -1, - REBASE_APPLY, - REBASE_MERGE, - REBASE_PRESERVE_MERGES -}; - -enum empty_type { - EMPTY_UNSPECIFIED = -1, - EMPTY_DROP, - EMPTY_KEEP, - EMPTY_ASK -}; - -struct rebase_options { - enum rebase_type type; - enum empty_type empty; - const char *default_backend; - const char *state_dir; - struct commit *upstream; - const char *upstream_name; - const char *upstream_arg; - char *head_name; - struct object_id orig_head; - struct commit *onto; - const char *onto_name; - const char *revisions; - const char *switch_to; - int root, root_with_onto; - struct object_id *squash_onto; - struct commit *restrict_revision; - int dont_finish_rebase; - enum { - REBASE_NO_QUIET = 1<<0, - REBASE_VERBOSE = 1<<1, - REBASE_DIFFSTAT = 1<<2, - REBASE_FORCE = 1<<3, - REBASE_INTERACTIVE_EXPLICIT = 1<<4, - } flags; - struct strvec git_am_opts; - const char *action; - int signoff; - int allow_rerere_autoupdate; - int keep_empty; - int autosquash; - char *gpg_sign_opt; - int autostash; - int committer_date_is_author_date; - int ignore_date; - char *cmd; - int allow_empty_message; - int rebase_merges, rebase_cousins; - char *strategy, *strategy_opts; - struct strbuf git_format_patch_opt; - int reschedule_failed_exec; - int use_legacy_rebase; - int reapply_cherry_picks; -}; - -#define REBASE_OPTIONS_INIT { \ - .type = REBASE_UNSPECIFIED, \ - .empty = EMPTY_UNSPECIFIED, \ - .keep_empty = 1, \ - .default_backend = "merge", \ - .flags = REBASE_NO_QUIET, \ - .git_am_opts = STRVEC_INIT, \ - .git_format_patch_opt = STRBUF_INIT \ - } - -static struct replay_opts get_replay_opts(const struct rebase_options *opts) -{ - struct replay_opts replay = REPLAY_OPTS_INIT; - - replay.action = REPLAY_INTERACTIVE_REBASE; - sequencer_init_config(&replay); - - replay.signoff = opts->signoff; - replay.allow_ff = !(opts->flags & REBASE_FORCE); - if (opts->allow_rerere_autoupdate) - replay.allow_rerere_auto = opts->allow_rerere_autoupdate; - replay.allow_empty = 1; - replay.allow_empty_message = opts->allow_empty_message; - replay.drop_redundant_commits = (opts->empty == EMPTY_DROP); - replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP); - replay.quiet = !(opts->flags & REBASE_NO_QUIET); - replay.verbose = opts->flags & REBASE_VERBOSE; - replay.reschedule_failed_exec = opts->reschedule_failed_exec; - replay.committer_date_is_author_date = - opts->committer_date_is_author_date; - replay.ignore_date = opts->ignore_date; - replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt); - replay.strategy = opts->strategy; - - if (opts->strategy_opts) - parse_strategy_opts(&replay, opts->strategy_opts); - - if (opts->squash_onto) { - oidcpy(&replay.squash_onto, opts->squash_onto); - replay.have_squash_onto = 1; - } - - return replay; -} - -enum action { - ACTION_NONE = 0, - ACTION_CONTINUE, - ACTION_SKIP, - ACTION_ABORT, - ACTION_QUIT, - ACTION_EDIT_TODO, - ACTION_SHOW_CURRENT_PATCH, - ACTION_SHORTEN_OIDS, - ACTION_EXPAND_OIDS, - ACTION_CHECK_TODO_LIST, - ACTION_REARRANGE_SQUASH, - ACTION_ADD_EXEC -}; - -static const char *action_names[] = { "undefined", - "continue", - "skip", - "abort", - "quit", - "edit_todo", - "show_current_patch" }; - -static int add_exec_commands(struct string_list *commands) -{ - const char *todo_file = rebase_path_todo(); - struct todo_list todo_list = TODO_LIST_INIT; - int res; - - if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) - return error_errno(_("could not read '%s'."), todo_file); - - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, - &todo_list)) { - todo_list_release(&todo_list); - return error(_("unusable todo list: '%s'"), todo_file); - } - - todo_list_add_exec_commands(&todo_list, commands); - res = todo_list_write_to_file(the_repository, &todo_list, - todo_file, NULL, NULL, -1, 0); - todo_list_release(&todo_list); - - if (res) - return error_errno(_("could not write '%s'."), todo_file); - return 0; -} - -static int rearrange_squash_in_todo_file(void) -{ - const char *todo_file = rebase_path_todo(); - struct todo_list todo_list = TODO_LIST_INIT; - int res = 0; - - if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) - return error_errno(_("could not read '%s'."), todo_file); - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, - &todo_list)) { - todo_list_release(&todo_list); - return error(_("unusable todo list: '%s'"), todo_file); - } - - res = todo_list_rearrange_squash(&todo_list); - if (!res) - res = todo_list_write_to_file(the_repository, &todo_list, - todo_file, NULL, NULL, -1, 0); - - todo_list_release(&todo_list); - - if (res) - return error_errno(_("could not write '%s'."), todo_file); - return 0; -} - -static int transform_todo_file(unsigned flags) -{ - const char *todo_file = rebase_path_todo(); - struct todo_list todo_list = TODO_LIST_INIT; - int res; - - if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) - return error_errno(_("could not read '%s'."), todo_file); - - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, - &todo_list)) { - todo_list_release(&todo_list); - return error(_("unusable todo list: '%s'"), todo_file); - } - - res = todo_list_write_to_file(the_repository, &todo_list, todo_file, - NULL, NULL, -1, flags); - todo_list_release(&todo_list); - - if (res) - return error_errno(_("could not write '%s'."), todo_file); - return 0; -} - -static int edit_todo_file(unsigned flags) -{ - const char *todo_file = rebase_path_todo(); - struct todo_list todo_list = TODO_LIST_INIT, - new_todo = TODO_LIST_INIT; - int res = 0; - - if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) - return error_errno(_("could not read '%s'."), todo_file); - - strbuf_stripspace(&todo_list.buf, 1); - res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags); - if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file, - NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS))) - res = error_errno(_("could not write '%s'"), todo_file); - - todo_list_release(&todo_list); - todo_list_release(&new_todo); - - return res; -} - -static int get_revision_ranges(struct commit *upstream, struct commit *onto, - struct object_id *orig_head, const char **head_hash, - char **revisions, char **shortrevisions) -{ - struct commit *base_rev = upstream ? upstream : onto; - const char *shorthead; - - *head_hash = find_unique_abbrev(orig_head, GIT_MAX_HEXSZ); - *revisions = xstrfmt("%s...%s", oid_to_hex(&base_rev->object.oid), - *head_hash); - - shorthead = find_unique_abbrev(orig_head, DEFAULT_ABBREV); - - if (upstream) { - const char *shortrev; - - shortrev = find_unique_abbrev(&base_rev->object.oid, - DEFAULT_ABBREV); - - *shortrevisions = xstrfmt("%s..%s", shortrev, shorthead); - } else - *shortrevisions = xstrdup(shorthead); - - return 0; -} - -static int init_basic_state(struct replay_opts *opts, const char *head_name, - struct commit *onto, const char *orig_head) -{ - FILE *interactive; - - if (!is_directory(merge_dir()) && mkdir_in_gitdir(merge_dir())) - return error_errno(_("could not create temporary %s"), merge_dir()); - - delete_reflog("REBASE_HEAD"); - - interactive = fopen(path_interactive(), "w"); - if (!interactive) - return error_errno(_("could not mark as interactive")); - fclose(interactive); - - return write_basic_state(opts, head_name, onto, orig_head); -} - -static void split_exec_commands(const char *cmd, struct string_list *commands) -{ - if (cmd && *cmd) { - string_list_split(commands, cmd, '\n', -1); - - /* rebase.c adds a new line to cmd after every command, - * so here the last command is always empty */ - string_list_remove_empty_items(commands, 0); - } -} - -static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) -{ - int ret; - const char *head_hash = NULL; - char *revisions = NULL, *shortrevisions = NULL; - struct strvec make_script_args = STRVEC_INIT; - struct todo_list todo_list = TODO_LIST_INIT; - struct replay_opts replay = get_replay_opts(opts); - struct string_list commands = STRING_LIST_INIT_DUP; - - if (get_revision_ranges(opts->upstream, opts->onto, &opts->orig_head, - &head_hash, &revisions, &shortrevisions)) - return -1; - - if (init_basic_state(&replay, - opts->head_name ? opts->head_name : "detached HEAD", - opts->onto, head_hash)) { - free(revisions); - free(shortrevisions); - - return -1; - } - - if (!opts->upstream && opts->squash_onto) - write_file(path_squash_onto(), "%s\n", - oid_to_hex(opts->squash_onto)); - - strvec_pushl(&make_script_args, "", revisions, NULL); - if (opts->restrict_revision) - strvec_pushf(&make_script_args, "^%s", - oid_to_hex(&opts->restrict_revision->object.oid)); - - ret = sequencer_make_script(the_repository, &todo_list.buf, - make_script_args.nr, make_script_args.v, - flags); - - if (ret) - error(_("could not generate todo list")); - else { - discard_cache(); - if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf, - &todo_list)) - BUG("unusable todo list"); - - split_exec_commands(opts->cmd, &commands); - ret = complete_action(the_repository, &replay, flags, - shortrevisions, opts->onto_name, opts->onto, head_hash, - &commands, opts->autosquash, &todo_list); - } - - string_list_clear(&commands, 0); - free(revisions); - free(shortrevisions); - todo_list_release(&todo_list); - strvec_clear(&make_script_args); - - return ret; -} - -static int run_sequencer_rebase(struct rebase_options *opts, - enum action command) -{ - unsigned flags = 0; - int abbreviate_commands = 0, ret = 0; - - git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands); - - flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0; - flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; - flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0; - flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0; - flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0; - flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0; - flags |= opts->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0; - - switch (command) { - case ACTION_NONE: { - if (!opts->onto && !opts->upstream) - die(_("a base commit must be provided with --upstream or --onto")); - - ret = do_interactive_rebase(opts, flags); - break; - } - case ACTION_SKIP: { - struct string_list merge_rr = STRING_LIST_INIT_DUP; - - rerere_clear(the_repository, &merge_rr); - } - /* fallthrough */ - case ACTION_CONTINUE: { - struct replay_opts replay_opts = get_replay_opts(opts); - - ret = sequencer_continue(the_repository, &replay_opts); - break; - } - case ACTION_EDIT_TODO: - ret = edit_todo_file(flags); - break; - case ACTION_SHOW_CURRENT_PATCH: { - struct child_process cmd = CHILD_PROCESS_INIT; - - cmd.git_cmd = 1; - strvec_pushl(&cmd.args, "show", "REBASE_HEAD", "--", NULL); - ret = run_command(&cmd); - - break; - } - case ACTION_SHORTEN_OIDS: - case ACTION_EXPAND_OIDS: - ret = transform_todo_file(flags); - break; - case ACTION_CHECK_TODO_LIST: - ret = check_todo_list_from_file(the_repository); - break; - case ACTION_REARRANGE_SQUASH: - ret = rearrange_squash_in_todo_file(); - break; - case ACTION_ADD_EXEC: { - struct string_list commands = STRING_LIST_INIT_DUP; - - split_exec_commands(opts->cmd, &commands); - ret = add_exec_commands(&commands); - string_list_clear(&commands, 0); - break; - } - default: - BUG("invalid command '%d'", command); - } - - return ret; -} - -static void imply_merge(struct rebase_options *opts, const char *option); -static int parse_opt_keep_empty(const struct option *opt, const char *arg, - int unset) -{ - struct rebase_options *opts = opt->value; - - BUG_ON_OPT_ARG(arg); - - imply_merge(opts, unset ? "--no-keep-empty" : "--keep-empty"); - opts->keep_empty = !unset; - opts->type = REBASE_MERGE; - return 0; -} - -static const char * const builtin_rebase_interactive_usage[] = { - N_("git rebase--interactive [<options>]"), - NULL -}; - -int cmd_rebase__interactive(int argc, const char **argv, const char *prefix) -{ - struct rebase_options opts = REBASE_OPTIONS_INIT; - struct object_id squash_onto = null_oid; - enum action command = ACTION_NONE; - struct option options[] = { - OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"), - REBASE_FORCE), - OPT_CALLBACK_F('k', "keep-empty", &options, NULL, - N_("keep commits which start empty"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, - parse_opt_keep_empty), - OPT_BOOL_F(0, "allow-empty-message", &opts.allow_empty_message, - N_("allow commits with empty messages"), - PARSE_OPT_HIDDEN), - OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")), - OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins, - N_("keep original branch points of cousins")), - OPT_BOOL(0, "autosquash", &opts.autosquash, - N_("move commits that begin with squash!/fixup!")), - OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")), - OPT_BIT('v', "verbose", &opts.flags, - N_("display a diffstat of what changed upstream"), - REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT), - OPT_CMDMODE(0, "continue", &command, N_("continue rebase"), - ACTION_CONTINUE), - OPT_CMDMODE(0, "skip", &command, N_("skip commit"), ACTION_SKIP), - OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"), - ACTION_EDIT_TODO), - OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"), - ACTION_SHOW_CURRENT_PATCH), - OPT_CMDMODE(0, "shorten-ids", &command, - N_("shorten commit ids in the todo list"), ACTION_SHORTEN_OIDS), - OPT_CMDMODE(0, "expand-ids", &command, - N_("expand commit ids in the todo list"), ACTION_EXPAND_OIDS), - OPT_CMDMODE(0, "check-todo-list", &command, - N_("check the todo list"), ACTION_CHECK_TODO_LIST), - OPT_CMDMODE(0, "rearrange-squash", &command, - N_("rearrange fixup/squash lines"), ACTION_REARRANGE_SQUASH), - OPT_CMDMODE(0, "add-exec-commands", &command, - N_("insert exec commands in todo list"), ACTION_ADD_EXEC), - { OPTION_CALLBACK, 0, "onto", &opts.onto, N_("onto"), N_("onto"), - PARSE_OPT_NONEG, parse_opt_commit, 0 }, - { OPTION_CALLBACK, 0, "restrict-revision", &opts.restrict_revision, - N_("restrict-revision"), N_("restrict revision"), - PARSE_OPT_NONEG, parse_opt_commit, 0 }, - { OPTION_CALLBACK, 0, "squash-onto", &squash_onto, N_("squash-onto"), - N_("squash onto"), PARSE_OPT_NONEG, parse_opt_object_id, 0 }, - { OPTION_CALLBACK, 0, "upstream", &opts.upstream, N_("upstream"), - N_("the upstream commit"), PARSE_OPT_NONEG, parse_opt_commit, - 0 }, - OPT_STRING(0, "head-name", &opts.head_name, N_("head-name"), N_("head name")), - { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"), - N_("GPG-sign commits"), - PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"), - N_("rebase strategy")), - OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"), - N_("strategy options")), - OPT_STRING(0, "switch-to", &opts.switch_to, N_("switch-to"), - N_("the branch or commit to checkout")), - OPT_STRING(0, "onto-name", &opts.onto_name, N_("onto-name"), N_("onto name")), - OPT_STRING(0, "cmd", &opts.cmd, N_("cmd"), N_("the command to run")), - OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_autoupdate), - OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec, - N_("automatically re-schedule any `exec` that fails")), - OPT_END() - }; - - opts.rebase_cousins = -1; - - if (argc == 1) - usage_with_options(builtin_rebase_interactive_usage, options); - - argc = parse_options(argc, argv, prefix, options, - builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0); - - if (!is_null_oid(&squash_onto)) - opts.squash_onto = &squash_onto; - - if (opts.rebase_cousins >= 0 && !opts.rebase_merges) - warning(_("--[no-]rebase-cousins has no effect without " - "--rebase-merges")); - - return !!run_sequencer_rebase(&opts, command); -} - -static int is_merge(struct rebase_options *opts) -{ - return opts->type == REBASE_MERGE || - opts->type == REBASE_PRESERVE_MERGES; -} - -static void imply_merge(struct rebase_options *opts, const char *option) -{ - switch (opts->type) { - case REBASE_APPLY: - die(_("%s requires the merge backend"), option); - break; - case REBASE_MERGE: - case REBASE_PRESERVE_MERGES: - break; - default: - opts->type = REBASE_MERGE; /* implied */ - break; - } -} - -/* Returns the filename prefixed by the state_dir */ -static const char *state_dir_path(const char *filename, struct rebase_options *opts) -{ - static struct strbuf path = STRBUF_INIT; - static size_t prefix_len; - - if (!prefix_len) { - strbuf_addf(&path, "%s/", opts->state_dir); - prefix_len = path.len; - } - - strbuf_setlen(&path, prefix_len); - strbuf_addstr(&path, filename); - return path.buf; -} - -/* Initialize the rebase options from the state directory. */ -static int read_basic_state(struct rebase_options *opts) -{ - struct strbuf head_name = STRBUF_INIT; - struct strbuf buf = STRBUF_INIT; - struct object_id oid; - - if (!read_oneliner(&head_name, state_dir_path("head-name", opts), - READ_ONELINER_WARN_MISSING) || - !read_oneliner(&buf, state_dir_path("onto", opts), - READ_ONELINER_WARN_MISSING)) - return -1; - opts->head_name = starts_with(head_name.buf, "refs/") ? - xstrdup(head_name.buf) : NULL; - strbuf_release(&head_name); - if (get_oid(buf.buf, &oid)) - return error(_("could not get 'onto': '%s'"), buf.buf); - opts->onto = lookup_commit_or_die(&oid, buf.buf); - - /* - * We always write to orig-head, but interactive rebase used to write to - * head. Fall back to reading from head to cover for the case that the - * user upgraded git with an ongoing interactive rebase. - */ - strbuf_reset(&buf); - if (file_exists(state_dir_path("orig-head", opts))) { - if (!read_oneliner(&buf, state_dir_path("orig-head", opts), - READ_ONELINER_WARN_MISSING)) - return -1; - } else if (!read_oneliner(&buf, state_dir_path("head", opts), - READ_ONELINER_WARN_MISSING)) - return -1; - if (get_oid(buf.buf, &opts->orig_head)) - return error(_("invalid orig-head: '%s'"), buf.buf); - - if (file_exists(state_dir_path("quiet", opts))) - opts->flags &= ~REBASE_NO_QUIET; - else - opts->flags |= REBASE_NO_QUIET; - - if (file_exists(state_dir_path("verbose", opts))) - opts->flags |= REBASE_VERBOSE; - - if (file_exists(state_dir_path("signoff", opts))) { - opts->signoff = 1; - opts->flags |= REBASE_FORCE; - } - - if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) { - strbuf_reset(&buf); - if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts), - READ_ONELINER_WARN_MISSING)) - return -1; - if (!strcmp(buf.buf, "--rerere-autoupdate")) - opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE; - else if (!strcmp(buf.buf, "--no-rerere-autoupdate")) - opts->allow_rerere_autoupdate = RERERE_NOAUTOUPDATE; - else - warning(_("ignoring invalid allow_rerere_autoupdate: " - "'%s'"), buf.buf); - } - - if (file_exists(state_dir_path("gpg_sign_opt", opts))) { - strbuf_reset(&buf); - if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts), - READ_ONELINER_WARN_MISSING)) - return -1; - free(opts->gpg_sign_opt); - opts->gpg_sign_opt = xstrdup(buf.buf); - } - - if (file_exists(state_dir_path("strategy", opts))) { - strbuf_reset(&buf); - if (!read_oneliner(&buf, state_dir_path("strategy", opts), - READ_ONELINER_WARN_MISSING)) - return -1; - free(opts->strategy); - opts->strategy = xstrdup(buf.buf); - } - - if (file_exists(state_dir_path("strategy_opts", opts))) { - strbuf_reset(&buf); - if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts), - READ_ONELINER_WARN_MISSING)) - return -1; - free(opts->strategy_opts); - opts->strategy_opts = xstrdup(buf.buf); - } - - strbuf_release(&buf); - - return 0; -} - -static int rebase_write_basic_state(struct rebase_options *opts) -{ - write_file(state_dir_path("head-name", opts), "%s", - opts->head_name ? opts->head_name : "detached HEAD"); - write_file(state_dir_path("onto", opts), "%s", - opts->onto ? oid_to_hex(&opts->onto->object.oid) : ""); - write_file(state_dir_path("orig-head", opts), "%s", - oid_to_hex(&opts->orig_head)); - if (!(opts->flags & REBASE_NO_QUIET)) - write_file(state_dir_path("quiet", opts), "%s", ""); - if (opts->flags & REBASE_VERBOSE) - write_file(state_dir_path("verbose", opts), "%s", ""); - if (opts->strategy) - write_file(state_dir_path("strategy", opts), "%s", - opts->strategy); - if (opts->strategy_opts) - write_file(state_dir_path("strategy_opts", opts), "%s", - opts->strategy_opts); - if (opts->allow_rerere_autoupdate > 0) - write_file(state_dir_path("allow_rerere_autoupdate", opts), - "-%s-rerere-autoupdate", - opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ? - "" : "-no"); - if (opts->gpg_sign_opt) - write_file(state_dir_path("gpg_sign_opt", opts), "%s", - opts->gpg_sign_opt); - if (opts->signoff) - write_file(state_dir_path("signoff", opts), "--signoff"); - - return 0; -} - -static int finish_rebase(struct rebase_options *opts) -{ - struct strbuf dir = STRBUF_INIT; - int ret = 0; - - delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); - apply_autostash(state_dir_path("autostash", opts)); - close_object_store(the_repository->objects); - /* - * We ignore errors in 'git maintenance run --auto', since the - * user should see them. - */ - run_auto_maintenance(!(opts->flags & (REBASE_NO_QUIET|REBASE_VERBOSE))); - if (opts->type == REBASE_MERGE) { - struct replay_opts replay = REPLAY_OPTS_INIT; - - replay.action = REPLAY_INTERACTIVE_REBASE; - ret = sequencer_remove_state(&replay); - } else { - strbuf_addstr(&dir, opts->state_dir); - if (remove_dir_recursively(&dir, 0)) - ret = error(_("could not remove '%s'"), - opts->state_dir); - strbuf_release(&dir); - } - - return ret; -} - -static struct commit *peel_committish(const char *name) -{ - struct object *obj; - struct object_id oid; - - if (get_oid(name, &oid)) - return NULL; - obj = parse_object(the_repository, &oid); - return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT); -} - -static void add_var(struct strbuf *buf, const char *name, const char *value) -{ - if (!value) - strbuf_addf(buf, "unset %s; ", name); - else { - strbuf_addf(buf, "%s=", name); - sq_quote_buf(buf, value); - strbuf_addstr(buf, "; "); - } -} - -static int move_to_original_branch(struct rebase_options *opts) -{ - struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT; - int ret; - - if (!opts->head_name) - return 0; /* nothing to move back to */ - - if (!opts->onto) - BUG("move_to_original_branch without onto"); - - strbuf_addf(&orig_head_reflog, "rebase finished: %s onto %s", - opts->head_name, oid_to_hex(&opts->onto->object.oid)); - strbuf_addf(&head_reflog, "rebase finished: returning to %s", - opts->head_name); - ret = reset_head(the_repository, NULL, "", opts->head_name, - RESET_HEAD_REFS_ONLY, - orig_head_reflog.buf, head_reflog.buf, - DEFAULT_REFLOG_ACTION); - - strbuf_release(&orig_head_reflog); - strbuf_release(&head_reflog); - return ret; -} - -static const char *resolvemsg = -N_("Resolve all conflicts manually, mark them as resolved with\n" -"\"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n" -"You can instead skip this commit: run \"git rebase --skip\".\n" -"To abort and get back to the state before \"git rebase\", run " -"\"git rebase --abort\"."); - -static int run_am(struct rebase_options *opts) -{ - struct child_process am = CHILD_PROCESS_INIT; - struct child_process format_patch = CHILD_PROCESS_INIT; - struct strbuf revisions = STRBUF_INIT; - int status; - char *rebased_patches; - - am.git_cmd = 1; - strvec_push(&am.args, "am"); - - if (opts->action && !strcmp("continue", opts->action)) { - strvec_push(&am.args, "--resolved"); - strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg); - if (opts->gpg_sign_opt) - strvec_push(&am.args, opts->gpg_sign_opt); - status = run_command(&am); - if (status) - return status; - - return move_to_original_branch(opts); - } - if (opts->action && !strcmp("skip", opts->action)) { - strvec_push(&am.args, "--skip"); - strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg); - status = run_command(&am); - if (status) - return status; - - return move_to_original_branch(opts); - } - if (opts->action && !strcmp("show-current-patch", opts->action)) { - strvec_push(&am.args, "--show-current-patch"); - return run_command(&am); - } - - strbuf_addf(&revisions, "%s...%s", - oid_to_hex(opts->root ? - /* this is now equivalent to !opts->upstream */ - &opts->onto->object.oid : - &opts->upstream->object.oid), - oid_to_hex(&opts->orig_head)); - - rebased_patches = xstrdup(git_path("rebased-patches")); - format_patch.out = open(rebased_patches, - O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (format_patch.out < 0) { - status = error_errno(_("could not open '%s' for writing"), - rebased_patches); - free(rebased_patches); - strvec_clear(&am.args); - return status; - } - - format_patch.git_cmd = 1; - strvec_pushl(&format_patch.args, "format-patch", "-k", "--stdout", - "--full-index", "--cherry-pick", "--right-only", - "--src-prefix=a/", "--dst-prefix=b/", "--no-renames", - "--no-cover-letter", "--pretty=mboxrd", "--topo-order", - "--no-base", NULL); - if (opts->git_format_patch_opt.len) - strvec_split(&format_patch.args, - opts->git_format_patch_opt.buf); - strvec_push(&format_patch.args, revisions.buf); - if (opts->restrict_revision) - strvec_pushf(&format_patch.args, "^%s", - oid_to_hex(&opts->restrict_revision->object.oid)); - - status = run_command(&format_patch); - if (status) { - unlink(rebased_patches); - free(rebased_patches); - strvec_clear(&am.args); - - reset_head(the_repository, &opts->orig_head, "checkout", - opts->head_name, 0, - "HEAD", NULL, DEFAULT_REFLOG_ACTION); - error(_("\ngit encountered an error while preparing the " - "patches to replay\n" - "these revisions:\n" - "\n %s\n\n" - "As a result, git cannot rebase them."), - opts->revisions); - - strbuf_release(&revisions); - return status; - } - strbuf_release(&revisions); - - am.in = open(rebased_patches, O_RDONLY); - if (am.in < 0) { - status = error_errno(_("could not open '%s' for reading"), - rebased_patches); - free(rebased_patches); - strvec_clear(&am.args); - return status; - } - - strvec_pushv(&am.args, opts->git_am_opts.v); - strvec_push(&am.args, "--rebasing"); - strvec_pushf(&am.args, "--resolvemsg=%s", resolvemsg); - strvec_push(&am.args, "--patch-format=mboxrd"); - if (opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE) - strvec_push(&am.args, "--rerere-autoupdate"); - else if (opts->allow_rerere_autoupdate == RERERE_NOAUTOUPDATE) - strvec_push(&am.args, "--no-rerere-autoupdate"); - if (opts->gpg_sign_opt) - strvec_push(&am.args, opts->gpg_sign_opt); - status = run_command(&am); - unlink(rebased_patches); - free(rebased_patches); - - if (!status) { - return move_to_original_branch(opts); - } - - if (is_directory(opts->state_dir)) - rebase_write_basic_state(opts); - - return status; -} - -static int run_specific_rebase(struct rebase_options *opts, enum action action) -{ - const char *argv[] = { NULL, NULL }; - struct strbuf script_snippet = STRBUF_INIT, buf = STRBUF_INIT; - int status; - const char *backend, *backend_func; - - if (opts->type == REBASE_MERGE) { - /* Run sequencer-based rebase */ - setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1); - if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) { - setenv("GIT_SEQUENCE_EDITOR", ":", 1); - opts->autosquash = 0; - } - if (opts->gpg_sign_opt) { - /* remove the leading "-S" */ - char *tmp = xstrdup(opts->gpg_sign_opt + 2); - free(opts->gpg_sign_opt); - opts->gpg_sign_opt = tmp; - } - - status = run_sequencer_rebase(opts, action); - goto finished_rebase; - } - - if (opts->type == REBASE_APPLY) { - status = run_am(opts); - goto finished_rebase; - } - - add_var(&script_snippet, "GIT_DIR", absolute_path(get_git_dir())); - add_var(&script_snippet, "state_dir", opts->state_dir); - - add_var(&script_snippet, "upstream_name", opts->upstream_name); - add_var(&script_snippet, "upstream", opts->upstream ? - oid_to_hex(&opts->upstream->object.oid) : NULL); - add_var(&script_snippet, "head_name", - opts->head_name ? opts->head_name : "detached HEAD"); - add_var(&script_snippet, "orig_head", oid_to_hex(&opts->orig_head)); - add_var(&script_snippet, "onto", opts->onto ? - oid_to_hex(&opts->onto->object.oid) : NULL); - add_var(&script_snippet, "onto_name", opts->onto_name); - add_var(&script_snippet, "revisions", opts->revisions); - add_var(&script_snippet, "restrict_revision", opts->restrict_revision ? - oid_to_hex(&opts->restrict_revision->object.oid) : NULL); - sq_quote_argv_pretty(&buf, opts->git_am_opts.v); - add_var(&script_snippet, "git_am_opt", buf.buf); - strbuf_release(&buf); - add_var(&script_snippet, "verbose", - opts->flags & REBASE_VERBOSE ? "t" : ""); - add_var(&script_snippet, "diffstat", - opts->flags & REBASE_DIFFSTAT ? "t" : ""); - add_var(&script_snippet, "force_rebase", - opts->flags & REBASE_FORCE ? "t" : ""); - if (opts->switch_to) - add_var(&script_snippet, "switch_to", opts->switch_to); - add_var(&script_snippet, "action", opts->action ? opts->action : ""); - add_var(&script_snippet, "signoff", opts->signoff ? "--signoff" : ""); - add_var(&script_snippet, "allow_rerere_autoupdate", - opts->allow_rerere_autoupdate ? - opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ? - "--rerere-autoupdate" : "--no-rerere-autoupdate" : ""); - add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : ""); - add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : ""); - add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt); - add_var(&script_snippet, "cmd", opts->cmd); - add_var(&script_snippet, "allow_empty_message", - opts->allow_empty_message ? "--allow-empty-message" : ""); - add_var(&script_snippet, "rebase_merges", - opts->rebase_merges ? "t" : ""); - add_var(&script_snippet, "rebase_cousins", - opts->rebase_cousins ? "t" : ""); - add_var(&script_snippet, "strategy", opts->strategy); - add_var(&script_snippet, "strategy_opts", opts->strategy_opts); - add_var(&script_snippet, "rebase_root", opts->root ? "t" : ""); - add_var(&script_snippet, "squash_onto", - opts->squash_onto ? oid_to_hex(opts->squash_onto) : ""); - add_var(&script_snippet, "git_format_patch_opt", - opts->git_format_patch_opt.buf); - - if (is_merge(opts) && - !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) { - strbuf_addstr(&script_snippet, - "GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; "); - opts->autosquash = 0; - } - - switch (opts->type) { - case REBASE_PRESERVE_MERGES: - backend = "git-rebase--preserve-merges"; - backend_func = "git_rebase__preserve_merges"; - break; - default: - BUG("Unhandled rebase type %d", opts->type); - break; - } - - strbuf_addf(&script_snippet, - ". git-sh-setup && . %s && %s", backend, backend_func); - argv[0] = script_snippet.buf; - - status = run_command_v_opt(argv, RUN_USING_SHELL); -finished_rebase: - if (opts->dont_finish_rebase) - ; /* do nothing */ - else if (opts->type == REBASE_MERGE) - ; /* merge backend cleans up after itself */ - else if (status == 0) { - if (!file_exists(state_dir_path("stopped-sha", opts))) - finish_rebase(opts); - } else if (status == 2) { - struct strbuf dir = STRBUF_INIT; - - apply_autostash(state_dir_path("autostash", opts)); - strbuf_addstr(&dir, opts->state_dir); - remove_dir_recursively(&dir, 0); - strbuf_release(&dir); - die("Nothing to do"); - } - - strbuf_release(&script_snippet); - - return status ? -1 : 0; -} - -static int rebase_config(const char *var, const char *value, void *data) -{ - struct rebase_options *opts = data; - - if (!strcmp(var, "rebase.stat")) { - if (git_config_bool(var, value)) - opts->flags |= REBASE_DIFFSTAT; - else - opts->flags &= ~REBASE_DIFFSTAT; - return 0; - } - - if (!strcmp(var, "rebase.autosquash")) { - opts->autosquash = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "commit.gpgsign")) { - free(opts->gpg_sign_opt); - opts->gpg_sign_opt = git_config_bool(var, value) ? - xstrdup("-S") : NULL; - return 0; - } - - if (!strcmp(var, "rebase.autostash")) { - opts->autostash = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "rebase.reschedulefailedexec")) { - opts->reschedule_failed_exec = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "rebase.usebuiltin")) { - opts->use_legacy_rebase = !git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "rebase.backend")) { - return git_config_string(&opts->default_backend, var, value); - } - - return git_default_config(var, value, data); -} - -/* - * Determines whether the commits in from..to are linear, i.e. contain - * no merge commits. This function *expects* `from` to be an ancestor of - * `to`. - */ -static int is_linear_history(struct commit *from, struct commit *to) -{ - while (to && to != from) { - parse_commit(to); - if (!to->parents) - return 1; - if (to->parents->next) - return 0; - to = to->parents->item; - } - return 1; -} - -static int can_fast_forward(struct commit *onto, struct commit *upstream, - struct commit *restrict_revision, - struct object_id *head_oid, struct object_id *merge_base) -{ - struct commit *head = lookup_commit(the_repository, head_oid); - struct commit_list *merge_bases = NULL; - int res = 0; - - if (!head) - goto done; - - merge_bases = get_merge_bases(onto, head); - if (!merge_bases || merge_bases->next) { - oidcpy(merge_base, &null_oid); - goto done; - } - - oidcpy(merge_base, &merge_bases->item->object.oid); - if (!oideq(merge_base, &onto->object.oid)) - goto done; - - if (restrict_revision && !oideq(&restrict_revision->object.oid, merge_base)) - goto done; - - if (!upstream) - goto done; - - free_commit_list(merge_bases); - merge_bases = get_merge_bases(upstream, head); - if (!merge_bases || merge_bases->next) - goto done; - - if (!oideq(&onto->object.oid, &merge_bases->item->object.oid)) - goto done; - - res = 1; - -done: - free_commit_list(merge_bases); - return res && is_linear_history(onto, head); -} - -static int parse_opt_am(const struct option *opt, const char *arg, int unset) -{ - struct rebase_options *opts = opt->value; - - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - - opts->type = REBASE_APPLY; - - return 0; -} - -/* -i followed by -m is still -i */ -static int parse_opt_merge(const struct option *opt, const char *arg, int unset) -{ - struct rebase_options *opts = opt->value; - - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - - if (!is_merge(opts)) - opts->type = REBASE_MERGE; - - return 0; -} - -/* -i followed by -p is still explicitly interactive, but -p alone is not */ -static int parse_opt_interactive(const struct option *opt, const char *arg, - int unset) -{ - struct rebase_options *opts = opt->value; - - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - - opts->type = REBASE_MERGE; - opts->flags |= REBASE_INTERACTIVE_EXPLICIT; - - return 0; -} - -static enum empty_type parse_empty_value(const char *value) -{ - if (!strcasecmp(value, "drop")) - return EMPTY_DROP; - else if (!strcasecmp(value, "keep")) - return EMPTY_KEEP; - else if (!strcasecmp(value, "ask")) - return EMPTY_ASK; - - die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value); -} - -static int parse_opt_empty(const struct option *opt, const char *arg, int unset) -{ - struct rebase_options *options = opt->value; - enum empty_type value = parse_empty_value(arg); - - BUG_ON_OPT_NEG(unset); - - options->empty = value; - return 0; -} - -static void NORETURN error_on_missing_default_upstream(void) -{ - struct branch *current_branch = branch_get(NULL); - - printf(_("%s\n" - "Please specify which branch you want to rebase against.\n" - "See git-rebase(1) for details.\n" - "\n" - " git rebase '<branch>'\n" - "\n"), - current_branch ? _("There is no tracking information for " - "the current branch.") : - _("You are not currently on a branch.")); - - if (current_branch) { - const char *remote = current_branch->remote_name; - - if (!remote) - remote = _("<remote>"); - - printf(_("If you wish to set tracking information for this " - "branch you can do so with:\n" - "\n" - " git branch --set-upstream-to=%s/<branch> %s\n" - "\n"), - remote, current_branch->name); - } - exit(1); -} - -static void set_reflog_action(struct rebase_options *options) -{ - const char *env; - struct strbuf buf = STRBUF_INIT; - - if (!is_merge(options)) - return; - - env = getenv(GIT_REFLOG_ACTION_ENVIRONMENT); - if (env && strcmp("rebase", env)) - return; /* only override it if it is "rebase" */ - - strbuf_addf(&buf, "rebase (%s)", options->action); - setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1); - strbuf_release(&buf); -} - -static int check_exec_cmd(const char *cmd) -{ - if (strchr(cmd, '\n')) - return error(_("exec commands cannot contain newlines")); - - /* Does the command consist purely of whitespace? */ - if (!cmd[strspn(cmd, " \t\r\f\v")]) - return error(_("empty exec command")); - - return 0; -} - -int cmd_rebase(int argc, const char **argv, const char *prefix) -{ - struct rebase_options options = REBASE_OPTIONS_INIT; - const char *branch_name; - int ret, flags, total_argc, in_progress = 0; - int keep_base = 0; - int ok_to_skip_pre_rebase = 0; - struct strbuf msg = STRBUF_INIT; - struct strbuf revisions = STRBUF_INIT; - struct strbuf buf = STRBUF_INIT; - struct object_id merge_base; - int ignore_whitespace = 0; - enum action action = ACTION_NONE; - const char *gpg_sign = NULL; - struct string_list exec = STRING_LIST_INIT_NODUP; - const char *rebase_merges = NULL; - int fork_point = -1; - struct string_list strategy_options = STRING_LIST_INIT_NODUP; - struct object_id squash_onto; - char *squash_onto_name = NULL; - int reschedule_failed_exec = -1; - int allow_preemptive_ff = 1; - struct option builtin_rebase_options[] = { - OPT_STRING(0, "onto", &options.onto_name, - N_("revision"), - N_("rebase onto given branch instead of upstream")), - OPT_BOOL(0, "keep-base", &keep_base, - N_("use the merge-base of upstream and branch as the current base")), - OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase, - N_("allow pre-rebase hook to run")), - OPT_NEGBIT('q', "quiet", &options.flags, - N_("be quiet. implies --no-stat"), - REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT), - OPT_BIT('v', "verbose", &options.flags, - N_("display a diffstat of what changed upstream"), - REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT), - {OPTION_NEGBIT, 'n', "no-stat", &options.flags, NULL, - N_("do not show diffstat of what changed upstream"), - PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT }, - OPT_BOOL(0, "signoff", &options.signoff, - N_("add a Signed-off-by: line to each commit")), - OPT_BOOL(0, "committer-date-is-author-date", - &options.committer_date_is_author_date, - N_("make committer date match author date")), - OPT_BOOL(0, "reset-author-date", &options.ignore_date, - N_("ignore author date and use current date")), - OPT_HIDDEN_BOOL(0, "ignore-date", &options.ignore_date, - N_("synonym of --reset-author-date")), - OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"), - N_("passed to 'git apply'"), 0), - OPT_BOOL(0, "ignore-whitespace", &ignore_whitespace, - N_("ignore changes in whitespace")), - OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts, - N_("action"), N_("passed to 'git apply'"), 0), - OPT_BIT('f', "force-rebase", &options.flags, - N_("cherry-pick all commits, even if unchanged"), - REBASE_FORCE), - OPT_BIT(0, "no-ff", &options.flags, - N_("cherry-pick all commits, even if unchanged"), - REBASE_FORCE), - OPT_CMDMODE(0, "continue", &action, N_("continue"), - ACTION_CONTINUE), - OPT_CMDMODE(0, "skip", &action, - N_("skip current patch and continue"), ACTION_SKIP), - OPT_CMDMODE(0, "abort", &action, - N_("abort and check out the original branch"), - ACTION_ABORT), - OPT_CMDMODE(0, "quit", &action, - N_("abort but keep HEAD where it is"), ACTION_QUIT), - OPT_CMDMODE(0, "edit-todo", &action, N_("edit the todo list " - "during an interactive rebase"), ACTION_EDIT_TODO), - OPT_CMDMODE(0, "show-current-patch", &action, - N_("show the patch file being applied or merged"), - ACTION_SHOW_CURRENT_PATCH), - OPT_CALLBACK_F(0, "apply", &options, NULL, - N_("use apply strategies to rebase"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, - parse_opt_am), - OPT_CALLBACK_F('m', "merge", &options, NULL, - N_("use merging strategies to rebase"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, - parse_opt_merge), - OPT_CALLBACK_F('i', "interactive", &options, NULL, - N_("let the user edit the list of commits to rebase"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, - parse_opt_interactive), - OPT_SET_INT_F('p', "preserve-merges", &options.type, - N_("(DEPRECATED) try to recreate merges instead of " - "ignoring them"), - REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN), - OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate), - OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}", - N_("how to handle commits that become empty"), - PARSE_OPT_NONEG, parse_opt_empty), - OPT_CALLBACK_F('k', "keep-empty", &options, NULL, - N_("keep commits which start empty"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, - parse_opt_keep_empty), - OPT_BOOL(0, "autosquash", &options.autosquash, - N_("move commits that begin with " - "squash!/fixup! under -i")), - { OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"), - N_("GPG-sign commits"), - PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - OPT_AUTOSTASH(&options.autostash), - OPT_STRING_LIST('x', "exec", &exec, N_("exec"), - N_("add exec lines after each commit of the " - "editable list")), - OPT_BOOL_F(0, "allow-empty-message", - &options.allow_empty_message, - N_("allow rebasing commits with empty messages"), - PARSE_OPT_HIDDEN), - {OPTION_STRING, 'r', "rebase-merges", &rebase_merges, - N_("mode"), - N_("try to rebase merges instead of skipping them"), - PARSE_OPT_OPTARG, NULL, (intptr_t)""}, - OPT_BOOL(0, "fork-point", &fork_point, - N_("use 'merge-base --fork-point' to refine upstream")), - OPT_STRING('s', "strategy", &options.strategy, - N_("strategy"), N_("use the given merge strategy")), - OPT_STRING_LIST('X', "strategy-option", &strategy_options, - N_("option"), - N_("pass the argument through to the merge " - "strategy")), - OPT_BOOL(0, "root", &options.root, - N_("rebase all reachable commits up to the root(s)")), - OPT_BOOL(0, "reschedule-failed-exec", - &reschedule_failed_exec, - N_("automatically re-schedule any `exec` that fails")), - OPT_BOOL(0, "reapply-cherry-picks", &options.reapply_cherry_picks, - N_("apply all changes, even those already present upstream")), - OPT_END(), - }; - int i; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage_with_options(builtin_rebase_usage, - builtin_rebase_options); - - options.allow_empty_message = 1; - git_config(rebase_config, &options); - /* options.gpg_sign_opt will be either "-S" or NULL */ - gpg_sign = options.gpg_sign_opt ? "" : NULL; - FREE_AND_NULL(options.gpg_sign_opt); - - if (options.use_legacy_rebase || - !git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1)) - warning(_("the rebase.useBuiltin support has been removed!\n" - "See its entry in 'git help config' for details.")); - - strbuf_reset(&buf); - strbuf_addf(&buf, "%s/applying", apply_dir()); - if(file_exists(buf.buf)) - die(_("It looks like 'git am' is in progress. Cannot rebase.")); - - if (is_directory(apply_dir())) { - options.type = REBASE_APPLY; - options.state_dir = apply_dir(); - } else if (is_directory(merge_dir())) { - strbuf_reset(&buf); - strbuf_addf(&buf, "%s/rewritten", merge_dir()); - if (is_directory(buf.buf)) { - options.type = REBASE_PRESERVE_MERGES; - options.flags |= REBASE_INTERACTIVE_EXPLICIT; - } else { - strbuf_reset(&buf); - strbuf_addf(&buf, "%s/interactive", merge_dir()); - if(file_exists(buf.buf)) { - options.type = REBASE_MERGE; - options.flags |= REBASE_INTERACTIVE_EXPLICIT; - } else - options.type = REBASE_MERGE; - } - options.state_dir = merge_dir(); - } - - if (options.type != REBASE_UNSPECIFIED) - in_progress = 1; - - total_argc = argc; - argc = parse_options(argc, argv, prefix, - builtin_rebase_options, - builtin_rebase_usage, 0); - - if (action != ACTION_NONE && total_argc != 2) { - usage_with_options(builtin_rebase_usage, - builtin_rebase_options); - } - - if (argc > 2) - usage_with_options(builtin_rebase_usage, - builtin_rebase_options); - - if (options.type == REBASE_PRESERVE_MERGES) - warning(_("git rebase --preserve-merges is deprecated. " - "Use --rebase-merges instead.")); - - if (keep_base) { - if (options.onto_name) - die(_("cannot combine '--keep-base' with '--onto'")); - if (options.root) - die(_("cannot combine '--keep-base' with '--root'")); - } - - if (options.root && fork_point > 0) - die(_("cannot combine '--root' with '--fork-point'")); - - if (action != ACTION_NONE && !in_progress) - die(_("No rebase in progress?")); - setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0); - - if (action == ACTION_EDIT_TODO && !is_merge(&options)) - die(_("The --edit-todo action can only be used during " - "interactive rebase.")); - - if (trace2_is_enabled()) { - if (is_merge(&options)) - trace2_cmd_mode("interactive"); - else if (exec.nr) - trace2_cmd_mode("interactive-exec"); - else - trace2_cmd_mode(action_names[action]); - } - - switch (action) { - case ACTION_CONTINUE: { - struct object_id head; - struct lock_file lock_file = LOCK_INIT; - int fd; - - options.action = "continue"; - set_reflog_action(&options); - - /* Sanity check */ - if (get_oid("HEAD", &head)) - die(_("Cannot read HEAD")); - - fd = hold_locked_index(&lock_file, 0); - if (repo_read_index(the_repository) < 0) - die(_("could not read index")); - refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, - NULL); - if (0 <= fd) - repo_update_index_if_able(the_repository, &lock_file); - rollback_lock_file(&lock_file); - - if (has_unstaged_changes(the_repository, 1)) { - puts(_("You must edit all merge conflicts and then\n" - "mark them as resolved using git add")); - exit(1); - } - if (read_basic_state(&options)) - exit(1); - goto run_rebase; - } - case ACTION_SKIP: { - struct string_list merge_rr = STRING_LIST_INIT_DUP; - - options.action = "skip"; - set_reflog_action(&options); - - rerere_clear(the_repository, &merge_rr); - string_list_clear(&merge_rr, 1); - - if (reset_head(the_repository, NULL, "reset", NULL, RESET_HEAD_HARD, - NULL, NULL, DEFAULT_REFLOG_ACTION) < 0) - die(_("could not discard worktree changes")); - remove_branch_state(the_repository, 0); - if (read_basic_state(&options)) - exit(1); - goto run_rebase; - } - case ACTION_ABORT: { - struct string_list merge_rr = STRING_LIST_INIT_DUP; - options.action = "abort"; - set_reflog_action(&options); - - rerere_clear(the_repository, &merge_rr); - string_list_clear(&merge_rr, 1); - - if (read_basic_state(&options)) - exit(1); - if (reset_head(the_repository, &options.orig_head, "reset", - options.head_name, RESET_HEAD_HARD, - NULL, NULL, DEFAULT_REFLOG_ACTION) < 0) - die(_("could not move back to %s"), - oid_to_hex(&options.orig_head)); - remove_branch_state(the_repository, 0); - ret = !!finish_rebase(&options); - goto cleanup; - } - case ACTION_QUIT: { - save_autostash(state_dir_path("autostash", &options)); - if (options.type == REBASE_MERGE) { - struct replay_opts replay = REPLAY_OPTS_INIT; - - replay.action = REPLAY_INTERACTIVE_REBASE; - ret = !!sequencer_remove_state(&replay); - } else { - strbuf_reset(&buf); - strbuf_addstr(&buf, options.state_dir); - ret = !!remove_dir_recursively(&buf, 0); - if (ret) - error(_("could not remove '%s'"), - options.state_dir); - } - goto cleanup; - } - case ACTION_EDIT_TODO: - options.action = "edit-todo"; - options.dont_finish_rebase = 1; - goto run_rebase; - case ACTION_SHOW_CURRENT_PATCH: - options.action = "show-current-patch"; - options.dont_finish_rebase = 1; - goto run_rebase; - case ACTION_NONE: - break; - default: - BUG("action: %d", action); - } - - /* Make sure no rebase is in progress */ - if (in_progress) { - const char *last_slash = strrchr(options.state_dir, '/'); - const char *state_dir_base = - last_slash ? last_slash + 1 : options.state_dir; - const char *cmd_live_rebase = - "git rebase (--continue | --abort | --skip)"; - strbuf_reset(&buf); - strbuf_addf(&buf, "rm -fr \"%s\"", options.state_dir); - die(_("It seems that there is already a %s directory, and\n" - "I wonder if you are in the middle of another rebase. " - "If that is the\n" - "case, please try\n\t%s\n" - "If that is not the case, please\n\t%s\n" - "and run me again. I am stopping in case you still " - "have something\n" - "valuable there.\n"), - state_dir_base, cmd_live_rebase, buf.buf); - } - - if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) || - (action != ACTION_NONE) || - (exec.nr > 0) || - options.autosquash) { - allow_preemptive_ff = 0; - } - if (options.committer_date_is_author_date || options.ignore_date) - options.flags |= REBASE_FORCE; - - for (i = 0; i < options.git_am_opts.nr; i++) { - const char *option = options.git_am_opts.v[i], *p; - if (!strcmp(option, "--whitespace=fix") || - !strcmp(option, "--whitespace=strip")) - allow_preemptive_ff = 0; - else if (skip_prefix(option, "-C", &p)) { - while (*p) - if (!isdigit(*(p++))) - die(_("switch `C' expects a " - "numerical value")); - } else if (skip_prefix(option, "--whitespace=", &p)) { - if (*p && strcmp(p, "warn") && strcmp(p, "nowarn") && - strcmp(p, "error") && strcmp(p, "error-all")) - die("Invalid whitespace option: '%s'", p); - } - } - - for (i = 0; i < exec.nr; i++) - if (check_exec_cmd(exec.items[i].string)) - exit(1); - - if (!(options.flags & REBASE_NO_QUIET)) - strvec_push(&options.git_am_opts, "-q"); - - if (options.empty != EMPTY_UNSPECIFIED) - imply_merge(&options, "--empty"); - - if (options.reapply_cherry_picks) - imply_merge(&options, "--reapply-cherry-picks"); - - if (gpg_sign) - options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign); - - if (exec.nr) { - int i; - - imply_merge(&options, "--exec"); - - strbuf_reset(&buf); - for (i = 0; i < exec.nr; i++) - strbuf_addf(&buf, "exec %s\n", exec.items[i].string); - options.cmd = xstrdup(buf.buf); - } - - if (rebase_merges) { - if (!*rebase_merges) - ; /* default mode; do nothing */ - else if (!strcmp("rebase-cousins", rebase_merges)) - options.rebase_cousins = 1; - else if (strcmp("no-rebase-cousins", rebase_merges)) - die(_("Unknown mode: %s"), rebase_merges); - options.rebase_merges = 1; - imply_merge(&options, "--rebase-merges"); - } - - if (options.type == REBASE_APPLY) { - if (ignore_whitespace) - strvec_push(&options.git_am_opts, - "--ignore-whitespace"); - if (options.committer_date_is_author_date) - strvec_push(&options.git_am_opts, - "--committer-date-is-author-date"); - if (options.ignore_date) - strvec_push(&options.git_am_opts, "--ignore-date"); - } else { - /* REBASE_MERGE and PRESERVE_MERGES */ - if (ignore_whitespace) { - string_list_append(&strategy_options, - "ignore-space-change"); - } - } - - if (strategy_options.nr) { - int i; - - if (!options.strategy) - options.strategy = "recursive"; - - strbuf_reset(&buf); - for (i = 0; i < strategy_options.nr; i++) - strbuf_addf(&buf, " --%s", - strategy_options.items[i].string); - options.strategy_opts = xstrdup(buf.buf); - } - - if (options.strategy) { - options.strategy = xstrdup(options.strategy); - switch (options.type) { - case REBASE_APPLY: - die(_("--strategy requires --merge or --interactive")); - case REBASE_MERGE: - case REBASE_PRESERVE_MERGES: - /* compatible */ - break; - case REBASE_UNSPECIFIED: - options.type = REBASE_MERGE; - break; - default: - BUG("unhandled rebase type (%d)", options.type); - } - } - - if (options.type == REBASE_MERGE) - imply_merge(&options, "--merge"); - - if (options.root && !options.onto_name) - imply_merge(&options, "--root without --onto"); - - if (isatty(2) && options.flags & REBASE_NO_QUIET) - strbuf_addstr(&options.git_format_patch_opt, " --progress"); - - if (options.git_am_opts.nr || options.type == REBASE_APPLY) { - /* all am options except -q are compatible only with --apply */ - for (i = options.git_am_opts.nr - 1; i >= 0; i--) - if (strcmp(options.git_am_opts.v[i], "-q")) - break; - - if (i >= 0) { - if (is_merge(&options)) - die(_("cannot combine apply options with " - "merge options")); - else - options.type = REBASE_APPLY; - } - } - - if (options.type == REBASE_UNSPECIFIED) { - if (!strcmp(options.default_backend, "merge")) - imply_merge(&options, "--merge"); - else if (!strcmp(options.default_backend, "apply")) - options.type = REBASE_APPLY; - else - die(_("Unknown rebase backend: %s"), - options.default_backend); - } - - switch (options.type) { - case REBASE_MERGE: - case REBASE_PRESERVE_MERGES: - options.state_dir = merge_dir(); - break; - case REBASE_APPLY: - options.state_dir = apply_dir(); - break; - default: - BUG("options.type was just set above; should be unreachable."); - } - - if (options.empty == EMPTY_UNSPECIFIED) { - if (options.flags & REBASE_INTERACTIVE_EXPLICIT) - options.empty = EMPTY_ASK; - else if (exec.nr > 0) - options.empty = EMPTY_KEEP; - else - options.empty = EMPTY_DROP; - } - if (reschedule_failed_exec > 0 && !is_merge(&options)) - die(_("--reschedule-failed-exec requires " - "--exec or --interactive")); - if (reschedule_failed_exec >= 0) - options.reschedule_failed_exec = reschedule_failed_exec; - - if (options.signoff) { - if (options.type == REBASE_PRESERVE_MERGES) - die("cannot combine '--signoff' with " - "'--preserve-merges'"); - strvec_push(&options.git_am_opts, "--signoff"); - options.flags |= REBASE_FORCE; - } - - if (options.type == REBASE_PRESERVE_MERGES) { - /* - * Note: incompatibility with --signoff handled in signoff block above - * Note: incompatibility with --interactive is just a strong warning; - * git-rebase.txt caveats with "unless you know what you are doing" - */ - if (options.rebase_merges) - die(_("cannot combine '--preserve-merges' with " - "'--rebase-merges'")); - - if (options.reschedule_failed_exec) - die(_("error: cannot combine '--preserve-merges' with " - "'--reschedule-failed-exec'")); - } - - if (!options.root) { - if (argc < 1) { - struct branch *branch; - - branch = branch_get(NULL); - options.upstream_name = branch_get_upstream(branch, - NULL); - if (!options.upstream_name) - error_on_missing_default_upstream(); - if (fork_point < 0) - fork_point = 1; - } else { - options.upstream_name = argv[0]; - argc--; - argv++; - if (!strcmp(options.upstream_name, "-")) - options.upstream_name = "@{-1}"; - } - options.upstream = peel_committish(options.upstream_name); - if (!options.upstream) - die(_("invalid upstream '%s'"), options.upstream_name); - options.upstream_arg = options.upstream_name; - } else { - if (!options.onto_name) { - if (commit_tree("", 0, the_hash_algo->empty_tree, NULL, - &squash_onto, NULL, NULL) < 0) - die(_("Could not create new root commit")); - options.squash_onto = &squash_onto; - options.onto_name = squash_onto_name = - xstrdup(oid_to_hex(&squash_onto)); - } else - options.root_with_onto = 1; - - options.upstream_name = NULL; - options.upstream = NULL; - if (argc > 1) - usage_with_options(builtin_rebase_usage, - builtin_rebase_options); - options.upstream_arg = "--root"; - } - - /* Make sure the branch to rebase onto is valid. */ - if (keep_base) { - strbuf_reset(&buf); - strbuf_addstr(&buf, options.upstream_name); - strbuf_addstr(&buf, "..."); - options.onto_name = xstrdup(buf.buf); - } else if (!options.onto_name) - options.onto_name = options.upstream_name; - if (strstr(options.onto_name, "...")) { - if (get_oid_mb(options.onto_name, &merge_base) < 0) { - if (keep_base) - die(_("'%s': need exactly one merge base with branch"), - options.upstream_name); - else - die(_("'%s': need exactly one merge base"), - options.onto_name); - } - options.onto = lookup_commit_or_die(&merge_base, - options.onto_name); - } else { - options.onto = peel_committish(options.onto_name); - if (!options.onto) - die(_("Does not point to a valid commit '%s'"), - options.onto_name); - } - - /* - * If the branch to rebase is given, that is the branch we will rebase - * branch_name -- branch/commit being rebased, or - * HEAD (already detached) - * orig_head -- commit object name of tip of the branch before rebasing - * head_name -- refs/heads/<that-branch> or NULL (detached HEAD) - */ - if (argc == 1) { - /* Is it "rebase other branchname" or "rebase other commit"? */ - branch_name = argv[0]; - options.switch_to = argv[0]; - - /* Is it a local branch? */ - strbuf_reset(&buf); - strbuf_addf(&buf, "refs/heads/%s", branch_name); - if (!read_ref(buf.buf, &options.orig_head)) { - die_if_checked_out(buf.buf, 1); - options.head_name = xstrdup(buf.buf); - /* If not is it a valid ref (branch or commit)? */ - } else if (!get_oid(branch_name, &options.orig_head)) - options.head_name = NULL; - else - die(_("fatal: no such branch/commit '%s'"), - branch_name); - } else if (argc == 0) { - /* Do not need to switch branches, we are already on it. */ - options.head_name = - xstrdup_or_null(resolve_ref_unsafe("HEAD", 0, NULL, - &flags)); - if (!options.head_name) - die(_("No such ref: %s"), "HEAD"); - if (flags & REF_ISSYMREF) { - if (!skip_prefix(options.head_name, - "refs/heads/", &branch_name)) - branch_name = options.head_name; - - } else { - FREE_AND_NULL(options.head_name); - branch_name = "HEAD"; - } - if (get_oid("HEAD", &options.orig_head)) - die(_("Could not resolve HEAD to a revision")); - } else - BUG("unexpected number of arguments left to parse"); - - if (fork_point > 0) { - struct commit *head = - lookup_commit_reference(the_repository, - &options.orig_head); - options.restrict_revision = - get_fork_point(options.upstream_name, head); - } - - if (repo_read_index(the_repository) < 0) - die(_("could not read index")); - - if (options.autostash) { - create_autostash(the_repository, state_dir_path("autostash", &options), - DEFAULT_REFLOG_ACTION); - } - - if (require_clean_work_tree(the_repository, "rebase", - _("Please commit or stash them."), 1, 1)) { - ret = 1; - goto cleanup; - } - - /* - * Now we are rebasing commits upstream..orig_head (or with --root, - * everything leading up to orig_head) on top of onto. - */ - - /* - * Check if we are already based on onto with linear history, - * in which case we could fast-forward without replacing the commits - * with new commits recreated by replaying their changes. - * - * Note that can_fast_forward() initializes merge_base, so we have to - * call it before checking allow_preemptive_ff. - */ - if (can_fast_forward(options.onto, options.upstream, options.restrict_revision, - &options.orig_head, &merge_base) && - allow_preemptive_ff) { - int flag; - - if (!(options.flags & REBASE_FORCE)) { - /* Lazily switch to the target branch if needed... */ - if (options.switch_to) { - strbuf_reset(&buf); - strbuf_addf(&buf, "%s: checkout %s", - getenv(GIT_REFLOG_ACTION_ENVIRONMENT), - options.switch_to); - if (reset_head(the_repository, - &options.orig_head, "checkout", - options.head_name, - RESET_HEAD_RUN_POST_CHECKOUT_HOOK, - NULL, buf.buf, - DEFAULT_REFLOG_ACTION) < 0) { - ret = !!error(_("could not switch to " - "%s"), - options.switch_to); - goto cleanup; - } - } - - if (!(options.flags & REBASE_NO_QUIET)) - ; /* be quiet */ - else if (!strcmp(branch_name, "HEAD") && - resolve_ref_unsafe("HEAD", 0, NULL, &flag)) - puts(_("HEAD is up to date.")); - else - printf(_("Current branch %s is up to date.\n"), - branch_name); - ret = !!finish_rebase(&options); - goto cleanup; - } else if (!(options.flags & REBASE_NO_QUIET)) - ; /* be quiet */ - else if (!strcmp(branch_name, "HEAD") && - resolve_ref_unsafe("HEAD", 0, NULL, &flag)) - puts(_("HEAD is up to date, rebase forced.")); - else - printf(_("Current branch %s is up to date, rebase " - "forced.\n"), branch_name); - } - - /* If a hook exists, give it a chance to interrupt*/ - if (!ok_to_skip_pre_rebase && - run_hook_le(NULL, "pre-rebase", options.upstream_arg, - argc ? argv[0] : NULL, NULL)) - die(_("The pre-rebase hook refused to rebase.")); - - if (options.flags & REBASE_DIFFSTAT) { - struct diff_options opts; - - if (options.flags & REBASE_VERBOSE) { - if (is_null_oid(&merge_base)) - printf(_("Changes to %s:\n"), - oid_to_hex(&options.onto->object.oid)); - else - printf(_("Changes from %s to %s:\n"), - oid_to_hex(&merge_base), - oid_to_hex(&options.onto->object.oid)); - } - - /* We want color (if set), but no pager */ - diff_setup(&opts); - opts.stat_width = -1; /* use full terminal width */ - opts.stat_graph_width = -1; /* respect statGraphWidth config */ - opts.output_format |= - DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; - opts.detect_rename = DIFF_DETECT_RENAME; - diff_setup_done(&opts); - diff_tree_oid(is_null_oid(&merge_base) ? - the_hash_algo->empty_tree : &merge_base, - &options.onto->object.oid, "", &opts); - diffcore_std(&opts); - diff_flush(&opts); - } - - if (is_merge(&options)) - goto run_rebase; - - /* Detach HEAD and reset the tree */ - if (options.flags & REBASE_NO_QUIET) - printf(_("First, rewinding head to replay your work on top of " - "it...\n")); - - strbuf_addf(&msg, "%s: checkout %s", - getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name); - if (reset_head(the_repository, &options.onto->object.oid, "checkout", NULL, - RESET_HEAD_DETACH | RESET_ORIG_HEAD | - RESET_HEAD_RUN_POST_CHECKOUT_HOOK, - NULL, msg.buf, DEFAULT_REFLOG_ACTION)) - die(_("Could not detach HEAD")); - strbuf_release(&msg); - - /* - * If the onto is a proper descendant of the tip of the branch, then - * we just fast-forwarded. - */ - strbuf_reset(&msg); - if (oideq(&merge_base, &options.orig_head)) { - printf(_("Fast-forwarded %s to %s.\n"), - branch_name, options.onto_name); - strbuf_addf(&msg, "rebase finished: %s onto %s", - options.head_name ? options.head_name : "detached HEAD", - oid_to_hex(&options.onto->object.oid)); - reset_head(the_repository, NULL, "Fast-forwarded", options.head_name, - RESET_HEAD_REFS_ONLY, "HEAD", msg.buf, - DEFAULT_REFLOG_ACTION); - strbuf_release(&msg); - ret = !!finish_rebase(&options); - goto cleanup; - } - - strbuf_addf(&revisions, "%s..%s", - options.root ? oid_to_hex(&options.onto->object.oid) : - (options.restrict_revision ? - oid_to_hex(&options.restrict_revision->object.oid) : - oid_to_hex(&options.upstream->object.oid)), - oid_to_hex(&options.orig_head)); - - options.revisions = revisions.buf; - -run_rebase: - ret = !!run_specific_rebase(&options, action); - -cleanup: - strbuf_release(&buf); - strbuf_release(&revisions); - free(options.head_name); - free(options.gpg_sign_opt); - free(options.cmd); - free(squash_onto_name); - return ret; -} diff --git a/third_party/git/builtin/receive-pack.c b/third_party/git/builtin/receive-pack.c deleted file mode 100644 index bb9909c52e4e..000000000000 --- a/third_party/git/builtin/receive-pack.c +++ /dev/null @@ -1,2547 +0,0 @@ -#include "builtin.h" -#include "repository.h" -#include "config.h" -#include "lockfile.h" -#include "pack.h" -#include "refs.h" -#include "pkt-line.h" -#include "sideband.h" -#include "run-command.h" -#include "exec-cmd.h" -#include "commit.h" -#include "object.h" -#include "remote.h" -#include "connect.h" -#include "string-list.h" -#include "oid-array.h" -#include "connected.h" -#include "strvec.h" -#include "version.h" -#include "tag.h" -#include "gpg-interface.h" -#include "sigchain.h" -#include "fsck.h" -#include "tmp-objdir.h" -#include "oidset.h" -#include "packfile.h" -#include "object-store.h" -#include "protocol.h" -#include "commit-reach.h" -#include "worktree.h" -#include "shallow.h" - -static const char * const receive_pack_usage[] = { - N_("git receive-pack <git-dir>"), - NULL -}; - -enum deny_action { - DENY_UNCONFIGURED, - DENY_IGNORE, - DENY_WARN, - DENY_REFUSE, - DENY_UPDATE_INSTEAD -}; - -static int deny_deletes; -static int deny_non_fast_forwards; -static enum deny_action deny_current_branch = DENY_UNCONFIGURED; -static enum deny_action deny_delete_current = DENY_UNCONFIGURED; -static int receive_fsck_objects = -1; -static int transfer_fsck_objects = -1; -static struct strbuf fsck_msg_types = STRBUF_INIT; -static int receive_unpack_limit = -1; -static int transfer_unpack_limit = -1; -static int advertise_atomic_push = 1; -static int advertise_push_options; -static int unpack_limit = 100; -static off_t max_input_size; -static int report_status; -static int report_status_v2; -static int use_sideband; -static int use_atomic; -static int use_push_options; -static int quiet; -static int prefer_ofs_delta = 1; -static int auto_update_server_info; -static int auto_gc = 1; -static int reject_thin; -static int stateless_rpc; -static const char *service_dir; -static const char *head_name; -static void *head_name_to_free; -static int sent_capabilities; -static int shallow_update; -static const char *alt_shallow_file; -static struct strbuf push_cert = STRBUF_INIT; -static struct object_id push_cert_oid; -static struct signature_check sigcheck; -static const char *push_cert_nonce; -static const char *cert_nonce_seed; - -static const char *NONCE_UNSOLICITED = "UNSOLICITED"; -static const char *NONCE_BAD = "BAD"; -static const char *NONCE_MISSING = "MISSING"; -static const char *NONCE_OK = "OK"; -static const char *NONCE_SLOP = "SLOP"; -static const char *nonce_status; -static long nonce_stamp_slop; -static timestamp_t nonce_stamp_slop_limit; -static struct ref_transaction *transaction; - -static enum { - KEEPALIVE_NEVER = 0, - KEEPALIVE_AFTER_NUL, - KEEPALIVE_ALWAYS -} use_keepalive; -static int keepalive_in_sec = 5; - -static struct tmp_objdir *tmp_objdir; - -static struct proc_receive_ref { - unsigned int want_add:1, - want_delete:1, - want_modify:1, - negative_ref:1; - char *ref_prefix; - struct proc_receive_ref *next; -} *proc_receive_ref; - -static void proc_receive_ref_append(const char *prefix); - -static enum deny_action parse_deny_action(const char *var, const char *value) -{ - if (value) { - if (!strcasecmp(value, "ignore")) - return DENY_IGNORE; - if (!strcasecmp(value, "warn")) - return DENY_WARN; - if (!strcasecmp(value, "refuse")) - return DENY_REFUSE; - if (!strcasecmp(value, "updateinstead")) - return DENY_UPDATE_INSTEAD; - } - if (git_config_bool(var, value)) - return DENY_REFUSE; - return DENY_IGNORE; -} - -static int receive_pack_config(const char *var, const char *value, void *cb) -{ - int status = parse_hide_refs_config(var, value, "receive"); - - if (status) - return status; - - if (strcmp(var, "receive.denydeletes") == 0) { - deny_deletes = git_config_bool(var, value); - return 0; - } - - if (strcmp(var, "receive.denynonfastforwards") == 0) { - deny_non_fast_forwards = git_config_bool(var, value); - return 0; - } - - if (strcmp(var, "receive.unpacklimit") == 0) { - receive_unpack_limit = git_config_int(var, value); - return 0; - } - - if (strcmp(var, "transfer.unpacklimit") == 0) { - transfer_unpack_limit = git_config_int(var, value); - return 0; - } - - if (strcmp(var, "receive.fsck.skiplist") == 0) { - const char *path; - - if (git_config_pathname(&path, var, value)) - return 1; - strbuf_addf(&fsck_msg_types, "%cskiplist=%s", - fsck_msg_types.len ? ',' : '=', path); - free((char *)path); - return 0; - } - - if (skip_prefix(var, "receive.fsck.", &var)) { - if (is_valid_msg_type(var, value)) - strbuf_addf(&fsck_msg_types, "%c%s=%s", - fsck_msg_types.len ? ',' : '=', var, value); - else - warning("Skipping unknown msg id '%s'", var); - return 0; - } - - if (strcmp(var, "receive.fsckobjects") == 0) { - receive_fsck_objects = git_config_bool(var, value); - return 0; - } - - if (strcmp(var, "transfer.fsckobjects") == 0) { - transfer_fsck_objects = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "receive.denycurrentbranch")) { - deny_current_branch = parse_deny_action(var, value); - return 0; - } - - if (strcmp(var, "receive.denydeletecurrent") == 0) { - deny_delete_current = parse_deny_action(var, value); - return 0; - } - - if (strcmp(var, "repack.usedeltabaseoffset") == 0) { - prefer_ofs_delta = git_config_bool(var, value); - return 0; - } - - if (strcmp(var, "receive.updateserverinfo") == 0) { - auto_update_server_info = git_config_bool(var, value); - return 0; - } - - if (strcmp(var, "receive.autogc") == 0) { - auto_gc = git_config_bool(var, value); - return 0; - } - - if (strcmp(var, "receive.shallowupdate") == 0) { - shallow_update = git_config_bool(var, value); - return 0; - } - - if (strcmp(var, "receive.certnonceseed") == 0) - return git_config_string(&cert_nonce_seed, var, value); - - if (strcmp(var, "receive.certnonceslop") == 0) { - nonce_stamp_slop_limit = git_config_ulong(var, value); - return 0; - } - - if (strcmp(var, "receive.advertiseatomic") == 0) { - advertise_atomic_push = git_config_bool(var, value); - return 0; - } - - if (strcmp(var, "receive.advertisepushoptions") == 0) { - advertise_push_options = git_config_bool(var, value); - return 0; - } - - if (strcmp(var, "receive.keepalive") == 0) { - keepalive_in_sec = git_config_int(var, value); - return 0; - } - - if (strcmp(var, "receive.maxinputsize") == 0) { - max_input_size = git_config_int64(var, value); - return 0; - } - - if (strcmp(var, "receive.procreceiverefs") == 0) { - if (!value) - return config_error_nonbool(var); - proc_receive_ref_append(value); - return 0; - } - - return git_default_config(var, value, cb); -} - -static void show_ref(const char *path, const struct object_id *oid) -{ - if (sent_capabilities) { - packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), path); - } else { - struct strbuf cap = STRBUF_INIT; - - strbuf_addstr(&cap, - "report-status report-status-v2 delete-refs side-band-64k quiet"); - if (advertise_atomic_push) - strbuf_addstr(&cap, " atomic"); - if (prefer_ofs_delta) - strbuf_addstr(&cap, " ofs-delta"); - if (push_cert_nonce) - strbuf_addf(&cap, " push-cert=%s", push_cert_nonce); - if (advertise_push_options) - strbuf_addstr(&cap, " push-options"); - strbuf_addf(&cap, " object-format=%s", the_hash_algo->name); - strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized()); - packet_write_fmt(1, "%s %s%c%s\n", - oid_to_hex(oid), path, 0, cap.buf); - strbuf_release(&cap); - sent_capabilities = 1; - } -} - -static int show_ref_cb(const char *path_full, const struct object_id *oid, - int flag, void *data) -{ - struct oidset *seen = data; - const char *path = strip_namespace(path_full); - - if (ref_is_hidden(path, path_full)) - return 0; - - /* - * Advertise refs outside our current namespace as ".have" - * refs, so that the client can use them to minimize data - * transfer but will otherwise ignore them. - */ - if (!path) { - if (oidset_insert(seen, oid)) - return 0; - path = ".have"; - } else { - oidset_insert(seen, oid); - } - show_ref(path, oid); - return 0; -} - -static void show_one_alternate_ref(const struct object_id *oid, - void *data) -{ - struct oidset *seen = data; - - if (oidset_insert(seen, oid)) - return; - - show_ref(".have", oid); -} - -static void write_head_info(void) -{ - static struct oidset seen = OIDSET_INIT; - - for_each_ref(show_ref_cb, &seen); - for_each_alternate_ref(show_one_alternate_ref, &seen); - oidset_clear(&seen); - if (!sent_capabilities) - show_ref("capabilities^{}", &null_oid); - - advertise_shallow_grafts(1); - - /* EOF */ - packet_flush(1); -} - -#define RUN_PROC_RECEIVE_SCHEDULED 1 -#define RUN_PROC_RECEIVE_RETURNED 2 -struct command { - struct command *next; - const char *error_string; - struct ref_push_report *report; - unsigned int skip_update:1, - did_not_exist:1, - run_proc_receive:2; - int index; - struct object_id old_oid; - struct object_id new_oid; - char ref_name[FLEX_ARRAY]; /* more */ -}; - -static void proc_receive_ref_append(const char *prefix) -{ - struct proc_receive_ref *ref_pattern; - char *p; - int len; - - ref_pattern = xcalloc(1, sizeof(struct proc_receive_ref)); - p = strchr(prefix, ':'); - if (p) { - while (prefix < p) { - if (*prefix == 'a') - ref_pattern->want_add = 1; - else if (*prefix == 'd') - ref_pattern->want_delete = 1; - else if (*prefix == 'm') - ref_pattern->want_modify = 1; - else if (*prefix == '!') - ref_pattern->negative_ref = 1; - prefix++; - } - prefix++; - } else { - ref_pattern->want_add = 1; - ref_pattern->want_delete = 1; - ref_pattern->want_modify = 1; - } - len = strlen(prefix); - while (len && prefix[len - 1] == '/') - len--; - ref_pattern->ref_prefix = xmemdupz(prefix, len); - if (!proc_receive_ref) { - proc_receive_ref = ref_pattern; - } else { - struct proc_receive_ref *end; - - end = proc_receive_ref; - while (end->next) - end = end->next; - end->next = ref_pattern; - } -} - -static int proc_receive_ref_matches(struct command *cmd) -{ - struct proc_receive_ref *p; - - if (!proc_receive_ref) - return 0; - - for (p = proc_receive_ref; p; p = p->next) { - const char *match = p->ref_prefix; - const char *remains; - - if (!p->want_add && is_null_oid(&cmd->old_oid)) - continue; - else if (!p->want_delete && is_null_oid(&cmd->new_oid)) - continue; - else if (!p->want_modify && - !is_null_oid(&cmd->old_oid) && - !is_null_oid(&cmd->new_oid)) - continue; - - if (skip_prefix(cmd->ref_name, match, &remains) && - (!*remains || *remains == '/')) { - if (!p->negative_ref) - return 1; - } else if (p->negative_ref) { - return 1; - } - } - return 0; -} - -static void rp_error(const char *err, ...) __attribute__((format (printf, 1, 2))); -static void rp_warning(const char *err, ...) __attribute__((format (printf, 1, 2))); - -static void report_message(const char *prefix, const char *err, va_list params) -{ - int sz; - char msg[4096]; - - sz = xsnprintf(msg, sizeof(msg), "%s", prefix); - sz += vsnprintf(msg + sz, sizeof(msg) - sz, err, params); - if (sz > (sizeof(msg) - 1)) - sz = sizeof(msg) - 1; - msg[sz++] = '\n'; - - if (use_sideband) - send_sideband(1, 2, msg, sz, use_sideband); - else - xwrite(2, msg, sz); -} - -static void rp_warning(const char *err, ...) -{ - va_list params; - va_start(params, err); - report_message("warning: ", err, params); - va_end(params); -} - -static void rp_error(const char *err, ...) -{ - va_list params; - va_start(params, err); - report_message("error: ", err, params); - va_end(params); -} - -static int copy_to_sideband(int in, int out, void *arg) -{ - char data[128]; - int keepalive_active = 0; - - if (keepalive_in_sec <= 0) - use_keepalive = KEEPALIVE_NEVER; - if (use_keepalive == KEEPALIVE_ALWAYS) - keepalive_active = 1; - - while (1) { - ssize_t sz; - - if (keepalive_active) { - struct pollfd pfd; - int ret; - - pfd.fd = in; - pfd.events = POLLIN; - ret = poll(&pfd, 1, 1000 * keepalive_in_sec); - - if (ret < 0) { - if (errno == EINTR) - continue; - else - break; - } else if (ret == 0) { - /* no data; send a keepalive packet */ - static const char buf[] = "0005\1"; - write_or_die(1, buf, sizeof(buf) - 1); - continue; - } /* else there is actual data to read */ - } - - sz = xread(in, data, sizeof(data)); - if (sz <= 0) - break; - - if (use_keepalive == KEEPALIVE_AFTER_NUL && !keepalive_active) { - const char *p = memchr(data, '\0', sz); - if (p) { - /* - * The NUL tells us to start sending keepalives. Make - * sure we send any other data we read along - * with it. - */ - keepalive_active = 1; - send_sideband(1, 2, data, p - data, use_sideband); - send_sideband(1, 2, p + 1, sz - (p - data + 1), use_sideband); - continue; - } - } - - /* - * Either we're not looking for a NUL signal, or we didn't see - * it yet; just pass along the data. - */ - send_sideband(1, 2, data, sz, use_sideband); - } - close(in); - return 0; -} - -static void hmac_hash(unsigned char *out, - const char *key_in, size_t key_len, - const char *text, size_t text_len) -{ - unsigned char key[GIT_MAX_BLKSZ]; - unsigned char k_ipad[GIT_MAX_BLKSZ]; - unsigned char k_opad[GIT_MAX_BLKSZ]; - int i; - git_hash_ctx ctx; - - /* RFC 2104 2. (1) */ - memset(key, '\0', GIT_MAX_BLKSZ); - if (the_hash_algo->blksz < key_len) { - the_hash_algo->init_fn(&ctx); - the_hash_algo->update_fn(&ctx, key_in, key_len); - the_hash_algo->final_fn(key, &ctx); - } else { - memcpy(key, key_in, key_len); - } - - /* RFC 2104 2. (2) & (5) */ - for (i = 0; i < sizeof(key); i++) { - k_ipad[i] = key[i] ^ 0x36; - k_opad[i] = key[i] ^ 0x5c; - } - - /* RFC 2104 2. (3) & (4) */ - the_hash_algo->init_fn(&ctx); - the_hash_algo->update_fn(&ctx, k_ipad, sizeof(k_ipad)); - the_hash_algo->update_fn(&ctx, text, text_len); - the_hash_algo->final_fn(out, &ctx); - - /* RFC 2104 2. (6) & (7) */ - the_hash_algo->init_fn(&ctx); - the_hash_algo->update_fn(&ctx, k_opad, sizeof(k_opad)); - the_hash_algo->update_fn(&ctx, out, the_hash_algo->rawsz); - the_hash_algo->final_fn(out, &ctx); -} - -static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp) -{ - struct strbuf buf = STRBUF_INIT; - unsigned char hash[GIT_MAX_RAWSZ]; - - strbuf_addf(&buf, "%s:%"PRItime, path, stamp); - hmac_hash(hash, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed)); - strbuf_release(&buf); - - /* RFC 2104 5. HMAC-SHA1 or HMAC-SHA256 */ - strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, (int)the_hash_algo->hexsz, hash_to_hex(hash)); - return strbuf_detach(&buf, NULL); -} - -/* - * NEEDSWORK: reuse find_commit_header() from jk/commit-author-parsing - * after dropping "_commit" from its name and possibly moving it out - * of commit.c - */ -static char *find_header(const char *msg, size_t len, const char *key, - const char **next_line) -{ - int key_len = strlen(key); - const char *line = msg; - - while (line && line < msg + len) { - const char *eol = strchrnul(line, '\n'); - - if ((msg + len <= eol) || line == eol) - return NULL; - if (line + key_len < eol && - !memcmp(line, key, key_len) && line[key_len] == ' ') { - int offset = key_len + 1; - if (next_line) - *next_line = *eol ? eol + 1 : eol; - return xmemdupz(line + offset, (eol - line) - offset); - } - line = *eol ? eol + 1 : NULL; - } - return NULL; -} - -/* - * Return zero if a and b are equal up to n bytes and nonzero if they are not. - * This operation is guaranteed to run in constant time to avoid leaking data. - */ -static int constant_memequal(const char *a, const char *b, size_t n) -{ - int res = 0; - size_t i; - - for (i = 0; i < n; i++) - res |= a[i] ^ b[i]; - return res; -} - -static const char *check_nonce(const char *buf, size_t len) -{ - char *nonce = find_header(buf, len, "nonce", NULL); - timestamp_t stamp, ostamp; - char *bohmac, *expect = NULL; - const char *retval = NONCE_BAD; - size_t noncelen; - - if (!nonce) { - retval = NONCE_MISSING; - goto leave; - } else if (!push_cert_nonce) { - retval = NONCE_UNSOLICITED; - goto leave; - } else if (!strcmp(push_cert_nonce, nonce)) { - retval = NONCE_OK; - goto leave; - } - - if (!stateless_rpc) { - /* returned nonce MUST match what we gave out earlier */ - retval = NONCE_BAD; - goto leave; - } - - /* - * In stateless mode, we may be receiving a nonce issued by - * another instance of the server that serving the same - * repository, and the timestamps may not match, but the - * nonce-seed and dir should match, so we can recompute and - * report the time slop. - * - * In addition, when a nonce issued by another instance has - * timestamp within receive.certnonceslop seconds, we pretend - * as if we issued that nonce when reporting to the hook. - */ - - /* nonce is concat(<seconds-since-epoch>, "-", <hmac>) */ - if (*nonce <= '0' || '9' < *nonce) { - retval = NONCE_BAD; - goto leave; - } - stamp = parse_timestamp(nonce, &bohmac, 10); - if (bohmac == nonce || bohmac[0] != '-') { - retval = NONCE_BAD; - goto leave; - } - - noncelen = strlen(nonce); - expect = prepare_push_cert_nonce(service_dir, stamp); - if (noncelen != strlen(expect)) { - /* This is not even the right size. */ - retval = NONCE_BAD; - goto leave; - } - if (constant_memequal(expect, nonce, noncelen)) { - /* Not what we would have signed earlier */ - retval = NONCE_BAD; - goto leave; - } - - /* - * By how many seconds is this nonce stale? Negative value - * would mean it was issued by another server with its clock - * skewed in the future. - */ - ostamp = parse_timestamp(push_cert_nonce, NULL, 10); - nonce_stamp_slop = (long)ostamp - (long)stamp; - - if (nonce_stamp_slop_limit && - labs(nonce_stamp_slop) <= nonce_stamp_slop_limit) { - /* - * Pretend as if the received nonce (which passes the - * HMAC check, so it is not a forged by third-party) - * is what we issued. - */ - free((void *)push_cert_nonce); - push_cert_nonce = xstrdup(nonce); - retval = NONCE_OK; - } else { - retval = NONCE_SLOP; - } - -leave: - free(nonce); - free(expect); - return retval; -} - -/* - * Return 1 if there is no push_cert or if the push options in push_cert are - * the same as those in the argument; 0 otherwise. - */ -static int check_cert_push_options(const struct string_list *push_options) -{ - const char *buf = push_cert.buf; - int len = push_cert.len; - - char *option; - const char *next_line; - int options_seen = 0; - - int retval = 1; - - if (!len) - return 1; - - while ((option = find_header(buf, len, "push-option", &next_line))) { - len -= (next_line - buf); - buf = next_line; - options_seen++; - if (options_seen > push_options->nr - || strcmp(option, - push_options->items[options_seen - 1].string)) { - retval = 0; - goto leave; - } - free(option); - } - - if (options_seen != push_options->nr) - retval = 0; - -leave: - free(option); - return retval; -} - -static void prepare_push_cert_sha1(struct child_process *proc) -{ - static int already_done; - - if (!push_cert.len) - return; - - if (!already_done) { - int bogs /* beginning_of_gpg_sig */; - - already_done = 1; - if (write_object_file(push_cert.buf, push_cert.len, "blob", - &push_cert_oid)) - oidclr(&push_cert_oid); - - memset(&sigcheck, '\0', sizeof(sigcheck)); - - bogs = parse_signature(push_cert.buf, push_cert.len); - check_signature(push_cert.buf, bogs, push_cert.buf + bogs, - push_cert.len - bogs, &sigcheck); - - nonce_status = check_nonce(push_cert.buf, bogs); - } - if (!is_null_oid(&push_cert_oid)) { - strvec_pushf(&proc->env_array, "GIT_PUSH_CERT=%s", - oid_to_hex(&push_cert_oid)); - strvec_pushf(&proc->env_array, "GIT_PUSH_CERT_SIGNER=%s", - sigcheck.signer ? sigcheck.signer : ""); - strvec_pushf(&proc->env_array, "GIT_PUSH_CERT_KEY=%s", - sigcheck.key ? sigcheck.key : ""); - strvec_pushf(&proc->env_array, "GIT_PUSH_CERT_STATUS=%c", - sigcheck.result); - if (push_cert_nonce) { - strvec_pushf(&proc->env_array, - "GIT_PUSH_CERT_NONCE=%s", - push_cert_nonce); - strvec_pushf(&proc->env_array, - "GIT_PUSH_CERT_NONCE_STATUS=%s", - nonce_status); - if (nonce_status == NONCE_SLOP) - strvec_pushf(&proc->env_array, - "GIT_PUSH_CERT_NONCE_SLOP=%ld", - nonce_stamp_slop); - } - } -} - -struct receive_hook_feed_state { - struct command *cmd; - struct ref_push_report *report; - int skip_broken; - struct strbuf buf; - const struct string_list *push_options; -}; - -typedef int (*feed_fn)(void *, const char **, size_t *); -static int run_and_feed_hook(const char *hook_name, feed_fn feed, - struct receive_hook_feed_state *feed_state) -{ - struct child_process proc = CHILD_PROCESS_INIT; - struct async muxer; - const char *argv[2]; - int code; - - argv[0] = find_hook(hook_name); - if (!argv[0]) - return 0; - - argv[1] = NULL; - - proc.argv = argv; - proc.in = -1; - proc.stdout_to_stderr = 1; - proc.trace2_hook_name = hook_name; - - if (feed_state->push_options) { - int i; - for (i = 0; i < feed_state->push_options->nr; i++) - strvec_pushf(&proc.env_array, - "GIT_PUSH_OPTION_%d=%s", i, - feed_state->push_options->items[i].string); - strvec_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT=%d", - feed_state->push_options->nr); - } else - strvec_pushf(&proc.env_array, "GIT_PUSH_OPTION_COUNT"); - - if (tmp_objdir) - strvec_pushv(&proc.env_array, tmp_objdir_env(tmp_objdir)); - - if (use_sideband) { - memset(&muxer, 0, sizeof(muxer)); - muxer.proc = copy_to_sideband; - muxer.in = -1; - code = start_async(&muxer); - if (code) - return code; - proc.err = muxer.in; - } - - prepare_push_cert_sha1(&proc); - - code = start_command(&proc); - if (code) { - if (use_sideband) - finish_async(&muxer); - return code; - } - - sigchain_push(SIGPIPE, SIG_IGN); - - while (1) { - const char *buf; - size_t n; - if (feed(feed_state, &buf, &n)) - break; - if (write_in_full(proc.in, buf, n) < 0) - break; - } - close(proc.in); - if (use_sideband) - finish_async(&muxer); - - sigchain_pop(SIGPIPE); - - return finish_command(&proc); -} - -static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep) -{ - struct receive_hook_feed_state *state = state_; - struct command *cmd = state->cmd; - - while (cmd && - state->skip_broken && (cmd->error_string || cmd->did_not_exist)) - cmd = cmd->next; - if (!cmd) - return -1; /* EOF */ - if (!bufp) - return 0; /* OK, can feed something. */ - strbuf_reset(&state->buf); - if (!state->report) - state->report = cmd->report; - if (state->report) { - struct object_id *old_oid; - struct object_id *new_oid; - const char *ref_name; - - old_oid = state->report->old_oid ? state->report->old_oid : &cmd->old_oid; - new_oid = state->report->new_oid ? state->report->new_oid : &cmd->new_oid; - ref_name = state->report->ref_name ? state->report->ref_name : cmd->ref_name; - strbuf_addf(&state->buf, "%s %s %s\n", - oid_to_hex(old_oid), oid_to_hex(new_oid), - ref_name); - state->report = state->report->next; - if (!state->report) - state->cmd = cmd->next; - } else { - strbuf_addf(&state->buf, "%s %s %s\n", - oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid), - cmd->ref_name); - state->cmd = cmd->next; - } - if (bufp) { - *bufp = state->buf.buf; - *sizep = state->buf.len; - } - return 0; -} - -static int run_receive_hook(struct command *commands, - const char *hook_name, - int skip_broken, - const struct string_list *push_options) -{ - struct receive_hook_feed_state state; - int status; - - strbuf_init(&state.buf, 0); - state.cmd = commands; - state.skip_broken = skip_broken; - state.report = NULL; - if (feed_receive_hook(&state, NULL, NULL)) - return 0; - state.cmd = commands; - state.push_options = push_options; - status = run_and_feed_hook(hook_name, feed_receive_hook, &state); - strbuf_release(&state.buf); - return status; -} - -static int run_update_hook(struct command *cmd) -{ - const char *argv[5]; - struct child_process proc = CHILD_PROCESS_INIT; - int code; - - argv[0] = find_hook("update"); - if (!argv[0]) - return 0; - - argv[1] = cmd->ref_name; - argv[2] = oid_to_hex(&cmd->old_oid); - argv[3] = oid_to_hex(&cmd->new_oid); - argv[4] = NULL; - - proc.no_stdin = 1; - proc.stdout_to_stderr = 1; - proc.err = use_sideband ? -1 : 0; - proc.argv = argv; - proc.trace2_hook_name = "update"; - - code = start_command(&proc); - if (code) - return code; - if (use_sideband) - copy_to_sideband(proc.err, -1, NULL); - return finish_command(&proc); -} - -static struct command *find_command_by_refname(struct command *list, - const char *refname) -{ - for (; list; list = list->next) - if (!strcmp(list->ref_name, refname)) - return list; - return NULL; -} - -static int read_proc_receive_report(struct packet_reader *reader, - struct command *commands, - struct strbuf *errmsg) -{ - struct command *cmd; - struct command *hint = NULL; - struct ref_push_report *report = NULL; - int new_report = 0; - int code = 0; - int once = 0; - - for (;;) { - struct object_id old_oid, new_oid; - const char *head; - const char *refname; - char *p; - - if (packet_reader_read(reader) != PACKET_READ_NORMAL) - break; - - head = reader->line; - p = strchr(head, ' '); - if (!p) { - strbuf_addf(errmsg, "proc-receive reported incomplete status line: '%s'\n", head); - code = -1; - continue; - } - *p++ = '\0'; - if (!strcmp(head, "option")) { - const char *key, *val; - - if (!hint || !(report || new_report)) { - if (!once++) - strbuf_addstr(errmsg, "proc-receive reported 'option' without a matching 'ok/ng' directive\n"); - code = -1; - continue; - } - if (new_report) { - if (!hint->report) { - hint->report = xcalloc(1, sizeof(struct ref_push_report)); - report = hint->report; - } else { - report = hint->report; - while (report->next) - report = report->next; - report->next = xcalloc(1, sizeof(struct ref_push_report)); - report = report->next; - } - new_report = 0; - } - key = p; - p = strchr(key, ' '); - if (p) - *p++ = '\0'; - val = p; - if (!strcmp(key, "refname")) - report->ref_name = xstrdup_or_null(val); - else if (!strcmp(key, "old-oid") && val && - !parse_oid_hex(val, &old_oid, &val)) - report->old_oid = oiddup(&old_oid); - else if (!strcmp(key, "new-oid") && val && - !parse_oid_hex(val, &new_oid, &val)) - report->new_oid = oiddup(&new_oid); - else if (!strcmp(key, "forced-update")) - report->forced_update = 1; - else if (!strcmp(key, "fall-through")) - /* Fall through, let 'receive-pack' to execute it. */ - hint->run_proc_receive = 0; - continue; - } - - report = NULL; - new_report = 0; - refname = p; - p = strchr(refname, ' '); - if (p) - *p++ = '\0'; - if (strcmp(head, "ok") && strcmp(head, "ng")) { - strbuf_addf(errmsg, "proc-receive reported bad status '%s' on ref '%s'\n", - head, refname); - code = -1; - continue; - } - - /* first try searching at our hint, falling back to all refs */ - if (hint) - hint = find_command_by_refname(hint, refname); - if (!hint) - hint = find_command_by_refname(commands, refname); - if (!hint) { - strbuf_addf(errmsg, "proc-receive reported status on unknown ref: %s\n", - refname); - code = -1; - continue; - } - if (!hint->run_proc_receive) { - strbuf_addf(errmsg, "proc-receive reported status on unexpected ref: %s\n", - refname); - code = -1; - continue; - } - hint->run_proc_receive |= RUN_PROC_RECEIVE_RETURNED; - if (!strcmp(head, "ng")) { - if (p) - hint->error_string = xstrdup(p); - else - hint->error_string = "failed"; - code = -1; - continue; - } - new_report = 1; - } - - for (cmd = commands; cmd; cmd = cmd->next) - if (cmd->run_proc_receive && !cmd->error_string && - !(cmd->run_proc_receive & RUN_PROC_RECEIVE_RETURNED)) { - cmd->error_string = "proc-receive failed to report status"; - code = -1; - } - return code; -} - -static int run_proc_receive_hook(struct command *commands, - const struct string_list *push_options) -{ - struct child_process proc = CHILD_PROCESS_INIT; - struct async muxer; - struct command *cmd; - const char *argv[2]; - struct packet_reader reader; - struct strbuf cap = STRBUF_INIT; - struct strbuf errmsg = STRBUF_INIT; - int hook_use_push_options = 0; - int version = 0; - int code; - - argv[0] = find_hook("proc-receive"); - if (!argv[0]) { - rp_error("cannot find hook 'proc-receive'"); - return -1; - } - argv[1] = NULL; - - proc.argv = argv; - proc.in = -1; - proc.out = -1; - proc.trace2_hook_name = "proc-receive"; - - if (use_sideband) { - memset(&muxer, 0, sizeof(muxer)); - muxer.proc = copy_to_sideband; - muxer.in = -1; - code = start_async(&muxer); - if (code) - return code; - proc.err = muxer.in; - } else { - proc.err = 0; - } - - code = start_command(&proc); - if (code) { - if (use_sideband) - finish_async(&muxer); - return code; - } - - sigchain_push(SIGPIPE, SIG_IGN); - - /* Version negotiaton */ - packet_reader_init(&reader, proc.out, NULL, 0, - PACKET_READ_CHOMP_NEWLINE | - PACKET_READ_GENTLE_ON_EOF); - if (use_atomic) - strbuf_addstr(&cap, " atomic"); - if (use_push_options) - strbuf_addstr(&cap, " push-options"); - if (cap.len) { - packet_write_fmt(proc.in, "version=1%c%s\n", '\0', cap.buf + 1); - strbuf_release(&cap); - } else { - packet_write_fmt(proc.in, "version=1\n"); - } - packet_flush(proc.in); - - for (;;) { - int linelen; - - if (packet_reader_read(&reader) != PACKET_READ_NORMAL) - break; - - if (reader.pktlen > 8 && starts_with(reader.line, "version=")) { - version = atoi(reader.line + 8); - linelen = strlen(reader.line); - if (linelen < reader.pktlen) { - const char *feature_list = reader.line + linelen + 1; - if (parse_feature_request(feature_list, "push-options")) - hook_use_push_options = 1; - } - } - } - - if (version != 1) { - strbuf_addf(&errmsg, "proc-receive version '%d' is not supported", - version); - code = -1; - goto cleanup; - } - - /* Send commands */ - for (cmd = commands; cmd; cmd = cmd->next) { - if (!cmd->run_proc_receive || cmd->skip_update || cmd->error_string) - continue; - packet_write_fmt(proc.in, "%s %s %s", - oid_to_hex(&cmd->old_oid), - oid_to_hex(&cmd->new_oid), - cmd->ref_name); - } - packet_flush(proc.in); - - /* Send push options */ - if (hook_use_push_options) { - struct string_list_item *item; - - for_each_string_list_item(item, push_options) - packet_write_fmt(proc.in, "%s", item->string); - packet_flush(proc.in); - } - - /* Read result from proc-receive */ - code = read_proc_receive_report(&reader, commands, &errmsg); - -cleanup: - close(proc.in); - close(proc.out); - if (use_sideband) - finish_async(&muxer); - if (finish_command(&proc)) - code = -1; - if (errmsg.len >0) { - char *p = errmsg.buf; - - p += errmsg.len - 1; - if (*p == '\n') - *p = '\0'; - rp_error("%s", errmsg.buf); - strbuf_release(&errmsg); - } - sigchain_pop(SIGPIPE); - - return code; -} - -static char *refuse_unconfigured_deny_msg = - N_("By default, updating the current branch in a non-bare repository\n" - "is denied, because it will make the index and work tree inconsistent\n" - "with what you pushed, and will require 'git reset --hard' to match\n" - "the work tree to HEAD.\n" - "\n" - "You can set the 'receive.denyCurrentBranch' configuration variable\n" - "to 'ignore' or 'warn' in the remote repository to allow pushing into\n" - "its current branch; however, this is not recommended unless you\n" - "arranged to update its work tree to match what you pushed in some\n" - "other way.\n" - "\n" - "To squelch this message and still keep the default behaviour, set\n" - "'receive.denyCurrentBranch' configuration variable to 'refuse'."); - -static void refuse_unconfigured_deny(void) -{ - rp_error("%s", _(refuse_unconfigured_deny_msg)); -} - -static char *refuse_unconfigured_deny_delete_current_msg = - N_("By default, deleting the current branch is denied, because the next\n" - "'git clone' won't result in any file checked out, causing confusion.\n" - "\n" - "You can set 'receive.denyDeleteCurrent' configuration variable to\n" - "'warn' or 'ignore' in the remote repository to allow deleting the\n" - "current branch, with or without a warning message.\n" - "\n" - "To squelch this message, you can set it to 'refuse'."); - -static void refuse_unconfigured_deny_delete_current(void) -{ - rp_error("%s", _(refuse_unconfigured_deny_delete_current_msg)); -} - -static int command_singleton_iterator(void *cb_data, struct object_id *oid); -static int update_shallow_ref(struct command *cmd, struct shallow_info *si) -{ - struct shallow_lock shallow_lock = SHALLOW_LOCK_INIT; - struct oid_array extra = OID_ARRAY_INIT; - struct check_connected_options opt = CHECK_CONNECTED_INIT; - uint32_t mask = 1 << (cmd->index % 32); - int i; - - trace_printf_key(&trace_shallow, - "shallow: update_shallow_ref %s\n", cmd->ref_name); - for (i = 0; i < si->shallow->nr; i++) - if (si->used_shallow[i] && - (si->used_shallow[i][cmd->index / 32] & mask) && - !delayed_reachability_test(si, i)) - oid_array_append(&extra, &si->shallow->oid[i]); - - opt.env = tmp_objdir_env(tmp_objdir); - setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra); - if (check_connected(command_singleton_iterator, cmd, &opt)) { - rollback_shallow_file(the_repository, &shallow_lock); - oid_array_clear(&extra); - return -1; - } - - commit_shallow_file(the_repository, &shallow_lock); - - /* - * Make sure setup_alternate_shallow() for the next ref does - * not lose these new roots.. - */ - for (i = 0; i < extra.nr; i++) - register_shallow(the_repository, &extra.oid[i]); - - si->shallow_ref[cmd->index] = 0; - oid_array_clear(&extra); - return 0; -} - -/* - * NEEDSWORK: we should consolidate various implementions of "are we - * on an unborn branch?" test into one, and make the unified one more - * robust. !get_sha1() based check used here and elsewhere would not - * allow us to tell an unborn branch from corrupt ref, for example. - * For the purpose of fixing "deploy-to-update does not work when - * pushing into an empty repository" issue, this should suffice for - * now. - */ -static int head_has_history(void) -{ - struct object_id oid; - - return !get_oid("HEAD", &oid); -} - -static const char *push_to_deploy(unsigned char *sha1, - struct strvec *env, - const char *work_tree) -{ - const char *update_refresh[] = { - "update-index", "-q", "--ignore-submodules", "--refresh", NULL - }; - const char *diff_files[] = { - "diff-files", "--quiet", "--ignore-submodules", "--", NULL - }; - const char *diff_index[] = { - "diff-index", "--quiet", "--cached", "--ignore-submodules", - NULL, "--", NULL - }; - const char *read_tree[] = { - "read-tree", "-u", "-m", NULL, NULL - }; - struct child_process child = CHILD_PROCESS_INIT; - - child.argv = update_refresh; - child.env = env->v; - child.dir = work_tree; - child.no_stdin = 1; - child.stdout_to_stderr = 1; - child.git_cmd = 1; - if (run_command(&child)) - return "Up-to-date check failed"; - - /* run_command() does not clean up completely; reinitialize */ - child_process_init(&child); - child.argv = diff_files; - child.env = env->v; - child.dir = work_tree; - child.no_stdin = 1; - child.stdout_to_stderr = 1; - child.git_cmd = 1; - if (run_command(&child)) - return "Working directory has unstaged changes"; - - /* diff-index with either HEAD or an empty tree */ - diff_index[4] = head_has_history() ? "HEAD" : empty_tree_oid_hex(); - - child_process_init(&child); - child.argv = diff_index; - child.env = env->v; - child.no_stdin = 1; - child.no_stdout = 1; - child.stdout_to_stderr = 0; - child.git_cmd = 1; - if (run_command(&child)) - return "Working directory has staged changes"; - - read_tree[3] = hash_to_hex(sha1); - child_process_init(&child); - child.argv = read_tree; - child.env = env->v; - child.dir = work_tree; - child.no_stdin = 1; - child.no_stdout = 1; - child.stdout_to_stderr = 0; - child.git_cmd = 1; - if (run_command(&child)) - return "Could not update working tree to new HEAD"; - - return NULL; -} - -static const char *push_to_checkout_hook = "push-to-checkout"; - -static const char *push_to_checkout(unsigned char *hash, - struct strvec *env, - const char *work_tree) -{ - strvec_pushf(env, "GIT_WORK_TREE=%s", absolute_path(work_tree)); - if (run_hook_le(env->v, push_to_checkout_hook, - hash_to_hex(hash), NULL)) - return "push-to-checkout hook declined"; - else - return NULL; -} - -static const char *update_worktree(unsigned char *sha1, const struct worktree *worktree) -{ - const char *retval, *work_tree, *git_dir = NULL; - struct strvec env = STRVEC_INIT; - - if (worktree && worktree->path) - work_tree = worktree->path; - else if (git_work_tree_cfg) - work_tree = git_work_tree_cfg; - else - work_tree = ".."; - - if (is_bare_repository()) - return "denyCurrentBranch = updateInstead needs a worktree"; - if (worktree) - git_dir = get_worktree_git_dir(worktree); - if (!git_dir) - git_dir = get_git_dir(); - - strvec_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir)); - - if (!find_hook(push_to_checkout_hook)) - retval = push_to_deploy(sha1, &env, work_tree); - else - retval = push_to_checkout(sha1, &env, work_tree); - - strvec_clear(&env); - return retval; -} - -static const char *update(struct command *cmd, struct shallow_info *si) -{ - const char *name = cmd->ref_name; - struct strbuf namespaced_name_buf = STRBUF_INIT; - static char *namespaced_name; - const char *ret; - struct object_id *old_oid = &cmd->old_oid; - struct object_id *new_oid = &cmd->new_oid; - int do_update_worktree = 0; - const struct worktree *worktree = is_bare_repository() ? NULL : find_shared_symref("HEAD", name); - - /* only refs/... are allowed */ - if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) { - rp_error("refusing to create funny ref '%s' remotely", name); - return "funny refname"; - } - - strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name); - free(namespaced_name); - namespaced_name = strbuf_detach(&namespaced_name_buf, NULL); - - if (worktree) { - switch (deny_current_branch) { - case DENY_IGNORE: - break; - case DENY_WARN: - rp_warning("updating the current branch"); - break; - case DENY_REFUSE: - case DENY_UNCONFIGURED: - rp_error("refusing to update checked out branch: %s", name); - if (deny_current_branch == DENY_UNCONFIGURED) - refuse_unconfigured_deny(); - return "branch is currently checked out"; - case DENY_UPDATE_INSTEAD: - /* pass -- let other checks intervene first */ - do_update_worktree = 1; - break; - } - } - - if (!is_null_oid(new_oid) && !has_object_file(new_oid)) { - error("unpack should have generated %s, " - "but I can't find it!", oid_to_hex(new_oid)); - return "bad pack"; - } - - if (!is_null_oid(old_oid) && is_null_oid(new_oid)) { - if (deny_deletes && starts_with(name, "refs/heads/")) { - rp_error("denying ref deletion for %s", name); - return "deletion prohibited"; - } - - if (worktree || (head_name && !strcmp(namespaced_name, head_name))) { - switch (deny_delete_current) { - case DENY_IGNORE: - break; - case DENY_WARN: - rp_warning("deleting the current branch"); - break; - case DENY_REFUSE: - case DENY_UNCONFIGURED: - case DENY_UPDATE_INSTEAD: - if (deny_delete_current == DENY_UNCONFIGURED) - refuse_unconfigured_deny_delete_current(); - rp_error("refusing to delete the current branch: %s", name); - return "deletion of the current branch prohibited"; - default: - return "Invalid denyDeleteCurrent setting"; - } - } - } - - if (deny_non_fast_forwards && !is_null_oid(new_oid) && - !is_null_oid(old_oid) && - starts_with(name, "refs/heads/")) { - struct object *old_object, *new_object; - struct commit *old_commit, *new_commit; - - old_object = parse_object(the_repository, old_oid); - new_object = parse_object(the_repository, new_oid); - - if (!old_object || !new_object || - old_object->type != OBJ_COMMIT || - new_object->type != OBJ_COMMIT) { - error("bad sha1 objects for %s", name); - return "bad ref"; - } - old_commit = (struct commit *)old_object; - new_commit = (struct commit *)new_object; - if (!in_merge_bases(old_commit, new_commit)) { - rp_error("denying non-fast-forward %s" - " (you should pull first)", name); - return "non-fast-forward"; - } - } - if (run_update_hook(cmd)) { - rp_error("hook declined to update %s", name); - return "hook declined"; - } - - if (do_update_worktree) { - ret = update_worktree(new_oid->hash, find_shared_symref("HEAD", name)); - if (ret) - return ret; - } - - if (is_null_oid(new_oid)) { - struct strbuf err = STRBUF_INIT; - if (!parse_object(the_repository, old_oid)) { - old_oid = NULL; - if (ref_exists(name)) { - rp_warning("Allowing deletion of corrupt ref."); - } else { - rp_warning("Deleting a non-existent ref."); - cmd->did_not_exist = 1; - } - } - if (ref_transaction_delete(transaction, - namespaced_name, - old_oid, - 0, "push", &err)) { - rp_error("%s", err.buf); - strbuf_release(&err); - return "failed to delete"; - } - strbuf_release(&err); - return NULL; /* good */ - } - else { - struct strbuf err = STRBUF_INIT; - if (shallow_update && si->shallow_ref[cmd->index] && - update_shallow_ref(cmd, si)) - return "shallow error"; - - if (ref_transaction_update(transaction, - namespaced_name, - new_oid, old_oid, - 0, "push", - &err)) { - rp_error("%s", err.buf); - strbuf_release(&err); - - return "failed to update ref"; - } - strbuf_release(&err); - - return NULL; /* good */ - } -} - -static void run_update_post_hook(struct command *commands) -{ - struct command *cmd; - struct child_process proc = CHILD_PROCESS_INIT; - const char *hook; - - hook = find_hook("post-update"); - if (!hook) - return; - - for (cmd = commands; cmd; cmd = cmd->next) { - if (cmd->error_string || cmd->did_not_exist) - continue; - if (!proc.args.nr) - strvec_push(&proc.args, hook); - strvec_push(&proc.args, cmd->ref_name); - } - if (!proc.args.nr) - return; - - proc.no_stdin = 1; - proc.stdout_to_stderr = 1; - proc.err = use_sideband ? -1 : 0; - proc.trace2_hook_name = "post-update"; - - if (!start_command(&proc)) { - if (use_sideband) - copy_to_sideband(proc.err, -1, NULL); - finish_command(&proc); - } -} - -static void check_aliased_update_internal(struct command *cmd, - struct string_list *list, - const char *dst_name, int flag) -{ - struct string_list_item *item; - struct command *dst_cmd; - - if (!(flag & REF_ISSYMREF)) - return; - - if (!dst_name) { - rp_error("refusing update to broken symref '%s'", cmd->ref_name); - cmd->skip_update = 1; - cmd->error_string = "broken symref"; - return; - } - dst_name = strip_namespace(dst_name); - - if ((item = string_list_lookup(list, dst_name)) == NULL) - return; - - cmd->skip_update = 1; - - dst_cmd = (struct command *) item->util; - - if (oideq(&cmd->old_oid, &dst_cmd->old_oid) && - oideq(&cmd->new_oid, &dst_cmd->new_oid)) - return; - - dst_cmd->skip_update = 1; - - rp_error("refusing inconsistent update between symref '%s' (%s..%s) and" - " its target '%s' (%s..%s)", - cmd->ref_name, - find_unique_abbrev(&cmd->old_oid, DEFAULT_ABBREV), - find_unique_abbrev(&cmd->new_oid, DEFAULT_ABBREV), - dst_cmd->ref_name, - find_unique_abbrev(&dst_cmd->old_oid, DEFAULT_ABBREV), - find_unique_abbrev(&dst_cmd->new_oid, DEFAULT_ABBREV)); - - cmd->error_string = dst_cmd->error_string = - "inconsistent aliased update"; -} - -static void check_aliased_update(struct command *cmd, struct string_list *list) -{ - struct strbuf buf = STRBUF_INIT; - const char *dst_name; - int flag; - - strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name); - dst_name = resolve_ref_unsafe(buf.buf, 0, NULL, &flag); - check_aliased_update_internal(cmd, list, dst_name, flag); - strbuf_release(&buf); -} - -static void check_aliased_updates(struct command *commands) -{ - struct command *cmd; - struct string_list ref_list = STRING_LIST_INIT_NODUP; - - for (cmd = commands; cmd; cmd = cmd->next) { - struct string_list_item *item = - string_list_append(&ref_list, cmd->ref_name); - item->util = (void *)cmd; - } - string_list_sort(&ref_list); - - for (cmd = commands; cmd; cmd = cmd->next) { - if (!cmd->error_string) - check_aliased_update(cmd, &ref_list); - } - - string_list_clear(&ref_list, 0); -} - -static int command_singleton_iterator(void *cb_data, struct object_id *oid) -{ - struct command **cmd_list = cb_data; - struct command *cmd = *cmd_list; - - if (!cmd || is_null_oid(&cmd->new_oid)) - return -1; /* end of list */ - *cmd_list = NULL; /* this returns only one */ - oidcpy(oid, &cmd->new_oid); - return 0; -} - -static void set_connectivity_errors(struct command *commands, - struct shallow_info *si) -{ - struct command *cmd; - - for (cmd = commands; cmd; cmd = cmd->next) { - struct command *singleton = cmd; - struct check_connected_options opt = CHECK_CONNECTED_INIT; - - if (shallow_update && si->shallow_ref[cmd->index]) - /* to be checked in update_shallow_ref() */ - continue; - - opt.env = tmp_objdir_env(tmp_objdir); - if (!check_connected(command_singleton_iterator, &singleton, - &opt)) - continue; - - cmd->error_string = "missing necessary objects"; - } -} - -struct iterate_data { - struct command *cmds; - struct shallow_info *si; -}; - -static int iterate_receive_command_list(void *cb_data, struct object_id *oid) -{ - struct iterate_data *data = cb_data; - struct command **cmd_list = &data->cmds; - struct command *cmd = *cmd_list; - - for (; cmd; cmd = cmd->next) { - if (shallow_update && data->si->shallow_ref[cmd->index]) - /* to be checked in update_shallow_ref() */ - continue; - if (!is_null_oid(&cmd->new_oid) && !cmd->skip_update) { - oidcpy(oid, &cmd->new_oid); - *cmd_list = cmd->next; - return 0; - } - } - *cmd_list = NULL; - return -1; /* end of list */ -} - -static void reject_updates_to_hidden(struct command *commands) -{ - struct strbuf refname_full = STRBUF_INIT; - size_t prefix_len; - struct command *cmd; - - strbuf_addstr(&refname_full, get_git_namespace()); - prefix_len = refname_full.len; - - for (cmd = commands; cmd; cmd = cmd->next) { - if (cmd->error_string) - continue; - - strbuf_setlen(&refname_full, prefix_len); - strbuf_addstr(&refname_full, cmd->ref_name); - - if (!ref_is_hidden(cmd->ref_name, refname_full.buf)) - continue; - if (is_null_oid(&cmd->new_oid)) - cmd->error_string = "deny deleting a hidden ref"; - else - cmd->error_string = "deny updating a hidden ref"; - } - - strbuf_release(&refname_full); -} - -static int should_process_cmd(struct command *cmd) -{ - return !cmd->error_string && !cmd->skip_update; -} - -static void warn_if_skipped_connectivity_check(struct command *commands, - struct shallow_info *si) -{ - struct command *cmd; - int checked_connectivity = 1; - - for (cmd = commands; cmd; cmd = cmd->next) { - if (should_process_cmd(cmd) && si->shallow_ref[cmd->index]) { - error("BUG: connectivity check has not been run on ref %s", - cmd->ref_name); - checked_connectivity = 0; - } - } - if (!checked_connectivity) - BUG("connectivity check skipped???"); -} - -static void execute_commands_non_atomic(struct command *commands, - struct shallow_info *si) -{ - struct command *cmd; - struct strbuf err = STRBUF_INIT; - - for (cmd = commands; cmd; cmd = cmd->next) { - if (!should_process_cmd(cmd) || cmd->run_proc_receive) - continue; - - transaction = ref_transaction_begin(&err); - if (!transaction) { - rp_error("%s", err.buf); - strbuf_reset(&err); - cmd->error_string = "transaction failed to start"; - continue; - } - - cmd->error_string = update(cmd, si); - - if (!cmd->error_string - && ref_transaction_commit(transaction, &err)) { - rp_error("%s", err.buf); - strbuf_reset(&err); - cmd->error_string = "failed to update ref"; - } - ref_transaction_free(transaction); - } - strbuf_release(&err); -} - -static void execute_commands_atomic(struct command *commands, - struct shallow_info *si) -{ - struct command *cmd; - struct strbuf err = STRBUF_INIT; - const char *reported_error = "atomic push failure"; - - transaction = ref_transaction_begin(&err); - if (!transaction) { - rp_error("%s", err.buf); - strbuf_reset(&err); - reported_error = "transaction failed to start"; - goto failure; - } - - for (cmd = commands; cmd; cmd = cmd->next) { - if (!should_process_cmd(cmd) || cmd->run_proc_receive) - continue; - - cmd->error_string = update(cmd, si); - - if (cmd->error_string) - goto failure; - } - - if (ref_transaction_commit(transaction, &err)) { - rp_error("%s", err.buf); - reported_error = "atomic transaction failed"; - goto failure; - } - goto cleanup; - -failure: - for (cmd = commands; cmd; cmd = cmd->next) - if (!cmd->error_string) - cmd->error_string = reported_error; - -cleanup: - ref_transaction_free(transaction); - strbuf_release(&err); -} - -static void execute_commands(struct command *commands, - const char *unpacker_error, - struct shallow_info *si, - const struct string_list *push_options) -{ - struct check_connected_options opt = CHECK_CONNECTED_INIT; - struct command *cmd; - struct iterate_data data; - struct async muxer; - int err_fd = 0; - int run_proc_receive = 0; - - if (unpacker_error) { - for (cmd = commands; cmd; cmd = cmd->next) - cmd->error_string = "unpacker error"; - return; - } - - if (use_sideband) { - memset(&muxer, 0, sizeof(muxer)); - muxer.proc = copy_to_sideband; - muxer.in = -1; - if (!start_async(&muxer)) - err_fd = muxer.in; - /* ...else, continue without relaying sideband */ - } - - data.cmds = commands; - data.si = si; - opt.err_fd = err_fd; - opt.progress = err_fd && !quiet; - opt.env = tmp_objdir_env(tmp_objdir); - if (check_connected(iterate_receive_command_list, &data, &opt)) - set_connectivity_errors(commands, si); - - if (use_sideband) - finish_async(&muxer); - - reject_updates_to_hidden(commands); - - /* - * Try to find commands that have special prefix in their reference names, - * and mark them to run an external "proc-receive" hook later. - */ - if (proc_receive_ref) { - for (cmd = commands; cmd; cmd = cmd->next) { - if (!should_process_cmd(cmd)) - continue; - - if (proc_receive_ref_matches(cmd)) { - cmd->run_proc_receive = RUN_PROC_RECEIVE_SCHEDULED; - run_proc_receive = 1; - } - } - } - - if (run_receive_hook(commands, "pre-receive", 0, push_options)) { - for (cmd = commands; cmd; cmd = cmd->next) { - if (!cmd->error_string) - cmd->error_string = "pre-receive hook declined"; - } - return; - } - - /* - * Now we'll start writing out refs, which means the objects need - * to be in their final positions so that other processes can see them. - */ - if (tmp_objdir_migrate(tmp_objdir) < 0) { - for (cmd = commands; cmd; cmd = cmd->next) { - if (!cmd->error_string) - cmd->error_string = "unable to migrate objects to permanent storage"; - } - return; - } - tmp_objdir = NULL; - - check_aliased_updates(commands); - - free(head_name_to_free); - head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL); - - if (run_proc_receive && - run_proc_receive_hook(commands, push_options)) - for (cmd = commands; cmd; cmd = cmd->next) - if (!cmd->error_string && - !(cmd->run_proc_receive & RUN_PROC_RECEIVE_RETURNED) && - (cmd->run_proc_receive || use_atomic)) - cmd->error_string = "fail to run proc-receive hook"; - - if (use_atomic) - execute_commands_atomic(commands, si); - else - execute_commands_non_atomic(commands, si); - - if (shallow_update) - warn_if_skipped_connectivity_check(commands, si); -} - -static struct command **queue_command(struct command **tail, - const char *line, - int linelen) -{ - struct object_id old_oid, new_oid; - struct command *cmd; - const char *refname; - int reflen; - const char *p; - - if (parse_oid_hex(line, &old_oid, &p) || - *p++ != ' ' || - parse_oid_hex(p, &new_oid, &p) || - *p++ != ' ') - die("protocol error: expected old/new/ref, got '%s'", line); - - refname = p; - reflen = linelen - (p - line); - FLEX_ALLOC_MEM(cmd, ref_name, refname, reflen); - oidcpy(&cmd->old_oid, &old_oid); - oidcpy(&cmd->new_oid, &new_oid); - *tail = cmd; - return &cmd->next; -} - -static void queue_commands_from_cert(struct command **tail, - struct strbuf *push_cert) -{ - const char *boc, *eoc; - - if (*tail) - die("protocol error: got both push certificate and unsigned commands"); - - boc = strstr(push_cert->buf, "\n\n"); - if (!boc) - die("malformed push certificate %.*s", 100, push_cert->buf); - else - boc += 2; - eoc = push_cert->buf + parse_signature(push_cert->buf, push_cert->len); - - while (boc < eoc) { - const char *eol = memchr(boc, '\n', eoc - boc); - tail = queue_command(tail, boc, eol ? eol - boc : eoc - boc); - boc = eol ? eol + 1 : eoc; - } -} - -static struct command *read_head_info(struct packet_reader *reader, - struct oid_array *shallow) -{ - struct command *commands = NULL; - struct command **p = &commands; - for (;;) { - int linelen; - - if (packet_reader_read(reader) != PACKET_READ_NORMAL) - break; - - if (reader->pktlen > 8 && starts_with(reader->line, "shallow ")) { - struct object_id oid; - if (get_oid_hex(reader->line + 8, &oid)) - die("protocol error: expected shallow sha, got '%s'", - reader->line + 8); - oid_array_append(shallow, &oid); - continue; - } - - linelen = strlen(reader->line); - if (linelen < reader->pktlen) { - const char *feature_list = reader->line + linelen + 1; - const char *hash = NULL; - int len = 0; - if (parse_feature_request(feature_list, "report-status")) - report_status = 1; - if (parse_feature_request(feature_list, "report-status-v2")) - report_status_v2 = 1; - if (parse_feature_request(feature_list, "side-band-64k")) - use_sideband = LARGE_PACKET_MAX; - if (parse_feature_request(feature_list, "quiet")) - quiet = 1; - if (advertise_atomic_push - && parse_feature_request(feature_list, "atomic")) - use_atomic = 1; - if (advertise_push_options - && parse_feature_request(feature_list, "push-options")) - use_push_options = 1; - hash = parse_feature_value(feature_list, "object-format", &len, NULL); - if (!hash) { - hash = hash_algos[GIT_HASH_SHA1].name; - len = strlen(hash); - } - if (xstrncmpz(the_hash_algo->name, hash, len)) - die("error: unsupported object format '%s'", hash); - } - - if (!strcmp(reader->line, "push-cert")) { - int true_flush = 0; - int saved_options = reader->options; - reader->options &= ~PACKET_READ_CHOMP_NEWLINE; - - for (;;) { - packet_reader_read(reader); - if (reader->status == PACKET_READ_FLUSH) { - true_flush = 1; - break; - } - if (reader->status != PACKET_READ_NORMAL) { - die("protocol error: got an unexpected packet"); - } - if (!strcmp(reader->line, "push-cert-end\n")) - break; /* end of cert */ - strbuf_addstr(&push_cert, reader->line); - } - reader->options = saved_options; - - if (true_flush) - break; - continue; - } - - p = queue_command(p, reader->line, linelen); - } - - if (push_cert.len) - queue_commands_from_cert(p, &push_cert); - - return commands; -} - -static void read_push_options(struct packet_reader *reader, - struct string_list *options) -{ - while (1) { - if (packet_reader_read(reader) != PACKET_READ_NORMAL) - break; - - string_list_append(options, reader->line); - } -} - -static const char *parse_pack_header(struct pack_header *hdr) -{ - switch (read_pack_header(0, hdr)) { - case PH_ERROR_EOF: - return "eof before pack header was fully read"; - - case PH_ERROR_PACK_SIGNATURE: - return "protocol error (pack signature mismatch detected)"; - - case PH_ERROR_PROTOCOL: - return "protocol error (pack version unsupported)"; - - default: - return "unknown error in parse_pack_header"; - - case 0: - return NULL; - } -} - -static const char *pack_lockfile; - -static void push_header_arg(struct strvec *args, struct pack_header *hdr) -{ - strvec_pushf(args, "--pack_header=%"PRIu32",%"PRIu32, - ntohl(hdr->hdr_version), ntohl(hdr->hdr_entries)); -} - -static const char *unpack(int err_fd, struct shallow_info *si) -{ - struct pack_header hdr; - const char *hdr_err; - int status; - struct child_process child = CHILD_PROCESS_INIT; - int fsck_objects = (receive_fsck_objects >= 0 - ? receive_fsck_objects - : transfer_fsck_objects >= 0 - ? transfer_fsck_objects - : 0); - - hdr_err = parse_pack_header(&hdr); - if (hdr_err) { - if (err_fd > 0) - close(err_fd); - return hdr_err; - } - - if (si->nr_ours || si->nr_theirs) { - alt_shallow_file = setup_temporary_shallow(si->shallow); - strvec_push(&child.args, "--shallow-file"); - strvec_push(&child.args, alt_shallow_file); - } - - tmp_objdir = tmp_objdir_create(); - if (!tmp_objdir) { - if (err_fd > 0) - close(err_fd); - return "unable to create temporary object directory"; - } - child.env = tmp_objdir_env(tmp_objdir); - - /* - * Normally we just pass the tmp_objdir environment to the child - * processes that do the heavy lifting, but we may need to see these - * objects ourselves to set up shallow information. - */ - tmp_objdir_add_as_alternate(tmp_objdir); - - if (ntohl(hdr.hdr_entries) < unpack_limit) { - strvec_push(&child.args, "unpack-objects"); - push_header_arg(&child.args, &hdr); - if (quiet) - strvec_push(&child.args, "-q"); - if (fsck_objects) - strvec_pushf(&child.args, "--strict%s", - fsck_msg_types.buf); - if (max_input_size) - strvec_pushf(&child.args, "--max-input-size=%"PRIuMAX, - (uintmax_t)max_input_size); - child.no_stdout = 1; - child.err = err_fd; - child.git_cmd = 1; - status = run_command(&child); - if (status) - return "unpack-objects abnormal exit"; - } else { - char hostname[HOST_NAME_MAX + 1]; - - strvec_pushl(&child.args, "index-pack", "--stdin", NULL); - push_header_arg(&child.args, &hdr); - - if (xgethostname(hostname, sizeof(hostname))) - xsnprintf(hostname, sizeof(hostname), "localhost"); - strvec_pushf(&child.args, - "--keep=receive-pack %"PRIuMAX" on %s", - (uintmax_t)getpid(), - hostname); - - if (!quiet && err_fd) - strvec_push(&child.args, "--show-resolving-progress"); - if (use_sideband) - strvec_push(&child.args, "--report-end-of-input"); - if (fsck_objects) - strvec_pushf(&child.args, "--strict%s", - fsck_msg_types.buf); - if (!reject_thin) - strvec_push(&child.args, "--fix-thin"); - if (max_input_size) - strvec_pushf(&child.args, "--max-input-size=%"PRIuMAX, - (uintmax_t)max_input_size); - child.out = -1; - child.err = err_fd; - child.git_cmd = 1; - status = start_command(&child); - if (status) - return "index-pack fork failed"; - pack_lockfile = index_pack_lockfile(child.out); - close(child.out); - status = finish_command(&child); - if (status) - return "index-pack abnormal exit"; - reprepare_packed_git(the_repository); - } - return NULL; -} - -static const char *unpack_with_sideband(struct shallow_info *si) -{ - struct async muxer; - const char *ret; - - if (!use_sideband) - return unpack(0, si); - - use_keepalive = KEEPALIVE_AFTER_NUL; - memset(&muxer, 0, sizeof(muxer)); - muxer.proc = copy_to_sideband; - muxer.in = -1; - if (start_async(&muxer)) - return NULL; - - ret = unpack(muxer.in, si); - - finish_async(&muxer); - return ret; -} - -static void prepare_shallow_update(struct shallow_info *si) -{ - int i, j, k, bitmap_size = DIV_ROUND_UP(si->ref->nr, 32); - - ALLOC_ARRAY(si->used_shallow, si->shallow->nr); - assign_shallow_commits_to_refs(si, si->used_shallow, NULL); - - si->need_reachability_test = - xcalloc(si->shallow->nr, sizeof(*si->need_reachability_test)); - si->reachable = - xcalloc(si->shallow->nr, sizeof(*si->reachable)); - si->shallow_ref = xcalloc(si->ref->nr, sizeof(*si->shallow_ref)); - - for (i = 0; i < si->nr_ours; i++) - si->need_reachability_test[si->ours[i]] = 1; - - for (i = 0; i < si->shallow->nr; i++) { - if (!si->used_shallow[i]) - continue; - for (j = 0; j < bitmap_size; j++) { - if (!si->used_shallow[i][j]) - continue; - si->need_reachability_test[i]++; - for (k = 0; k < 32; k++) - if (si->used_shallow[i][j] & (1U << k)) - si->shallow_ref[j * 32 + k]++; - } - - /* - * true for those associated with some refs and belong - * in "ours" list aka "step 7 not done yet" - */ - si->need_reachability_test[i] = - si->need_reachability_test[i] > 1; - } - - /* - * keep hooks happy by forcing a temporary shallow file via - * env variable because we can't add --shallow-file to every - * command. check_connected() will be done with - * true .git/shallow though. - */ - setenv(GIT_SHALLOW_FILE_ENVIRONMENT, alt_shallow_file, 1); -} - -static void update_shallow_info(struct command *commands, - struct shallow_info *si, - struct oid_array *ref) -{ - struct command *cmd; - int *ref_status; - remove_nonexistent_theirs_shallow(si); - if (!si->nr_ours && !si->nr_theirs) { - shallow_update = 0; - return; - } - - for (cmd = commands; cmd; cmd = cmd->next) { - if (is_null_oid(&cmd->new_oid)) - continue; - oid_array_append(ref, &cmd->new_oid); - cmd->index = ref->nr - 1; - } - si->ref = ref; - - if (shallow_update) { - prepare_shallow_update(si); - return; - } - - ALLOC_ARRAY(ref_status, ref->nr); - assign_shallow_commits_to_refs(si, NULL, ref_status); - for (cmd = commands; cmd; cmd = cmd->next) { - if (is_null_oid(&cmd->new_oid)) - continue; - if (ref_status[cmd->index]) { - cmd->error_string = "shallow update not allowed"; - cmd->skip_update = 1; - } - } - free(ref_status); -} - -static void report(struct command *commands, const char *unpack_status) -{ - struct command *cmd; - struct strbuf buf = STRBUF_INIT; - - packet_buf_write(&buf, "unpack %s\n", - unpack_status ? unpack_status : "ok"); - for (cmd = commands; cmd; cmd = cmd->next) { - if (!cmd->error_string) - packet_buf_write(&buf, "ok %s\n", - cmd->ref_name); - else - packet_buf_write(&buf, "ng %s %s\n", - cmd->ref_name, cmd->error_string); - } - packet_buf_flush(&buf); - - if (use_sideband) - send_sideband(1, 1, buf.buf, buf.len, use_sideband); - else - write_or_die(1, buf.buf, buf.len); - strbuf_release(&buf); -} - -static void report_v2(struct command *commands, const char *unpack_status) -{ - struct command *cmd; - struct strbuf buf = STRBUF_INIT; - struct ref_push_report *report; - - packet_buf_write(&buf, "unpack %s\n", - unpack_status ? unpack_status : "ok"); - for (cmd = commands; cmd; cmd = cmd->next) { - int count = 0; - - if (cmd->error_string) { - packet_buf_write(&buf, "ng %s %s\n", - cmd->ref_name, - cmd->error_string); - continue; - } - packet_buf_write(&buf, "ok %s\n", - cmd->ref_name); - for (report = cmd->report; report; report = report->next) { - if (count++ > 0) - packet_buf_write(&buf, "ok %s\n", - cmd->ref_name); - if (report->ref_name) - packet_buf_write(&buf, "option refname %s\n", - report->ref_name); - if (report->old_oid) - packet_buf_write(&buf, "option old-oid %s\n", - oid_to_hex(report->old_oid)); - if (report->new_oid) - packet_buf_write(&buf, "option new-oid %s\n", - oid_to_hex(report->new_oid)); - if (report->forced_update) - packet_buf_write(&buf, "option forced-update\n"); - } - } - packet_buf_flush(&buf); - - if (use_sideband) - send_sideband(1, 1, buf.buf, buf.len, use_sideband); - else - write_or_die(1, buf.buf, buf.len); - strbuf_release(&buf); -} - -static int delete_only(struct command *commands) -{ - struct command *cmd; - for (cmd = commands; cmd; cmd = cmd->next) { - if (!is_null_oid(&cmd->new_oid)) - return 0; - } - return 1; -} - -int cmd_receive_pack(int argc, const char **argv, const char *prefix) -{ - int advertise_refs = 0; - struct command *commands; - struct oid_array shallow = OID_ARRAY_INIT; - struct oid_array ref = OID_ARRAY_INIT; - struct shallow_info si; - struct packet_reader reader; - - struct option options[] = { - OPT__QUIET(&quiet, N_("quiet")), - OPT_HIDDEN_BOOL(0, "stateless-rpc", &stateless_rpc, NULL), - OPT_HIDDEN_BOOL(0, "advertise-refs", &advertise_refs, NULL), - OPT_HIDDEN_BOOL(0, "reject-thin-pack-for-testing", &reject_thin, NULL), - OPT_END() - }; - - packet_trace_identity("receive-pack"); - - argc = parse_options(argc, argv, prefix, options, receive_pack_usage, 0); - - if (argc > 1) - usage_msg_opt(_("Too many arguments."), receive_pack_usage, options); - if (argc == 0) - usage_msg_opt(_("You must specify a directory."), receive_pack_usage, options); - - service_dir = argv[0]; - - setup_path(); - - if (!enter_repo(service_dir, 0)) - die("'%s' does not appear to be a git repository", service_dir); - - git_config(receive_pack_config, NULL); - if (cert_nonce_seed) - push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL)); - - if (0 <= transfer_unpack_limit) - unpack_limit = transfer_unpack_limit; - else if (0 <= receive_unpack_limit) - unpack_limit = receive_unpack_limit; - - switch (determine_protocol_version_server()) { - case protocol_v2: - /* - * push support for protocol v2 has not been implemented yet, - * so ignore the request to use v2 and fallback to using v0. - */ - break; - case protocol_v1: - /* - * v1 is just the original protocol with a version string, - * so just fall through after writing the version string. - */ - if (advertise_refs || !stateless_rpc) - packet_write_fmt(1, "version 1\n"); - - /* fallthrough */ - case protocol_v0: - break; - case protocol_unknown_version: - BUG("unknown protocol version"); - } - - if (advertise_refs || !stateless_rpc) { - write_head_info(); - } - if (advertise_refs) - return 0; - - packet_reader_init(&reader, 0, NULL, 0, - PACKET_READ_CHOMP_NEWLINE | - PACKET_READ_DIE_ON_ERR_PACKET); - - if ((commands = read_head_info(&reader, &shallow)) != NULL) { - const char *unpack_status = NULL; - struct string_list push_options = STRING_LIST_INIT_DUP; - - if (use_push_options) - read_push_options(&reader, &push_options); - if (!check_cert_push_options(&push_options)) { - struct command *cmd; - for (cmd = commands; cmd; cmd = cmd->next) - cmd->error_string = "inconsistent push options"; - } - - prepare_shallow_info(&si, &shallow); - if (!si.nr_ours && !si.nr_theirs) - shallow_update = 0; - if (!delete_only(commands)) { - unpack_status = unpack_with_sideband(&si); - update_shallow_info(commands, &si, &ref); - } - use_keepalive = KEEPALIVE_ALWAYS; - execute_commands(commands, unpack_status, &si, - &push_options); - if (pack_lockfile) - unlink_or_warn(pack_lockfile); - if (report_status_v2) - report_v2(commands, unpack_status); - else if (report_status) - report(commands, unpack_status); - run_receive_hook(commands, "post-receive", 1, - &push_options); - run_update_post_hook(commands); - string_list_clear(&push_options, 0); - if (auto_gc) { - const char *argv_gc_auto[] = { - "gc", "--auto", "--quiet", NULL, - }; - struct child_process proc = CHILD_PROCESS_INIT; - - proc.no_stdin = 1; - proc.stdout_to_stderr = 1; - proc.err = use_sideband ? -1 : 0; - proc.git_cmd = 1; - proc.argv = argv_gc_auto; - - close_object_store(the_repository->objects); - if (!start_command(&proc)) { - if (use_sideband) - copy_to_sideband(proc.err, -1, NULL); - finish_command(&proc); - } - } - if (auto_update_server_info) - update_server_info(0); - clear_shallow_info(&si); - } - if (use_sideband) - packet_flush(1); - oid_array_clear(&shallow); - oid_array_clear(&ref); - free((void *)push_cert_nonce); - return 0; -} diff --git a/third_party/git/builtin/reflog.c b/third_party/git/builtin/reflog.c deleted file mode 100644 index ca1d8079f320..000000000000 --- a/third_party/git/builtin/reflog.c +++ /dev/null @@ -1,789 +0,0 @@ -#include "builtin.h" -#include "config.h" -#include "lockfile.h" -#include "object-store.h" -#include "repository.h" -#include "commit.h" -#include "refs.h" -#include "dir.h" -#include "tree-walk.h" -#include "diff.h" -#include "revision.h" -#include "reachable.h" -#include "worktree.h" - -/* NEEDSWORK: switch to using parse_options */ -static const char reflog_expire_usage[] = -N_("git reflog expire [--expire=<time>] " - "[--expire-unreachable=<time>] " - "[--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] " - "[--verbose] [--all] <refs>..."); -static const char reflog_delete_usage[] = -N_("git reflog delete [--rewrite] [--updateref] " - "[--dry-run | -n] [--verbose] <refs>..."); -static const char reflog_exists_usage[] = -N_("git reflog exists <ref>"); - -static timestamp_t default_reflog_expire; -static timestamp_t default_reflog_expire_unreachable; - -struct cmd_reflog_expire_cb { - struct rev_info revs; - int stalefix; - timestamp_t expire_total; - timestamp_t expire_unreachable; - int recno; -}; - -struct expire_reflog_policy_cb { - enum { - UE_NORMAL, - UE_ALWAYS, - UE_HEAD - } unreachable_expire_kind; - struct commit_list *mark_list; - unsigned long mark_limit; - struct cmd_reflog_expire_cb cmd; - struct commit *tip_commit; - struct commit_list *tips; -}; - -struct collected_reflog { - struct object_id oid; - char reflog[FLEX_ARRAY]; -}; - -struct collect_reflog_cb { - struct collected_reflog **e; - int alloc; - int nr; - struct worktree *wt; -}; - -/* Remember to update object flag allocation in object.h */ -#define INCOMPLETE (1u<<10) -#define STUDYING (1u<<11) -#define REACHABLE (1u<<12) - -static int tree_is_complete(const struct object_id *oid) -{ - struct tree_desc desc; - struct name_entry entry; - int complete; - struct tree *tree; - - tree = lookup_tree(the_repository, oid); - if (!tree) - return 0; - if (tree->object.flags & SEEN) - return 1; - if (tree->object.flags & INCOMPLETE) - return 0; - - if (!tree->buffer) { - enum object_type type; - unsigned long size; - void *data = read_object_file(oid, &type, &size); - if (!data) { - tree->object.flags |= INCOMPLETE; - return 0; - } - tree->buffer = data; - tree->size = size; - } - init_tree_desc(&desc, tree->buffer, tree->size); - complete = 1; - while (tree_entry(&desc, &entry)) { - if (!has_object_file(&entry.oid) || - (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) { - tree->object.flags |= INCOMPLETE; - complete = 0; - } - } - free_tree_buffer(tree); - - if (complete) - tree->object.flags |= SEEN; - return complete; -} - -static int commit_is_complete(struct commit *commit) -{ - struct object_array study; - struct object_array found; - int is_incomplete = 0; - int i; - - /* early return */ - if (commit->object.flags & SEEN) - return 1; - if (commit->object.flags & INCOMPLETE) - return 0; - /* - * Find all commits that are reachable and are not marked as - * SEEN. Then make sure the trees and blobs contained are - * complete. After that, mark these commits also as SEEN. - * If some of the objects that are needed to complete this - * commit are missing, mark this commit as INCOMPLETE. - */ - memset(&study, 0, sizeof(study)); - memset(&found, 0, sizeof(found)); - add_object_array(&commit->object, NULL, &study); - add_object_array(&commit->object, NULL, &found); - commit->object.flags |= STUDYING; - while (study.nr) { - struct commit *c; - struct commit_list *parent; - - c = (struct commit *)object_array_pop(&study); - if (!c->object.parsed && !parse_object(the_repository, &c->object.oid)) - c->object.flags |= INCOMPLETE; - - if (c->object.flags & INCOMPLETE) { - is_incomplete = 1; - break; - } - else if (c->object.flags & SEEN) - continue; - for (parent = c->parents; parent; parent = parent->next) { - struct commit *p = parent->item; - if (p->object.flags & STUDYING) - continue; - p->object.flags |= STUDYING; - add_object_array(&p->object, NULL, &study); - add_object_array(&p->object, NULL, &found); - } - } - if (!is_incomplete) { - /* - * make sure all commits in "found" array have all the - * necessary objects. - */ - for (i = 0; i < found.nr; i++) { - struct commit *c = - (struct commit *)found.objects[i].item; - if (!tree_is_complete(get_commit_tree_oid(c))) { - is_incomplete = 1; - c->object.flags |= INCOMPLETE; - } - } - if (!is_incomplete) { - /* mark all found commits as complete, iow SEEN */ - for (i = 0; i < found.nr; i++) - found.objects[i].item->flags |= SEEN; - } - } - /* clear flags from the objects we traversed */ - for (i = 0; i < found.nr; i++) - found.objects[i].item->flags &= ~STUDYING; - if (is_incomplete) - commit->object.flags |= INCOMPLETE; - else { - /* - * If we come here, we have (1) traversed the ancestry chain - * from the "commit" until we reach SEEN commits (which are - * known to be complete), and (2) made sure that the commits - * encountered during the above traversal refer to trees that - * are complete. Which means that we know *all* the commits - * we have seen during this process are complete. - */ - for (i = 0; i < found.nr; i++) - found.objects[i].item->flags |= SEEN; - } - /* free object arrays */ - object_array_clear(&study); - object_array_clear(&found); - return !is_incomplete; -} - -static int keep_entry(struct commit **it, struct object_id *oid) -{ - struct commit *commit; - - if (is_null_oid(oid)) - return 1; - commit = lookup_commit_reference_gently(the_repository, oid, 1); - if (!commit) - return 0; - - /* - * Make sure everything in this commit exists. - * - * We have walked all the objects reachable from the refs - * and cache earlier. The commits reachable by this commit - * must meet SEEN commits -- and then we should mark them as - * SEEN as well. - */ - if (!commit_is_complete(commit)) - return 0; - *it = commit; - return 1; -} - -/* - * Starting from commits in the cb->mark_list, mark commits that are - * reachable from them. Stop the traversal at commits older than - * the expire_limit and queue them back, so that the caller can call - * us again to restart the traversal with longer expire_limit. - */ -static void mark_reachable(struct expire_reflog_policy_cb *cb) -{ - struct commit_list *pending; - timestamp_t expire_limit = cb->mark_limit; - struct commit_list *leftover = NULL; - - for (pending = cb->mark_list; pending; pending = pending->next) - pending->item->object.flags &= ~REACHABLE; - - pending = cb->mark_list; - while (pending) { - struct commit_list *parent; - struct commit *commit = pop_commit(&pending); - if (commit->object.flags & REACHABLE) - continue; - if (parse_commit(commit)) - continue; - commit->object.flags |= REACHABLE; - if (commit->date < expire_limit) { - commit_list_insert(commit, &leftover); - continue; - } - commit->object.flags |= REACHABLE; - parent = commit->parents; - while (parent) { - commit = parent->item; - parent = parent->next; - if (commit->object.flags & REACHABLE) - continue; - commit_list_insert(commit, &pending); - } - } - cb->mark_list = leftover; -} - -static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, struct object_id *oid) -{ - /* - * We may or may not have the commit yet - if not, look it - * up using the supplied sha1. - */ - if (!commit) { - if (is_null_oid(oid)) - return 0; - - commit = lookup_commit_reference_gently(the_repository, oid, - 1); - - /* Not a commit -- keep it */ - if (!commit) - return 0; - } - - /* Reachable from the current ref? Don't prune. */ - if (commit->object.flags & REACHABLE) - return 0; - - if (cb->mark_list && cb->mark_limit) { - cb->mark_limit = 0; /* dig down to the root */ - mark_reachable(cb); - } - - return !(commit->object.flags & REACHABLE); -} - -/* - * Return true iff the specified reflog entry should be expired. - */ -static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, timestamp_t timestamp, int tz, - const char *message, void *cb_data) -{ - struct expire_reflog_policy_cb *cb = cb_data; - struct commit *old_commit, *new_commit; - - if (timestamp < cb->cmd.expire_total) - return 1; - - old_commit = new_commit = NULL; - if (cb->cmd.stalefix && - (!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid))) - return 1; - - if (timestamp < cb->cmd.expire_unreachable) { - if (cb->unreachable_expire_kind == UE_ALWAYS) - return 1; - if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid)) - return 1; - } - - if (cb->cmd.recno && --(cb->cmd.recno) == 0) - return 1; - - return 0; -} - -static int push_tip_to_list(const char *refname, const struct object_id *oid, - int flags, void *cb_data) -{ - struct commit_list **list = cb_data; - struct commit *tip_commit; - if (flags & REF_ISSYMREF) - return 0; - tip_commit = lookup_commit_reference_gently(the_repository, oid, 1); - if (!tip_commit) - return 0; - commit_list_insert(tip_commit, list); - return 0; -} - -static int is_head(const char *refname) -{ - switch (ref_type(refname)) { - case REF_TYPE_OTHER_PSEUDOREF: - case REF_TYPE_MAIN_PSEUDOREF: - if (parse_worktree_ref(refname, NULL, NULL, &refname)) - BUG("not a worktree ref: %s", refname); - break; - default: - break; - } - return !strcmp(refname, "HEAD"); -} - -static void reflog_expiry_prepare(const char *refname, - const struct object_id *oid, - void *cb_data) -{ - struct expire_reflog_policy_cb *cb = cb_data; - - if (!cb->cmd.expire_unreachable || is_head(refname)) { - cb->tip_commit = NULL; - cb->unreachable_expire_kind = UE_HEAD; - } else { - cb->tip_commit = lookup_commit_reference_gently(the_repository, - oid, 1); - if (!cb->tip_commit) - cb->unreachable_expire_kind = UE_ALWAYS; - else - cb->unreachable_expire_kind = UE_NORMAL; - } - - if (cb->cmd.expire_unreachable <= cb->cmd.expire_total) - cb->unreachable_expire_kind = UE_ALWAYS; - - cb->mark_list = NULL; - cb->tips = NULL; - if (cb->unreachable_expire_kind != UE_ALWAYS) { - if (cb->unreachable_expire_kind == UE_HEAD) { - struct commit_list *elem; - - for_each_ref(push_tip_to_list, &cb->tips); - for (elem = cb->tips; elem; elem = elem->next) - commit_list_insert(elem->item, &cb->mark_list); - } else { - commit_list_insert(cb->tip_commit, &cb->mark_list); - } - cb->mark_limit = cb->cmd.expire_total; - mark_reachable(cb); - } -} - -static void reflog_expiry_cleanup(void *cb_data) -{ - struct expire_reflog_policy_cb *cb = cb_data; - - if (cb->unreachable_expire_kind != UE_ALWAYS) { - if (cb->unreachable_expire_kind == UE_HEAD) { - struct commit_list *elem; - for (elem = cb->tips; elem; elem = elem->next) - clear_commit_marks(elem->item, REACHABLE); - free_commit_list(cb->tips); - } else { - clear_commit_marks(cb->tip_commit, REACHABLE); - } - } -} - -static int collect_reflog(const char *ref, const struct object_id *oid, int unused, void *cb_data) -{ - struct collected_reflog *e; - struct collect_reflog_cb *cb = cb_data; - struct strbuf newref = STRBUF_INIT; - - /* - * Avoid collecting the same shared ref multiple times because - * they are available via all worktrees. - */ - if (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL) - return 0; - - strbuf_worktree_ref(cb->wt, &newref, ref); - FLEX_ALLOC_STR(e, reflog, newref.buf); - strbuf_release(&newref); - - oidcpy(&e->oid, oid); - ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc); - cb->e[cb->nr++] = e; - return 0; -} - -static struct reflog_expire_cfg { - struct reflog_expire_cfg *next; - timestamp_t expire_total; - timestamp_t expire_unreachable; - char pattern[FLEX_ARRAY]; -} *reflog_expire_cfg, **reflog_expire_cfg_tail; - -static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len) -{ - struct reflog_expire_cfg *ent; - - if (!reflog_expire_cfg_tail) - reflog_expire_cfg_tail = &reflog_expire_cfg; - - for (ent = reflog_expire_cfg; ent; ent = ent->next) - if (!strncmp(ent->pattern, pattern, len) && - ent->pattern[len] == '\0') - return ent; - - FLEX_ALLOC_MEM(ent, pattern, pattern, len); - *reflog_expire_cfg_tail = ent; - reflog_expire_cfg_tail = &(ent->next); - return ent; -} - -/* expiry timer slot */ -#define EXPIRE_TOTAL 01 -#define EXPIRE_UNREACH 02 - -static int reflog_expire_config(const char *var, const char *value, void *cb) -{ - const char *pattern, *key; - size_t pattern_len; - timestamp_t expire; - int slot; - struct reflog_expire_cfg *ent; - - if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0) - return git_default_config(var, value, cb); - - if (!strcmp(key, "reflogexpire")) { - slot = EXPIRE_TOTAL; - if (git_config_expiry_date(&expire, var, value)) - return -1; - } else if (!strcmp(key, "reflogexpireunreachable")) { - slot = EXPIRE_UNREACH; - if (git_config_expiry_date(&expire, var, value)) - return -1; - } else - return git_default_config(var, value, cb); - - if (!pattern) { - switch (slot) { - case EXPIRE_TOTAL: - default_reflog_expire = expire; - break; - case EXPIRE_UNREACH: - default_reflog_expire_unreachable = expire; - break; - } - return 0; - } - - ent = find_cfg_ent(pattern, pattern_len); - if (!ent) - return -1; - switch (slot) { - case EXPIRE_TOTAL: - ent->expire_total = expire; - break; - case EXPIRE_UNREACH: - ent->expire_unreachable = expire; - break; - } - return 0; -} - -static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, const char *ref) -{ - struct reflog_expire_cfg *ent; - - if (slot == (EXPIRE_TOTAL|EXPIRE_UNREACH)) - return; /* both given explicitly -- nothing to tweak */ - - for (ent = reflog_expire_cfg; ent; ent = ent->next) { - if (!wildmatch(ent->pattern, ref, 0)) { - if (!(slot & EXPIRE_TOTAL)) - cb->expire_total = ent->expire_total; - if (!(slot & EXPIRE_UNREACH)) - cb->expire_unreachable = ent->expire_unreachable; - return; - } - } - - /* - * If unconfigured, make stash never expire - */ - if (!strcmp(ref, "refs/stash")) { - if (!(slot & EXPIRE_TOTAL)) - cb->expire_total = 0; - if (!(slot & EXPIRE_UNREACH)) - cb->expire_unreachable = 0; - return; - } - - /* Nothing matched -- use the default value */ - if (!(slot & EXPIRE_TOTAL)) - cb->expire_total = default_reflog_expire; - if (!(slot & EXPIRE_UNREACH)) - cb->expire_unreachable = default_reflog_expire_unreachable; -} - -static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) -{ - struct expire_reflog_policy_cb cb; - timestamp_t now = time(NULL); - int i, status, do_all, all_worktrees = 1; - int explicit_expiry = 0; - unsigned int flags = 0; - - default_reflog_expire_unreachable = now - 30 * 24 * 3600; - default_reflog_expire = now - 90 * 24 * 3600; - git_config(reflog_expire_config, NULL); - - save_commit_buffer = 0; - do_all = status = 0; - memset(&cb, 0, sizeof(cb)); - - cb.cmd.expire_total = default_reflog_expire; - cb.cmd.expire_unreachable = default_reflog_expire_unreachable; - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n")) - flags |= EXPIRE_REFLOGS_DRY_RUN; - else if (skip_prefix(arg, "--expire=", &arg)) { - if (parse_expiry_date(arg, &cb.cmd.expire_total)) - die(_("'%s' is not a valid timestamp"), arg); - explicit_expiry |= EXPIRE_TOTAL; - } - else if (skip_prefix(arg, "--expire-unreachable=", &arg)) { - if (parse_expiry_date(arg, &cb.cmd.expire_unreachable)) - die(_("'%s' is not a valid timestamp"), arg); - explicit_expiry |= EXPIRE_UNREACH; - } - else if (!strcmp(arg, "--stale-fix")) - cb.cmd.stalefix = 1; - else if (!strcmp(arg, "--rewrite")) - flags |= EXPIRE_REFLOGS_REWRITE; - else if (!strcmp(arg, "--updateref")) - flags |= EXPIRE_REFLOGS_UPDATE_REF; - else if (!strcmp(arg, "--all")) - do_all = 1; - else if (!strcmp(arg, "--single-worktree")) - all_worktrees = 0; - else if (!strcmp(arg, "--verbose")) - flags |= EXPIRE_REFLOGS_VERBOSE; - else if (!strcmp(arg, "--")) { - i++; - break; - } - else if (arg[0] == '-') - usage(_(reflog_expire_usage)); - else - break; - } - - /* - * We can trust the commits and objects reachable from refs - * even in older repository. We cannot trust what's reachable - * from reflog if the repository was pruned with older git. - */ - if (cb.cmd.stalefix) { - repo_init_revisions(the_repository, &cb.cmd.revs, prefix); - if (flags & EXPIRE_REFLOGS_VERBOSE) - printf(_("Marking reachable objects...")); - mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL); - if (flags & EXPIRE_REFLOGS_VERBOSE) - putchar('\n'); - } - - if (do_all) { - struct collect_reflog_cb collected; - struct worktree **worktrees, **p; - int i; - - memset(&collected, 0, sizeof(collected)); - worktrees = get_worktrees(); - for (p = worktrees; *p; p++) { - if (!all_worktrees && !(*p)->is_current) - continue; - collected.wt = *p; - refs_for_each_reflog(get_worktree_ref_store(*p), - collect_reflog, &collected); - } - free_worktrees(worktrees); - for (i = 0; i < collected.nr; i++) { - struct collected_reflog *e = collected.e[i]; - set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog); - status |= reflog_expire(e->reflog, &e->oid, flags, - reflog_expiry_prepare, - should_expire_reflog_ent, - reflog_expiry_cleanup, - &cb); - free(e); - } - free(collected.e); - } - - for (; i < argc; i++) { - char *ref; - struct object_id oid; - if (!dwim_log(argv[i], strlen(argv[i]), &oid, &ref)) { - status |= error(_("%s points nowhere!"), argv[i]); - continue; - } - set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref); - status |= reflog_expire(ref, &oid, flags, - reflog_expiry_prepare, - should_expire_reflog_ent, - reflog_expiry_cleanup, - &cb); - } - return status; -} - -static int count_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, timestamp_t timestamp, int tz, - const char *message, void *cb_data) -{ - struct expire_reflog_policy_cb *cb = cb_data; - if (!cb->cmd.expire_total || timestamp < cb->cmd.expire_total) - cb->cmd.recno++; - return 0; -} - -static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) -{ - struct expire_reflog_policy_cb cb; - int i, status = 0; - unsigned int flags = 0; - - memset(&cb, 0, sizeof(cb)); - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n")) - flags |= EXPIRE_REFLOGS_DRY_RUN; - else if (!strcmp(arg, "--rewrite")) - flags |= EXPIRE_REFLOGS_REWRITE; - else if (!strcmp(arg, "--updateref")) - flags |= EXPIRE_REFLOGS_UPDATE_REF; - else if (!strcmp(arg, "--verbose")) - flags |= EXPIRE_REFLOGS_VERBOSE; - else if (!strcmp(arg, "--")) { - i++; - break; - } - else if (arg[0] == '-') - usage(_(reflog_delete_usage)); - else - break; - } - - if (argc - i < 1) - return error(_("no reflog specified to delete")); - - for ( ; i < argc; i++) { - const char *spec = strstr(argv[i], "@{"); - struct object_id oid; - char *ep, *ref; - int recno; - - if (!spec) { - status |= error(_("not a reflog: %s"), argv[i]); - continue; - } - - if (!dwim_log(argv[i], spec - argv[i], &oid, &ref)) { - status |= error(_("no reflog for '%s'"), argv[i]); - continue; - } - - recno = strtoul(spec + 2, &ep, 10); - if (*ep == '}') { - cb.cmd.recno = -recno; - for_each_reflog_ent(ref, count_reflog_ent, &cb); - } else { - cb.cmd.expire_total = approxidate(spec + 2); - for_each_reflog_ent(ref, count_reflog_ent, &cb); - cb.cmd.expire_total = 0; - } - - status |= reflog_expire(ref, &oid, flags, - reflog_expiry_prepare, - should_expire_reflog_ent, - reflog_expiry_cleanup, - &cb); - free(ref); - } - return status; -} - -static int cmd_reflog_exists(int argc, const char **argv, const char *prefix) -{ - int i, start = 0; - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!strcmp(arg, "--")) { - i++; - break; - } - else if (arg[0] == '-') - usage(_(reflog_exists_usage)); - else - break; - } - - start = i; - - if (argc - start != 1) - usage(_(reflog_exists_usage)); - - if (check_refname_format(argv[start], REFNAME_ALLOW_ONELEVEL)) - die(_("invalid ref format: %s"), argv[start]); - return !reflog_exists(argv[start]); -} - -/* - * main "reflog" - */ - -static const char reflog_usage[] = -N_("git reflog [ show | expire | delete | exists ]"); - -int cmd_reflog(int argc, const char **argv, const char *prefix) -{ - if (argc > 1 && !strcmp(argv[1], "-h")) - usage(_(reflog_usage)); - - /* With no command, we default to showing it. */ - if (argc < 2 || *argv[1] == '-') - return cmd_log_reflog(argc, argv, prefix); - - if (!strcmp(argv[1], "show")) - return cmd_log_reflog(argc - 1, argv + 1, prefix); - - if (!strcmp(argv[1], "expire")) - return cmd_reflog_expire(argc - 1, argv + 1, prefix); - - if (!strcmp(argv[1], "delete")) - return cmd_reflog_delete(argc - 1, argv + 1, prefix); - - if (!strcmp(argv[1], "exists")) - return cmd_reflog_exists(argc - 1, argv + 1, prefix); - - return cmd_log_reflog(argc, argv, prefix); -} diff --git a/third_party/git/builtin/remote-ext.c b/third_party/git/builtin/remote-ext.c deleted file mode 100644 index fd3538d4f0e6..000000000000 --- a/third_party/git/builtin/remote-ext.c +++ /dev/null @@ -1,202 +0,0 @@ -#include "builtin.h" -#include "transport.h" -#include "run-command.h" -#include "pkt-line.h" - -static const char usage_msg[] = - "git remote-ext <remote> <url>"; - -/* - * URL syntax: - * 'command [arg1 [arg2 [...]]]' Invoke command with given arguments. - * Special characters: - * '% ': Literal space in argument. - * '%%': Literal percent sign. - * '%S': Name of service (git-upload-pack/git-upload-archive/ - * git-receive-pack. - * '%s': Same as \s, but with possible git- prefix stripped. - * '%G': Only allowed as first 'character' of argument. Do not pass this - * Argument to command, instead send this as name of repository - * in in-line git://-style request (also activates sending this - * style of request). - * '%V': Only allowed as first 'character' of argument. Used in - * conjunction with '%G': Do not pass this argument to command, - * instead send this as vhost in git://-style request (note: does - * not activate sending git:// style request). - */ - -static char *git_req; -static char *git_req_vhost; - -static char *strip_escapes(const char *str, const char *service, - const char **next) -{ - size_t rpos = 0; - int escape = 0; - char special = 0; - const char *service_noprefix = service; - struct strbuf ret = STRBUF_INIT; - - skip_prefix(service_noprefix, "git-", &service_noprefix); - - /* Pass the service to command. */ - setenv("GIT_EXT_SERVICE", service, 1); - setenv("GIT_EXT_SERVICE_NOPREFIX", service_noprefix, 1); - - /* Scan the length of argument. */ - while (str[rpos] && (escape || str[rpos] != ' ')) { - if (escape) { - switch (str[rpos]) { - case ' ': - case '%': - case 's': - case 'S': - break; - case 'G': - case 'V': - special = str[rpos]; - if (rpos == 1) - break; - /* fallthrough */ - default: - die("Bad remote-ext placeholder '%%%c'.", - str[rpos]); - } - escape = 0; - } else - escape = (str[rpos] == '%'); - rpos++; - } - if (escape && !str[rpos]) - die("remote-ext command has incomplete placeholder"); - *next = str + rpos; - if (**next == ' ') - ++*next; /* Skip over space */ - - /* - * Do the actual placeholder substitution. The string will be short - * enough not to overflow integers. - */ - rpos = special ? 2 : 0; /* Skip first 2 bytes in specials. */ - escape = 0; - while (str[rpos] && (escape || str[rpos] != ' ')) { - if (escape) { - switch (str[rpos]) { - case ' ': - case '%': - strbuf_addch(&ret, str[rpos]); - break; - case 's': - strbuf_addstr(&ret, service_noprefix); - break; - case 'S': - strbuf_addstr(&ret, service); - break; - } - escape = 0; - } else - switch (str[rpos]) { - case '%': - escape = 1; - break; - default: - strbuf_addch(&ret, str[rpos]); - break; - } - rpos++; - } - switch (special) { - case 'G': - git_req = strbuf_detach(&ret, NULL); - return NULL; - case 'V': - git_req_vhost = strbuf_detach(&ret, NULL); - return NULL; - default: - return strbuf_detach(&ret, NULL); - } -} - -static void parse_argv(struct strvec *out, const char *arg, const char *service) -{ - while (*arg) { - char *expanded = strip_escapes(arg, service, &arg); - if (expanded) - strvec_push(out, expanded); - free(expanded); - } -} - -static void send_git_request(int stdin_fd, const char *serv, const char *repo, - const char *vhost) -{ - if (!vhost) - packet_write_fmt(stdin_fd, "%s %s%c", serv, repo, 0); - else - packet_write_fmt(stdin_fd, "%s %s%chost=%s%c", serv, repo, 0, - vhost, 0); -} - -static int run_child(const char *arg, const char *service) -{ - int r; - struct child_process child = CHILD_PROCESS_INIT; - - child.in = -1; - child.out = -1; - child.err = 0; - parse_argv(&child.args, arg, service); - - if (start_command(&child) < 0) - die("Can't run specified command"); - - if (git_req) - send_git_request(child.in, service, git_req, git_req_vhost); - - r = bidirectional_transfer_loop(child.out, child.in); - if (!r) - r = finish_command(&child); - else - finish_command(&child); - return r; -} - -#define MAXCOMMAND 4096 - -static int command_loop(const char *child) -{ - char buffer[MAXCOMMAND]; - - while (1) { - size_t i; - if (!fgets(buffer, MAXCOMMAND - 1, stdin)) { - if (ferror(stdin)) - die("Command input error"); - exit(0); - } - /* Strip end of line characters. */ - i = strlen(buffer); - while (i > 0 && isspace(buffer[i - 1])) - buffer[--i] = 0; - - if (!strcmp(buffer, "capabilities")) { - printf("*connect\n\n"); - fflush(stdout); - } else if (!strncmp(buffer, "connect ", 8)) { - printf("\n"); - fflush(stdout); - return run_child(child, buffer + 8); - } else { - fprintf(stderr, "Bad command"); - return 1; - } - } -} - -int cmd_remote_ext(int argc, const char **argv, const char *prefix) -{ - if (argc != 3) - usage(usage_msg); - - return command_loop(argv[2]); -} diff --git a/third_party/git/builtin/remote-fd.c b/third_party/git/builtin/remote-fd.c deleted file mode 100644 index 91dfe07e06a1..000000000000 --- a/third_party/git/builtin/remote-fd.c +++ /dev/null @@ -1,82 +0,0 @@ -#include "builtin.h" -#include "transport.h" - -static const char usage_msg[] = - "git remote-fd <remote> <url>"; - -/* - * URL syntax: - * 'fd::<inoutfd>[/<anything>]' Read/write socket pair - * <inoutfd>. - * 'fd::<infd>,<outfd>[/<anything>]' Read pipe <infd> and write - * pipe <outfd>. - * [foo] indicates 'foo' is optional. <anything> is any string. - * - * The data output to <outfd>/<inoutfd> should be passed unmolested to - * git-receive-pack/git-upload-pack/git-upload-archive and output of - * git-receive-pack/git-upload-pack/git-upload-archive should be passed - * unmolested to <infd>/<inoutfd>. - * - */ - -#define MAXCOMMAND 4096 - -static void command_loop(int input_fd, int output_fd) -{ - char buffer[MAXCOMMAND]; - - while (1) { - size_t i; - if (!fgets(buffer, MAXCOMMAND - 1, stdin)) { - if (ferror(stdin)) - die("Input error"); - return; - } - /* Strip end of line characters. */ - i = strlen(buffer); - while (i > 0 && isspace(buffer[i - 1])) - buffer[--i] = 0; - - if (!strcmp(buffer, "capabilities")) { - printf("*connect\n\n"); - fflush(stdout); - } else if (!strncmp(buffer, "connect ", 8)) { - printf("\n"); - fflush(stdout); - if (bidirectional_transfer_loop(input_fd, - output_fd)) - die("Copying data between file descriptors failed"); - return; - } else { - die("Bad command: %s", buffer); - } - } -} - -int cmd_remote_fd(int argc, const char **argv, const char *prefix) -{ - int input_fd = -1; - int output_fd = -1; - char *end; - - if (argc != 3) - usage(usage_msg); - - input_fd = (int)strtoul(argv[2], &end, 10); - - if ((end == argv[2]) || (*end != ',' && *end != '/' && *end)) - die("Bad URL syntax"); - - if (*end == '/' || !*end) { - output_fd = input_fd; - } else { - char *end2; - output_fd = (int)strtoul(end + 1, &end2, 10); - - if ((end2 == end + 1) || (*end2 != '/' && *end2)) - die("Bad URL syntax"); - } - - command_loop(input_fd, output_fd); - return 0; -} diff --git a/third_party/git/builtin/remote.c b/third_party/git/builtin/remote.c deleted file mode 100644 index 64b4b551eb02..000000000000 --- a/third_party/git/builtin/remote.c +++ /dev/null @@ -1,1723 +0,0 @@ -#include "builtin.h" -#include "config.h" -#include "parse-options.h" -#include "transport.h" -#include "remote.h" -#include "string-list.h" -#include "strbuf.h" -#include "run-command.h" -#include "rebase.h" -#include "refs.h" -#include "refspec.h" -#include "object-store.h" -#include "strvec.h" -#include "commit-reach.h" - -static const char * const builtin_remote_usage[] = { - N_("git remote [-v | --verbose]"), - N_("git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>"), - N_("git remote rename <old> <new>"), - N_("git remote remove <name>"), - N_("git remote set-head <name> (-a | --auto | -d | --delete | <branch>)"), - N_("git remote [-v | --verbose] show [-n] <name>"), - N_("git remote prune [-n | --dry-run] <name>"), - N_("git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]"), - N_("git remote set-branches [--add] <name> <branch>..."), - N_("git remote get-url [--push] [--all] <name>"), - N_("git remote set-url [--push] <name> <newurl> [<oldurl>]"), - N_("git remote set-url --add <name> <newurl>"), - N_("git remote set-url --delete <name> <url>"), - NULL -}; - -static const char * const builtin_remote_add_usage[] = { - N_("git remote add [<options>] <name> <url>"), - NULL -}; - -static const char * const builtin_remote_rename_usage[] = { - N_("git remote rename <old> <new>"), - NULL -}; - -static const char * const builtin_remote_rm_usage[] = { - N_("git remote remove <name>"), - NULL -}; - -static const char * const builtin_remote_sethead_usage[] = { - N_("git remote set-head <name> (-a | --auto | -d | --delete | <branch>)"), - NULL -}; - -static const char * const builtin_remote_setbranches_usage[] = { - N_("git remote set-branches <name> <branch>..."), - N_("git remote set-branches --add <name> <branch>..."), - NULL -}; - -static const char * const builtin_remote_show_usage[] = { - N_("git remote show [<options>] <name>"), - NULL -}; - -static const char * const builtin_remote_prune_usage[] = { - N_("git remote prune [<options>] <name>"), - NULL -}; - -static const char * const builtin_remote_update_usage[] = { - N_("git remote update [<options>] [<group> | <remote>]..."), - NULL -}; - -static const char * const builtin_remote_geturl_usage[] = { - N_("git remote get-url [--push] [--all] <name>"), - NULL -}; - -static const char * const builtin_remote_seturl_usage[] = { - N_("git remote set-url [--push] <name> <newurl> [<oldurl>]"), - N_("git remote set-url --add <name> <newurl>"), - N_("git remote set-url --delete <name> <url>"), - NULL -}; - -#define GET_REF_STATES (1<<0) -#define GET_HEAD_NAMES (1<<1) -#define GET_PUSH_REF_STATES (1<<2) - -static int verbose; - -static int fetch_remote(const char *name) -{ - const char *argv[] = { "fetch", name, NULL, NULL }; - if (verbose) { - argv[1] = "-v"; - argv[2] = name; - } - printf_ln(_("Updating %s"), name); - if (run_command_v_opt(argv, RUN_GIT_CMD)) - return error(_("Could not fetch %s"), name); - return 0; -} - -enum { - TAGS_UNSET = 0, - TAGS_DEFAULT = 1, - TAGS_SET = 2 -}; - -#define MIRROR_NONE 0 -#define MIRROR_FETCH 1 -#define MIRROR_PUSH 2 -#define MIRROR_BOTH (MIRROR_FETCH|MIRROR_PUSH) - -static void add_branch(const char *key, const char *branchname, - const char *remotename, int mirror, struct strbuf *tmp) -{ - strbuf_reset(tmp); - strbuf_addch(tmp, '+'); - if (mirror) - strbuf_addf(tmp, "refs/%s:refs/%s", - branchname, branchname); - else - strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s", - branchname, remotename, branchname); - git_config_set_multivar(key, tmp->buf, "^$", 0); -} - -static const char mirror_advice[] = -N_("--mirror is dangerous and deprecated; please\n" - "\t use --mirror=fetch or --mirror=push instead"); - -static int parse_mirror_opt(const struct option *opt, const char *arg, int not) -{ - unsigned *mirror = opt->value; - if (not) - *mirror = MIRROR_NONE; - else if (!arg) { - warning("%s", _(mirror_advice)); - *mirror = MIRROR_BOTH; - } - else if (!strcmp(arg, "fetch")) - *mirror = MIRROR_FETCH; - else if (!strcmp(arg, "push")) - *mirror = MIRROR_PUSH; - else - return error(_("unknown mirror argument: %s"), arg); - return 0; -} - -static int add(int argc, const char **argv) -{ - int fetch = 0, fetch_tags = TAGS_DEFAULT; - unsigned mirror = MIRROR_NONE; - struct string_list track = STRING_LIST_INIT_NODUP; - const char *master = NULL; - struct remote *remote; - struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT; - const char *name, *url; - int i; - - struct option options[] = { - OPT_BOOL('f', "fetch", &fetch, N_("fetch the remote branches")), - OPT_SET_INT(0, "tags", &fetch_tags, - N_("import all tags and associated objects when fetching"), - TAGS_SET), - OPT_SET_INT(0, NULL, &fetch_tags, - N_("or do not fetch any tag at all (--no-tags)"), TAGS_UNSET), - OPT_STRING_LIST('t', "track", &track, N_("branch"), - N_("branch(es) to track")), - OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")), - OPT_CALLBACK_F(0, "mirror", &mirror, "(push|fetch)", - N_("set up remote as a mirror to push to or fetch from"), - PARSE_OPT_OPTARG | PARSE_OPT_COMP_ARG, parse_mirror_opt), - OPT_END() - }; - - argc = parse_options(argc, argv, NULL, options, builtin_remote_add_usage, - 0); - - if (argc != 2) - usage_with_options(builtin_remote_add_usage, options); - - if (mirror && master) - die(_("specifying a master branch makes no sense with --mirror")); - if (mirror && !(mirror & MIRROR_FETCH) && track.nr) - die(_("specifying branches to track makes sense only with fetch mirrors")); - - name = argv[0]; - url = argv[1]; - - remote = remote_get(name); - if (remote_is_configured(remote, 1)) - die(_("remote %s already exists."), name); - - strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name); - if (!valid_fetch_refspec(buf2.buf)) - die(_("'%s' is not a valid remote name"), name); - - strbuf_addf(&buf, "remote.%s.url", name); - git_config_set(buf.buf, url); - - if (!mirror || mirror & MIRROR_FETCH) { - strbuf_reset(&buf); - strbuf_addf(&buf, "remote.%s.fetch", name); - if (track.nr == 0) - string_list_append(&track, "*"); - for (i = 0; i < track.nr; i++) { - add_branch(buf.buf, track.items[i].string, - name, mirror, &buf2); - } - } - - if (mirror & MIRROR_PUSH) { - strbuf_reset(&buf); - strbuf_addf(&buf, "remote.%s.mirror", name); - git_config_set(buf.buf, "true"); - } - - if (fetch_tags != TAGS_DEFAULT) { - strbuf_reset(&buf); - strbuf_addf(&buf, "remote.%s.tagopt", name); - git_config_set(buf.buf, - fetch_tags == TAGS_SET ? "--tags" : "--no-tags"); - } - - if (fetch && fetch_remote(name)) - return 1; - - if (master) { - strbuf_reset(&buf); - strbuf_addf(&buf, "refs/remotes/%s/HEAD", name); - - strbuf_reset(&buf2); - strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master); - - if (create_symref(buf.buf, buf2.buf, "remote add")) - return error(_("Could not setup master '%s'"), master); - } - - strbuf_release(&buf); - strbuf_release(&buf2); - string_list_clear(&track, 0); - - return 0; -} - -struct branch_info { - char *remote_name; - struct string_list merge; - enum rebase_type rebase; - char *push_remote_name; -}; - -static struct string_list branch_list = STRING_LIST_INIT_NODUP; - -static const char *abbrev_ref(const char *name, const char *prefix) -{ - skip_prefix(name, prefix, &name); - return name; -} -#define abbrev_branch(name) abbrev_ref((name), "refs/heads/") - -static int config_read_branches(const char *key, const char *value, void *cb) -{ - const char *orig_key = key; - char *name; - struct string_list_item *item; - struct branch_info *info; - enum { REMOTE, MERGE, REBASE, PUSH_REMOTE } type; - size_t key_len; - - if (!starts_with(key, "branch.")) - return 0; - - key += strlen("branch."); - if (strip_suffix(key, ".remote", &key_len)) - type = REMOTE; - else if (strip_suffix(key, ".merge", &key_len)) - type = MERGE; - else if (strip_suffix(key, ".rebase", &key_len)) - type = REBASE; - else if (strip_suffix(key, ".pushremote", &key_len)) - type = PUSH_REMOTE; - else - return 0; - name = xmemdupz(key, key_len); - - item = string_list_insert(&branch_list, name); - - if (!item->util) - item->util = xcalloc(1, sizeof(struct branch_info)); - info = item->util; - switch (type) { - case REMOTE: - if (info->remote_name) - warning(_("more than one %s"), orig_key); - info->remote_name = xstrdup(value); - break; - case MERGE: { - char *space = strchr(value, ' '); - value = abbrev_branch(value); - while (space) { - char *merge; - merge = xstrndup(value, space - value); - string_list_append(&info->merge, merge); - value = abbrev_branch(space + 1); - space = strchr(value, ' '); - } - string_list_append(&info->merge, xstrdup(value)); - break; - } - case REBASE: - /* - * Consider invalid values as false and check the - * truth value with >= REBASE_TRUE. - */ - info->rebase = rebase_parse_value(value); - break; - case PUSH_REMOTE: - if (info->push_remote_name) - warning(_("more than one %s"), orig_key); - info->push_remote_name = xstrdup(value); - break; - default: - BUG("unexpected type=%d", type); - } - - return 0; -} - -static void read_branches(void) -{ - if (branch_list.nr) - return; - git_config(config_read_branches, NULL); -} - -struct ref_states { - struct remote *remote; - struct string_list new_refs, stale, tracked, heads, push; - int queried; -}; - -static int get_ref_states(const struct ref *remote_refs, struct ref_states *states) -{ - struct ref *fetch_map = NULL, **tail = &fetch_map; - struct ref *ref, *stale_refs; - int i; - - for (i = 0; i < states->remote->fetch.nr; i++) - if (get_fetch_map(remote_refs, &states->remote->fetch.items[i], &tail, 1)) - die(_("Could not get fetch map for refspec %s"), - states->remote->fetch.raw[i]); - - states->new_refs.strdup_strings = 1; - states->tracked.strdup_strings = 1; - states->stale.strdup_strings = 1; - for (ref = fetch_map; ref; ref = ref->next) { - if (!ref->peer_ref || !ref_exists(ref->peer_ref->name)) - string_list_append(&states->new_refs, abbrev_branch(ref->name)); - else - string_list_append(&states->tracked, abbrev_branch(ref->name)); - } - stale_refs = get_stale_heads(&states->remote->fetch, fetch_map); - for (ref = stale_refs; ref; ref = ref->next) { - struct string_list_item *item = - string_list_append(&states->stale, abbrev_branch(ref->name)); - item->util = xstrdup(ref->name); - } - free_refs(stale_refs); - free_refs(fetch_map); - - string_list_sort(&states->new_refs); - string_list_sort(&states->tracked); - string_list_sort(&states->stale); - - return 0; -} - -struct push_info { - char *dest; - int forced; - enum { - PUSH_STATUS_CREATE = 0, - PUSH_STATUS_DELETE, - PUSH_STATUS_UPTODATE, - PUSH_STATUS_FASTFORWARD, - PUSH_STATUS_OUTOFDATE, - PUSH_STATUS_NOTQUERIED - } status; -}; - -static int get_push_ref_states(const struct ref *remote_refs, - struct ref_states *states) -{ - struct remote *remote = states->remote; - struct ref *ref, *local_refs, *push_map; - if (remote->mirror) - return 0; - - local_refs = get_local_heads(); - push_map = copy_ref_list(remote_refs); - - match_push_refs(local_refs, &push_map, &remote->push, MATCH_REFS_NONE); - - states->push.strdup_strings = 1; - for (ref = push_map; ref; ref = ref->next) { - struct string_list_item *item; - struct push_info *info; - - if (!ref->peer_ref) - continue; - oidcpy(&ref->new_oid, &ref->peer_ref->new_oid); - - item = string_list_append(&states->push, - abbrev_branch(ref->peer_ref->name)); - item->util = xcalloc(1, sizeof(struct push_info)); - info = item->util; - info->forced = ref->force; - info->dest = xstrdup(abbrev_branch(ref->name)); - - if (is_null_oid(&ref->new_oid)) { - info->status = PUSH_STATUS_DELETE; - } else if (oideq(&ref->old_oid, &ref->new_oid)) - info->status = PUSH_STATUS_UPTODATE; - else if (is_null_oid(&ref->old_oid)) - info->status = PUSH_STATUS_CREATE; - else if (has_object_file(&ref->old_oid) && - ref_newer(&ref->new_oid, &ref->old_oid)) - info->status = PUSH_STATUS_FASTFORWARD; - else - info->status = PUSH_STATUS_OUTOFDATE; - } - free_refs(local_refs); - free_refs(push_map); - return 0; -} - -static int get_push_ref_states_noquery(struct ref_states *states) -{ - int i; - struct remote *remote = states->remote; - struct string_list_item *item; - struct push_info *info; - - if (remote->mirror) - return 0; - - states->push.strdup_strings = 1; - if (!remote->push.nr) { - item = string_list_append(&states->push, _("(matching)")); - info = item->util = xcalloc(1, sizeof(struct push_info)); - info->status = PUSH_STATUS_NOTQUERIED; - info->dest = xstrdup(item->string); - } - for (i = 0; i < remote->push.nr; i++) { - const struct refspec_item *spec = &remote->push.items[i]; - if (spec->matching) - item = string_list_append(&states->push, _("(matching)")); - else if (strlen(spec->src)) - item = string_list_append(&states->push, spec->src); - else - item = string_list_append(&states->push, _("(delete)")); - - info = item->util = xcalloc(1, sizeof(struct push_info)); - info->forced = spec->force; - info->status = PUSH_STATUS_NOTQUERIED; - info->dest = xstrdup(spec->dst ? spec->dst : item->string); - } - return 0; -} - -static int get_head_names(const struct ref *remote_refs, struct ref_states *states) -{ - struct ref *ref, *matches; - struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map; - struct refspec_item refspec; - - memset(&refspec, 0, sizeof(refspec)); - refspec.force = 0; - refspec.pattern = 1; - refspec.src = refspec.dst = "refs/heads/*"; - states->heads.strdup_strings = 1; - get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0); - matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"), - fetch_map, 1); - for (ref = matches; ref; ref = ref->next) - string_list_append(&states->heads, abbrev_branch(ref->name)); - - free_refs(fetch_map); - free_refs(matches); - - return 0; -} - -struct known_remote { - struct known_remote *next; - struct remote *remote; -}; - -struct known_remotes { - struct remote *to_delete; - struct known_remote *list; -}; - -static int add_known_remote(struct remote *remote, void *cb_data) -{ - struct known_remotes *all = cb_data; - struct known_remote *r; - - if (!strcmp(all->to_delete->name, remote->name)) - return 0; - - r = xmalloc(sizeof(*r)); - r->remote = remote; - r->next = all->list; - all->list = r; - return 0; -} - -struct branches_for_remote { - struct remote *remote; - struct string_list *branches, *skipped; - struct known_remotes *keep; -}; - -static int add_branch_for_removal(const char *refname, - const struct object_id *oid, int flags, void *cb_data) -{ - struct branches_for_remote *branches = cb_data; - struct refspec_item refspec; - struct known_remote *kr; - - memset(&refspec, 0, sizeof(refspec)); - refspec.dst = (char *)refname; - if (remote_find_tracking(branches->remote, &refspec)) - return 0; - - /* don't delete a branch if another remote also uses it */ - for (kr = branches->keep->list; kr; kr = kr->next) { - memset(&refspec, 0, sizeof(refspec)); - refspec.dst = (char *)refname; - if (!remote_find_tracking(kr->remote, &refspec)) - return 0; - } - - /* don't delete non-remote-tracking refs */ - if (!starts_with(refname, "refs/remotes/")) { - /* advise user how to delete local branches */ - if (starts_with(refname, "refs/heads/")) - string_list_append(branches->skipped, - abbrev_branch(refname)); - /* silently skip over other non-remote refs */ - return 0; - } - - string_list_append(branches->branches, refname); - - return 0; -} - -struct rename_info { - const char *old_name; - const char *new_name; - struct string_list *remote_branches; -}; - -static int read_remote_branches(const char *refname, - const struct object_id *oid, int flags, void *cb_data) -{ - struct rename_info *rename = cb_data; - struct strbuf buf = STRBUF_INIT; - struct string_list_item *item; - int flag; - const char *symref; - - strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name); - if (starts_with(refname, buf.buf)) { - item = string_list_append(rename->remote_branches, refname); - symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING, - NULL, &flag); - if (symref && (flag & REF_ISSYMREF)) - item->util = xstrdup(symref); - else - item->util = NULL; - } - strbuf_release(&buf); - - return 0; -} - -static int migrate_file(struct remote *remote) -{ - struct strbuf buf = STRBUF_INIT; - int i; - - strbuf_addf(&buf, "remote.%s.url", remote->name); - for (i = 0; i < remote->url_nr; i++) - git_config_set_multivar(buf.buf, remote->url[i], "^$", 0); - strbuf_reset(&buf); - strbuf_addf(&buf, "remote.%s.push", remote->name); - for (i = 0; i < remote->push.raw_nr; i++) - git_config_set_multivar(buf.buf, remote->push.raw[i], "^$", 0); - strbuf_reset(&buf); - strbuf_addf(&buf, "remote.%s.fetch", remote->name); - for (i = 0; i < remote->fetch.raw_nr; i++) - git_config_set_multivar(buf.buf, remote->fetch.raw[i], "^$", 0); - if (remote->origin == REMOTE_REMOTES) - unlink_or_warn(git_path("remotes/%s", remote->name)); - else if (remote->origin == REMOTE_BRANCHES) - unlink_or_warn(git_path("branches/%s", remote->name)); - strbuf_release(&buf); - - return 0; -} - -struct push_default_info -{ - const char *old_name; - enum config_scope scope; - struct strbuf origin; - int linenr; -}; - -static int config_read_push_default(const char *key, const char *value, - void *cb) -{ - struct push_default_info* info = cb; - if (strcmp(key, "remote.pushdefault") || - !value || strcmp(value, info->old_name)) - return 0; - - info->scope = current_config_scope(); - strbuf_reset(&info->origin); - strbuf_addstr(&info->origin, current_config_name()); - info->linenr = current_config_line(); - - return 0; -} - -static void handle_push_default(const char* old_name, const char* new_name) -{ - struct push_default_info push_default = { - old_name, CONFIG_SCOPE_UNKNOWN, STRBUF_INIT, -1 }; - git_config(config_read_push_default, &push_default); - if (push_default.scope >= CONFIG_SCOPE_COMMAND) - ; /* pass */ - else if (push_default.scope >= CONFIG_SCOPE_LOCAL) { - int result = git_config_set_gently("remote.pushDefault", - new_name); - if (new_name && result && result != CONFIG_NOTHING_SET) - die(_("could not set '%s'"), "remote.pushDefault"); - else if (!new_name && result && result != CONFIG_NOTHING_SET) - die(_("could not unset '%s'"), "remote.pushDefault"); - } else if (push_default.scope >= CONFIG_SCOPE_SYSTEM) { - /* warn */ - warning(_("The %s configuration remote.pushDefault in:\n" - "\t%s:%d\n" - "now names the non-existent remote '%s'"), - config_scope_name(push_default.scope), - push_default.origin.buf, push_default.linenr, - old_name); - } -} - - -static int mv(int argc, const char **argv) -{ - struct option options[] = { - OPT_END() - }; - struct remote *oldremote, *newremote; - struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT, - old_remote_context = STRBUF_INIT; - struct string_list remote_branches = STRING_LIST_INIT_DUP; - struct rename_info rename; - int i, refspec_updated = 0; - - if (argc != 3) - usage_with_options(builtin_remote_rename_usage, options); - - rename.old_name = argv[1]; - rename.new_name = argv[2]; - rename.remote_branches = &remote_branches; - - oldremote = remote_get(rename.old_name); - if (!remote_is_configured(oldremote, 1)) - die(_("No such remote: '%s'"), rename.old_name); - - if (!strcmp(rename.old_name, rename.new_name) && oldremote->origin != REMOTE_CONFIG) - return migrate_file(oldremote); - - newremote = remote_get(rename.new_name); - if (remote_is_configured(newremote, 1)) - die(_("remote %s already exists."), rename.new_name); - - strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new_name); - if (!valid_fetch_refspec(buf.buf)) - die(_("'%s' is not a valid remote name"), rename.new_name); - - strbuf_reset(&buf); - strbuf_addf(&buf, "remote.%s", rename.old_name); - strbuf_addf(&buf2, "remote.%s", rename.new_name); - if (git_config_rename_section(buf.buf, buf2.buf) < 1) - return error(_("Could not rename config section '%s' to '%s'"), - buf.buf, buf2.buf); - - strbuf_reset(&buf); - strbuf_addf(&buf, "remote.%s.fetch", rename.new_name); - git_config_set_multivar(buf.buf, NULL, NULL, 1); - strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); - for (i = 0; i < oldremote->fetch.raw_nr; i++) { - char *ptr; - - strbuf_reset(&buf2); - strbuf_addstr(&buf2, oldremote->fetch.raw[i]); - ptr = strstr(buf2.buf, old_remote_context.buf); - if (ptr) { - refspec_updated = 1; - strbuf_splice(&buf2, - ptr-buf2.buf + strlen(":refs/remotes/"), - strlen(rename.old_name), rename.new_name, - strlen(rename.new_name)); - } else - warning(_("Not updating non-default fetch refspec\n" - "\t%s\n" - "\tPlease update the configuration manually if necessary."), - buf2.buf); - - git_config_set_multivar(buf.buf, buf2.buf, "^$", 0); - } - - read_branches(); - for (i = 0; i < branch_list.nr; i++) { - struct string_list_item *item = branch_list.items + i; - struct branch_info *info = item->util; - if (info->remote_name && !strcmp(info->remote_name, rename.old_name)) { - strbuf_reset(&buf); - strbuf_addf(&buf, "branch.%s.remote", item->string); - git_config_set(buf.buf, rename.new_name); - } - if (info->push_remote_name && !strcmp(info->push_remote_name, rename.old_name)) { - strbuf_reset(&buf); - strbuf_addf(&buf, "branch.%s.pushremote", item->string); - git_config_set(buf.buf, rename.new_name); - } - } - - if (!refspec_updated) - return 0; - - /* - * First remove symrefs, then rename the rest, finally create - * the new symrefs. - */ - for_each_ref(read_remote_branches, &rename); - for (i = 0; i < remote_branches.nr; i++) { - struct string_list_item *item = remote_branches.items + i; - int flag = 0; - - read_ref_full(item->string, RESOLVE_REF_READING, NULL, &flag); - if (!(flag & REF_ISSYMREF)) - continue; - if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF)) - die(_("deleting '%s' failed"), item->string); - } - for (i = 0; i < remote_branches.nr; i++) { - struct string_list_item *item = remote_branches.items + i; - - if (item->util) - continue; - strbuf_reset(&buf); - strbuf_addstr(&buf, item->string); - strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name), - rename.new_name, strlen(rename.new_name)); - strbuf_reset(&buf2); - strbuf_addf(&buf2, "remote: renamed %s to %s", - item->string, buf.buf); - if (rename_ref(item->string, buf.buf, buf2.buf)) - die(_("renaming '%s' failed"), item->string); - } - for (i = 0; i < remote_branches.nr; i++) { - struct string_list_item *item = remote_branches.items + i; - - if (!item->util) - continue; - strbuf_reset(&buf); - strbuf_addstr(&buf, item->string); - strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name), - rename.new_name, strlen(rename.new_name)); - strbuf_reset(&buf2); - strbuf_addstr(&buf2, item->util); - strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old_name), - rename.new_name, strlen(rename.new_name)); - strbuf_reset(&buf3); - strbuf_addf(&buf3, "remote: renamed %s to %s", - item->string, buf.buf); - if (create_symref(buf.buf, buf2.buf, buf3.buf)) - die(_("creating '%s' failed"), buf.buf); - } - string_list_clear(&remote_branches, 1); - - handle_push_default(rename.old_name, rename.new_name); - - return 0; -} - -static int rm(int argc, const char **argv) -{ - struct option options[] = { - OPT_END() - }; - struct remote *remote; - struct strbuf buf = STRBUF_INIT; - struct known_remotes known_remotes = { NULL, NULL }; - struct string_list branches = STRING_LIST_INIT_DUP; - struct string_list skipped = STRING_LIST_INIT_DUP; - struct branches_for_remote cb_data; - int i, result; - - memset(&cb_data, 0, sizeof(cb_data)); - cb_data.branches = &branches; - cb_data.skipped = &skipped; - cb_data.keep = &known_remotes; - - if (argc != 2) - usage_with_options(builtin_remote_rm_usage, options); - - remote = remote_get(argv[1]); - if (!remote_is_configured(remote, 1)) - die(_("No such remote: '%s'"), argv[1]); - - known_remotes.to_delete = remote; - for_each_remote(add_known_remote, &known_remotes); - - read_branches(); - for (i = 0; i < branch_list.nr; i++) { - struct string_list_item *item = branch_list.items + i; - struct branch_info *info = item->util; - if (info->remote_name && !strcmp(info->remote_name, remote->name)) { - const char *keys[] = { "remote", "merge", NULL }, **k; - for (k = keys; *k; k++) { - strbuf_reset(&buf); - strbuf_addf(&buf, "branch.%s.%s", - item->string, *k); - result = git_config_set_gently(buf.buf, NULL); - if (result && result != CONFIG_NOTHING_SET) - die(_("could not unset '%s'"), buf.buf); - } - } - if (info->push_remote_name && !strcmp(info->push_remote_name, remote->name)) { - strbuf_reset(&buf); - strbuf_addf(&buf, "branch.%s.pushremote", item->string); - result = git_config_set_gently(buf.buf, NULL); - if (result && result != CONFIG_NOTHING_SET) - die(_("could not unset '%s'"), buf.buf); - } - } - - /* - * We cannot just pass a function to for_each_ref() which deletes - * the branches one by one, since for_each_ref() relies on cached - * refs, which are invalidated when deleting a branch. - */ - cb_data.remote = remote; - result = for_each_ref(add_branch_for_removal, &cb_data); - strbuf_release(&buf); - - if (!result) - result = delete_refs("remote: remove", &branches, REF_NO_DEREF); - string_list_clear(&branches, 0); - - if (skipped.nr) { - fprintf_ln(stderr, - Q_("Note: A branch outside the refs/remotes/ hierarchy was not removed;\n" - "to delete it, use:", - "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n" - "to delete them, use:", - skipped.nr)); - for (i = 0; i < skipped.nr; i++) - fprintf(stderr, " git branch -d %s\n", - skipped.items[i].string); - } - string_list_clear(&skipped, 0); - - if (!result) { - strbuf_addf(&buf, "remote.%s", remote->name); - if (git_config_rename_section(buf.buf, NULL) < 1) - return error(_("Could not remove config section '%s'"), buf.buf); - - handle_push_default(remote->name, NULL); - } - - return result; -} - -static void clear_push_info(void *util, const char *string) -{ - struct push_info *info = util; - free(info->dest); - free(info); -} - -static void free_remote_ref_states(struct ref_states *states) -{ - string_list_clear(&states->new_refs, 0); - string_list_clear(&states->stale, 1); - string_list_clear(&states->tracked, 0); - string_list_clear(&states->heads, 0); - string_list_clear_func(&states->push, clear_push_info); -} - -static int append_ref_to_tracked_list(const char *refname, - const struct object_id *oid, int flags, void *cb_data) -{ - struct ref_states *states = cb_data; - struct refspec_item refspec; - - if (flags & REF_ISSYMREF) - return 0; - - memset(&refspec, 0, sizeof(refspec)); - refspec.dst = (char *)refname; - if (!remote_find_tracking(states->remote, &refspec)) - string_list_append(&states->tracked, abbrev_branch(refspec.src)); - - return 0; -} - -static int get_remote_ref_states(const char *name, - struct ref_states *states, - int query) -{ - struct transport *transport; - const struct ref *remote_refs; - - states->remote = remote_get(name); - if (!states->remote) - return error(_("No such remote: '%s'"), name); - - read_branches(); - - if (query) { - transport = transport_get(states->remote, states->remote->url_nr > 0 ? - states->remote->url[0] : NULL); - remote_refs = transport_get_remote_refs(transport, NULL); - transport_disconnect(transport); - - states->queried = 1; - if (query & GET_REF_STATES) - get_ref_states(remote_refs, states); - if (query & GET_HEAD_NAMES) - get_head_names(remote_refs, states); - if (query & GET_PUSH_REF_STATES) - get_push_ref_states(remote_refs, states); - } else { - for_each_ref(append_ref_to_tracked_list, states); - string_list_sort(&states->tracked); - get_push_ref_states_noquery(states); - } - - return 0; -} - -struct show_info { - struct string_list *list; - struct ref_states *states; - int width, width2; - int any_rebase; -}; - -static int add_remote_to_show_info(struct string_list_item *item, void *cb_data) -{ - struct show_info *info = cb_data; - int n = strlen(item->string); - if (n > info->width) - info->width = n; - string_list_insert(info->list, item->string); - return 0; -} - -static int show_remote_info_item(struct string_list_item *item, void *cb_data) -{ - struct show_info *info = cb_data; - struct ref_states *states = info->states; - const char *name = item->string; - - if (states->queried) { - const char *fmt = "%s"; - const char *arg = ""; - if (string_list_has_string(&states->new_refs, name)) { - fmt = _(" new (next fetch will store in remotes/%s)"); - arg = states->remote->name; - } else if (string_list_has_string(&states->tracked, name)) - arg = _(" tracked"); - else if (string_list_has_string(&states->stale, name)) - arg = _(" stale (use 'git remote prune' to remove)"); - else - arg = _(" ???"); - printf(" %-*s", info->width, name); - printf(fmt, arg); - printf("\n"); - } else - printf(" %s\n", name); - - return 0; -} - -static int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data) -{ - struct show_info *show_info = cb_data; - struct ref_states *states = show_info->states; - struct branch_info *branch_info = branch_item->util; - struct string_list_item *item; - int n; - - if (!branch_info->merge.nr || !branch_info->remote_name || - strcmp(states->remote->name, branch_info->remote_name)) - return 0; - if ((n = strlen(branch_item->string)) > show_info->width) - show_info->width = n; - if (branch_info->rebase >= REBASE_TRUE) - show_info->any_rebase = 1; - - item = string_list_insert(show_info->list, branch_item->string); - item->util = branch_info; - - return 0; -} - -static int show_local_info_item(struct string_list_item *item, void *cb_data) -{ - struct show_info *show_info = cb_data; - struct branch_info *branch_info = item->util; - struct string_list *merge = &branch_info->merge; - int width = show_info->width + 4; - int i; - - if (branch_info->rebase >= REBASE_TRUE && branch_info->merge.nr > 1) { - error(_("invalid branch.%s.merge; cannot rebase onto > 1 branch"), - item->string); - return 0; - } - - printf(" %-*s ", show_info->width, item->string); - if (branch_info->rebase >= REBASE_TRUE) { - const char *msg; - if (branch_info->rebase == REBASE_INTERACTIVE) - msg = _("rebases interactively onto remote %s"); - else if (branch_info->rebase == REBASE_MERGES) - msg = _("rebases interactively (with merges) onto " - "remote %s"); - else - msg = _("rebases onto remote %s"); - printf_ln(msg, merge->items[0].string); - return 0; - } else if (show_info->any_rebase) { - printf_ln(_(" merges with remote %s"), merge->items[0].string); - width++; - } else { - printf_ln(_("merges with remote %s"), merge->items[0].string); - } - for (i = 1; i < merge->nr; i++) - printf(_("%-*s and with remote %s\n"), width, "", - merge->items[i].string); - - return 0; -} - -static int add_push_to_show_info(struct string_list_item *push_item, void *cb_data) -{ - struct show_info *show_info = cb_data; - struct push_info *push_info = push_item->util; - struct string_list_item *item; - int n; - if ((n = strlen(push_item->string)) > show_info->width) - show_info->width = n; - if ((n = strlen(push_info->dest)) > show_info->width2) - show_info->width2 = n; - item = string_list_append(show_info->list, push_item->string); - item->util = push_item->util; - return 0; -} - -/* - * Sorting comparison for a string list that has push_info - * structs in its util field - */ -static int cmp_string_with_push(const void *va, const void *vb) -{ - const struct string_list_item *a = va; - const struct string_list_item *b = vb; - const struct push_info *a_push = a->util; - const struct push_info *b_push = b->util; - int cmp = strcmp(a->string, b->string); - return cmp ? cmp : strcmp(a_push->dest, b_push->dest); -} - -static int show_push_info_item(struct string_list_item *item, void *cb_data) -{ - struct show_info *show_info = cb_data; - struct push_info *push_info = item->util; - const char *src = item->string, *status = NULL; - - switch (push_info->status) { - case PUSH_STATUS_CREATE: - status = _("create"); - break; - case PUSH_STATUS_DELETE: - status = _("delete"); - src = _("(none)"); - break; - case PUSH_STATUS_UPTODATE: - status = _("up to date"); - break; - case PUSH_STATUS_FASTFORWARD: - status = _("fast-forwardable"); - break; - case PUSH_STATUS_OUTOFDATE: - status = _("local out of date"); - break; - case PUSH_STATUS_NOTQUERIED: - break; - } - if (status) { - if (push_info->forced) - printf_ln(_(" %-*s forces to %-*s (%s)"), show_info->width, src, - show_info->width2, push_info->dest, status); - else - printf_ln(_(" %-*s pushes to %-*s (%s)"), show_info->width, src, - show_info->width2, push_info->dest, status); - } else { - if (push_info->forced) - printf_ln(_(" %-*s forces to %s"), show_info->width, src, - push_info->dest); - else - printf_ln(_(" %-*s pushes to %s"), show_info->width, src, - push_info->dest); - } - return 0; -} - -static int get_one_entry(struct remote *remote, void *priv) -{ - struct string_list *list = priv; - struct strbuf url_buf = STRBUF_INIT; - const char **url; - int i, url_nr; - - if (remote->url_nr > 0) { - strbuf_addf(&url_buf, "%s (fetch)", remote->url[0]); - string_list_append(list, remote->name)->util = - strbuf_detach(&url_buf, NULL); - } else - string_list_append(list, remote->name)->util = NULL; - if (remote->pushurl_nr) { - url = remote->pushurl; - url_nr = remote->pushurl_nr; - } else { - url = remote->url; - url_nr = remote->url_nr; - } - for (i = 0; i < url_nr; i++) - { - strbuf_addf(&url_buf, "%s (push)", url[i]); - string_list_append(list, remote->name)->util = - strbuf_detach(&url_buf, NULL); - } - - return 0; -} - -static int show_all(void) -{ - struct string_list list = STRING_LIST_INIT_NODUP; - int result; - - list.strdup_strings = 1; - result = for_each_remote(get_one_entry, &list); - - if (!result) { - int i; - - string_list_sort(&list); - for (i = 0; i < list.nr; i++) { - struct string_list_item *item = list.items + i; - if (verbose) - printf("%s\t%s\n", item->string, - item->util ? (const char *)item->util : ""); - else { - if (i && !strcmp((item - 1)->string, item->string)) - continue; - printf("%s\n", item->string); - } - } - } - string_list_clear(&list, 1); - return result; -} - -static int show(int argc, const char **argv) -{ - int no_query = 0, result = 0, query_flag = 0; - struct option options[] = { - OPT_BOOL('n', NULL, &no_query, N_("do not query remotes")), - OPT_END() - }; - struct ref_states states; - struct string_list info_list = STRING_LIST_INIT_NODUP; - struct show_info info; - - argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage, - 0); - - if (argc < 1) - return show_all(); - - if (!no_query) - query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES); - - memset(&states, 0, sizeof(states)); - memset(&info, 0, sizeof(info)); - info.states = &states; - info.list = &info_list; - for (; argc; argc--, argv++) { - int i; - const char **url; - int url_nr; - - get_remote_ref_states(*argv, &states, query_flag); - - printf_ln(_("* remote %s"), *argv); - printf_ln(_(" Fetch URL: %s"), states.remote->url_nr > 0 ? - states.remote->url[0] : _("(no URL)")); - if (states.remote->pushurl_nr) { - url = states.remote->pushurl; - url_nr = states.remote->pushurl_nr; - } else { - url = states.remote->url; - url_nr = states.remote->url_nr; - } - for (i = 0; i < url_nr; i++) - /* - * TRANSLATORS: the colon ':' should align - * with the one in " Fetch URL: %s" - * translation. - */ - printf_ln(_(" Push URL: %s"), url[i]); - if (!i) - printf_ln(_(" Push URL: %s"), _("(no URL)")); - if (no_query) - printf_ln(_(" HEAD branch: %s"), _("(not queried)")); - else if (!states.heads.nr) - printf_ln(_(" HEAD branch: %s"), _("(unknown)")); - else if (states.heads.nr == 1) - printf_ln(_(" HEAD branch: %s"), states.heads.items[0].string); - else { - printf(_(" HEAD branch (remote HEAD is ambiguous," - " may be one of the following):\n")); - for (i = 0; i < states.heads.nr; i++) - printf(" %s\n", states.heads.items[i].string); - } - - /* remote branch info */ - info.width = 0; - for_each_string_list(&states.new_refs, add_remote_to_show_info, &info); - for_each_string_list(&states.tracked, add_remote_to_show_info, &info); - for_each_string_list(&states.stale, add_remote_to_show_info, &info); - if (info.list->nr) - printf_ln(Q_(" Remote branch:%s", - " Remote branches:%s", - info.list->nr), - no_query ? _(" (status not queried)") : ""); - for_each_string_list(info.list, show_remote_info_item, &info); - string_list_clear(info.list, 0); - - /* git pull info */ - info.width = 0; - info.any_rebase = 0; - for_each_string_list(&branch_list, add_local_to_show_info, &info); - if (info.list->nr) - printf_ln(Q_(" Local branch configured for 'git pull':", - " Local branches configured for 'git pull':", - info.list->nr)); - for_each_string_list(info.list, show_local_info_item, &info); - string_list_clear(info.list, 0); - - /* git push info */ - if (states.remote->mirror) - printf_ln(_(" Local refs will be mirrored by 'git push'")); - - info.width = info.width2 = 0; - for_each_string_list(&states.push, add_push_to_show_info, &info); - QSORT(info.list->items, info.list->nr, cmp_string_with_push); - if (info.list->nr) - printf_ln(Q_(" Local ref configured for 'git push'%s:", - " Local refs configured for 'git push'%s:", - info.list->nr), - no_query ? _(" (status not queried)") : ""); - for_each_string_list(info.list, show_push_info_item, &info); - string_list_clear(info.list, 0); - - free_remote_ref_states(&states); - } - - return result; -} - -static int set_head(int argc, const char **argv) -{ - int i, opt_a = 0, opt_d = 0, result = 0; - struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT; - char *head_name = NULL; - - struct option options[] = { - OPT_BOOL('a', "auto", &opt_a, - N_("set refs/remotes/<name>/HEAD according to remote")), - OPT_BOOL('d', "delete", &opt_d, - N_("delete refs/remotes/<name>/HEAD")), - OPT_END() - }; - argc = parse_options(argc, argv, NULL, options, builtin_remote_sethead_usage, - 0); - if (argc) - strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]); - - if (!opt_a && !opt_d && argc == 2) { - head_name = xstrdup(argv[1]); - } else if (opt_a && !opt_d && argc == 1) { - struct ref_states states; - memset(&states, 0, sizeof(states)); - get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES); - if (!states.heads.nr) - result |= error(_("Cannot determine remote HEAD")); - else if (states.heads.nr > 1) { - result |= error(_("Multiple remote HEAD branches. " - "Please choose one explicitly with:")); - for (i = 0; i < states.heads.nr; i++) - fprintf(stderr, " git remote set-head %s %s\n", - argv[0], states.heads.items[i].string); - } else - head_name = xstrdup(states.heads.items[0].string); - free_remote_ref_states(&states); - } else if (opt_d && !opt_a && argc == 1) { - if (delete_ref(NULL, buf.buf, NULL, REF_NO_DEREF)) - result |= error(_("Could not delete %s"), buf.buf); - } else - usage_with_options(builtin_remote_sethead_usage, options); - - if (head_name) { - strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name); - /* make sure it's valid */ - if (!ref_exists(buf2.buf)) - result |= error(_("Not a valid ref: %s"), buf2.buf); - else if (create_symref(buf.buf, buf2.buf, "remote set-head")) - result |= error(_("Could not setup %s"), buf.buf); - else if (opt_a) - printf("%s/HEAD set to %s\n", argv[0], head_name); - free(head_name); - } - - strbuf_release(&buf); - strbuf_release(&buf2); - return result; -} - -static int prune_remote(const char *remote, int dry_run) -{ - int result = 0; - struct ref_states states; - struct string_list refs_to_prune = STRING_LIST_INIT_NODUP; - struct string_list_item *item; - const char *dangling_msg = dry_run - ? _(" %s will become dangling!") - : _(" %s has become dangling!"); - - memset(&states, 0, sizeof(states)); - get_remote_ref_states(remote, &states, GET_REF_STATES); - - if (!states.stale.nr) { - free_remote_ref_states(&states); - return 0; - } - - printf_ln(_("Pruning %s"), remote); - printf_ln(_("URL: %s"), - states.remote->url_nr - ? states.remote->url[0] - : _("(no URL)")); - - for_each_string_list_item(item, &states.stale) - string_list_append(&refs_to_prune, item->util); - string_list_sort(&refs_to_prune); - - if (!dry_run) - result |= delete_refs("remote: prune", &refs_to_prune, 0); - - for_each_string_list_item(item, &states.stale) { - const char *refname = item->util; - - if (dry_run) - printf_ln(_(" * [would prune] %s"), - abbrev_ref(refname, "refs/remotes/")); - else - printf_ln(_(" * [pruned] %s"), - abbrev_ref(refname, "refs/remotes/")); - } - - warn_dangling_symrefs(stdout, dangling_msg, &refs_to_prune); - - string_list_clear(&refs_to_prune, 0); - free_remote_ref_states(&states); - return result; -} - -static int prune(int argc, const char **argv) -{ - int dry_run = 0, result = 0; - struct option options[] = { - OPT__DRY_RUN(&dry_run, N_("dry run")), - OPT_END() - }; - - argc = parse_options(argc, argv, NULL, options, builtin_remote_prune_usage, - 0); - - if (argc < 1) - usage_with_options(builtin_remote_prune_usage, options); - - for (; argc; argc--, argv++) - result |= prune_remote(*argv, dry_run); - - return result; -} - -static int get_remote_default(const char *key, const char *value, void *priv) -{ - if (strcmp(key, "remotes.default") == 0) { - int *found = priv; - *found = 1; - } - return 0; -} - -static int update(int argc, const char **argv) -{ - int i, prune = -1; - struct option options[] = { - OPT_BOOL('p', "prune", &prune, - N_("prune remotes after fetching")), - OPT_END() - }; - struct strvec fetch_argv = STRVEC_INIT; - int default_defined = 0; - int retval; - - argc = parse_options(argc, argv, NULL, options, builtin_remote_update_usage, - PARSE_OPT_KEEP_ARGV0); - - strvec_push(&fetch_argv, "fetch"); - - if (prune != -1) - strvec_push(&fetch_argv, prune ? "--prune" : "--no-prune"); - if (verbose) - strvec_push(&fetch_argv, "-v"); - strvec_push(&fetch_argv, "--multiple"); - if (argc < 2) - strvec_push(&fetch_argv, "default"); - for (i = 1; i < argc; i++) - strvec_push(&fetch_argv, argv[i]); - - if (strcmp(fetch_argv.v[fetch_argv.nr-1], "default") == 0) { - git_config(get_remote_default, &default_defined); - if (!default_defined) { - strvec_pop(&fetch_argv); - strvec_push(&fetch_argv, "--all"); - } - } - - retval = run_command_v_opt(fetch_argv.v, RUN_GIT_CMD); - strvec_clear(&fetch_argv); - return retval; -} - -static int remove_all_fetch_refspecs(const char *key) -{ - return git_config_set_multivar_gently(key, NULL, NULL, 1); -} - -static void add_branches(struct remote *remote, const char **branches, - const char *key) -{ - const char *remotename = remote->name; - int mirror = remote->mirror; - struct strbuf refspec = STRBUF_INIT; - - for (; *branches; branches++) - add_branch(key, *branches, remotename, mirror, &refspec); - - strbuf_release(&refspec); -} - -static int set_remote_branches(const char *remotename, const char **branches, - int add_mode) -{ - struct strbuf key = STRBUF_INIT; - struct remote *remote; - - strbuf_addf(&key, "remote.%s.fetch", remotename); - - remote = remote_get(remotename); - if (!remote_is_configured(remote, 1)) - die(_("No such remote '%s'"), remotename); - - if (!add_mode && remove_all_fetch_refspecs(key.buf)) { - strbuf_release(&key); - return 1; - } - add_branches(remote, branches, key.buf); - - strbuf_release(&key); - return 0; -} - -static int set_branches(int argc, const char **argv) -{ - int add_mode = 0; - struct option options[] = { - OPT_BOOL('\0', "add", &add_mode, N_("add branch")), - OPT_END() - }; - - argc = parse_options(argc, argv, NULL, options, - builtin_remote_setbranches_usage, 0); - if (argc == 0) { - error(_("no remote specified")); - usage_with_options(builtin_remote_setbranches_usage, options); - } - argv[argc] = NULL; - - return set_remote_branches(argv[0], argv + 1, add_mode); -} - -static int get_url(int argc, const char **argv) -{ - int i, push_mode = 0, all_mode = 0; - const char *remotename = NULL; - struct remote *remote; - const char **url; - int url_nr; - struct option options[] = { - OPT_BOOL('\0', "push", &push_mode, - N_("query push URLs rather than fetch URLs")), - OPT_BOOL('\0', "all", &all_mode, - N_("return all URLs")), - OPT_END() - }; - argc = parse_options(argc, argv, NULL, options, builtin_remote_geturl_usage, 0); - - if (argc != 1) - usage_with_options(builtin_remote_geturl_usage, options); - - remotename = argv[0]; - - remote = remote_get(remotename); - if (!remote_is_configured(remote, 1)) - die(_("No such remote '%s'"), remotename); - - url_nr = 0; - if (push_mode) { - url = remote->pushurl; - url_nr = remote->pushurl_nr; - } - /* else fetch mode */ - - /* Use the fetch URL when no push URLs were found or requested. */ - if (!url_nr) { - url = remote->url; - url_nr = remote->url_nr; - } - - if (!url_nr) - die(_("no URLs configured for remote '%s'"), remotename); - - if (all_mode) { - for (i = 0; i < url_nr; i++) - printf_ln("%s", url[i]); - } else { - printf_ln("%s", *url); - } - - return 0; -} - -static int set_url(int argc, const char **argv) -{ - int i, push_mode = 0, add_mode = 0, delete_mode = 0; - int matches = 0, negative_matches = 0; - const char *remotename = NULL; - const char *newurl = NULL; - const char *oldurl = NULL; - struct remote *remote; - regex_t old_regex; - const char **urlset; - int urlset_nr; - struct strbuf name_buf = STRBUF_INIT; - struct option options[] = { - OPT_BOOL('\0', "push", &push_mode, - N_("manipulate push URLs")), - OPT_BOOL('\0', "add", &add_mode, - N_("add URL")), - OPT_BOOL('\0', "delete", &delete_mode, - N_("delete URLs")), - OPT_END() - }; - argc = parse_options(argc, argv, NULL, options, builtin_remote_seturl_usage, - PARSE_OPT_KEEP_ARGV0); - - if (add_mode && delete_mode) - die(_("--add --delete doesn't make sense")); - - if (argc < 3 || argc > 4 || ((add_mode || delete_mode) && argc != 3)) - usage_with_options(builtin_remote_seturl_usage, options); - - remotename = argv[1]; - newurl = argv[2]; - if (argc > 3) - oldurl = argv[3]; - - if (delete_mode) - oldurl = newurl; - - remote = remote_get(remotename); - if (!remote_is_configured(remote, 1)) - die(_("No such remote '%s'"), remotename); - - if (push_mode) { - strbuf_addf(&name_buf, "remote.%s.pushurl", remotename); - urlset = remote->pushurl; - urlset_nr = remote->pushurl_nr; - } else { - strbuf_addf(&name_buf, "remote.%s.url", remotename); - urlset = remote->url; - urlset_nr = remote->url_nr; - } - - /* Special cases that add new entry. */ - if ((!oldurl && !delete_mode) || add_mode) { - if (add_mode) - git_config_set_multivar(name_buf.buf, newurl, - "^$", 0); - else - git_config_set(name_buf.buf, newurl); - goto out; - } - - /* Old URL specified. Demand that one matches. */ - if (regcomp(&old_regex, oldurl, REG_EXTENDED)) - die(_("Invalid old URL pattern: %s"), oldurl); - - for (i = 0; i < urlset_nr; i++) - if (!regexec(&old_regex, urlset[i], 0, NULL, 0)) - matches++; - else - negative_matches++; - if (!delete_mode && !matches) - die(_("No such URL found: %s"), oldurl); - if (delete_mode && !negative_matches && !push_mode) - die(_("Will not delete all non-push URLs")); - - regfree(&old_regex); - - if (!delete_mode) - git_config_set_multivar(name_buf.buf, newurl, oldurl, 0); - else - git_config_set_multivar(name_buf.buf, NULL, oldurl, 1); -out: - strbuf_release(&name_buf); - return 0; -} - -int cmd_remote(int argc, const char **argv, const char *prefix) -{ - struct option options[] = { - OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")), - OPT_END() - }; - int result; - - argc = parse_options(argc, argv, prefix, options, builtin_remote_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - - if (argc < 1) - result = show_all(); - else if (!strcmp(argv[0], "add")) - result = add(argc, argv); - else if (!strcmp(argv[0], "rename")) - result = mv(argc, argv); - else if (!strcmp(argv[0], "rm") || !strcmp(argv[0], "remove")) - result = rm(argc, argv); - else if (!strcmp(argv[0], "set-head")) - result = set_head(argc, argv); - else if (!strcmp(argv[0], "set-branches")) - result = set_branches(argc, argv); - else if (!strcmp(argv[0], "get-url")) - result = get_url(argc, argv); - else if (!strcmp(argv[0], "set-url")) - result = set_url(argc, argv); - else if (!strcmp(argv[0], "show")) - result = show(argc, argv); - else if (!strcmp(argv[0], "prune")) - result = prune(argc, argv); - else if (!strcmp(argv[0], "update")) - result = update(argc, argv); - else { - error(_("Unknown subcommand: %s"), argv[0]); - usage_with_options(builtin_remote_usage, options); - } - - return result ? 1 : 0; -} diff --git a/third_party/git/builtin/repack.c b/third_party/git/builtin/repack.c deleted file mode 100644 index 01e7767c7928..000000000000 --- a/third_party/git/builtin/repack.c +++ /dev/null @@ -1,580 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "dir.h" -#include "parse-options.h" -#include "run-command.h" -#include "sigchain.h" -#include "strbuf.h" -#include "string-list.h" -#include "strvec.h" -#include "midx.h" -#include "packfile.h" -#include "prune-packed.h" -#include "object-store.h" -#include "promisor-remote.h" -#include "shallow.h" - -static int delta_base_offset = 1; -static int pack_kept_objects = -1; -static int write_bitmaps = -1; -static int use_delta_islands; -static char *packdir, *packtmp; - -static const char *const git_repack_usage[] = { - N_("git repack [<options>]"), - NULL -}; - -static const char incremental_bitmap_conflict_error[] = N_( -"Incremental repacks are incompatible with bitmap indexes. Use\n" -"--no-write-bitmap-index or disable the pack.writebitmaps configuration." -); - - -static int repack_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "repack.usedeltabaseoffset")) { - delta_base_offset = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "repack.packkeptobjects")) { - pack_kept_objects = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "repack.writebitmaps") || - !strcmp(var, "pack.writebitmaps")) { - write_bitmaps = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "repack.usedeltaislands")) { - use_delta_islands = git_config_bool(var, value); - return 0; - } - return git_default_config(var, value, cb); -} - -/* - * Remove temporary $GIT_OBJECT_DIRECTORY/pack/.tmp-$$-pack-* files. - */ -static void remove_temporary_files(void) -{ - struct strbuf buf = STRBUF_INIT; - size_t dirlen, prefixlen; - DIR *dir; - struct dirent *e; - - dir = opendir(packdir); - if (!dir) - return; - - /* Point at the slash at the end of ".../objects/pack/" */ - dirlen = strlen(packdir) + 1; - strbuf_addstr(&buf, packtmp); - /* Hold the length of ".tmp-%d-pack-" */ - prefixlen = buf.len - dirlen; - - while ((e = readdir(dir))) { - if (strncmp(e->d_name, buf.buf + dirlen, prefixlen)) - continue; - strbuf_setlen(&buf, dirlen); - strbuf_addstr(&buf, e->d_name); - unlink(buf.buf); - } - closedir(dir); - strbuf_release(&buf); -} - -static void remove_pack_on_signal(int signo) -{ - remove_temporary_files(); - sigchain_pop(signo); - raise(signo); -} - -/* - * Adds all packs hex strings to the fname list, which do not - * have a corresponding .keep file. These packs are not to - * be kept if we are going to pack everything into one file. - */ -static void get_non_kept_pack_filenames(struct string_list *fname_list, - const struct string_list *extra_keep) -{ - DIR *dir; - struct dirent *e; - char *fname; - - if (!(dir = opendir(packdir))) - return; - - while ((e = readdir(dir)) != NULL) { - size_t len; - int i; - - for (i = 0; i < extra_keep->nr; i++) - if (!fspathcmp(e->d_name, extra_keep->items[i].string)) - break; - if (extra_keep->nr > 0 && i < extra_keep->nr) - continue; - - if (!strip_suffix(e->d_name, ".pack", &len)) - continue; - - fname = xmemdupz(e->d_name, len); - - if (!file_exists(mkpath("%s/%s.keep", packdir, fname))) - string_list_append_nodup(fname_list, fname); - else - free(fname); - } - closedir(dir); -} - -static void remove_redundant_pack(const char *dir_name, const char *base_name) -{ - struct strbuf buf = STRBUF_INIT; - struct multi_pack_index *m = get_local_multi_pack_index(the_repository); - strbuf_addf(&buf, "%s.pack", base_name); - if (m && midx_contains_pack(m, buf.buf)) - clear_midx_file(the_repository); - strbuf_insertf(&buf, 0, "%s/", dir_name); - unlink_pack_path(buf.buf, 1); - strbuf_release(&buf); -} - -struct pack_objects_args { - const char *window; - const char *window_memory; - const char *depth; - const char *threads; - const char *max_pack_size; - int no_reuse_delta; - int no_reuse_object; - int quiet; - int local; -}; - -static void prepare_pack_objects(struct child_process *cmd, - const struct pack_objects_args *args) -{ - strvec_push(&cmd->args, "pack-objects"); - if (args->window) - strvec_pushf(&cmd->args, "--window=%s", args->window); - if (args->window_memory) - strvec_pushf(&cmd->args, "--window-memory=%s", args->window_memory); - if (args->depth) - strvec_pushf(&cmd->args, "--depth=%s", args->depth); - if (args->threads) - strvec_pushf(&cmd->args, "--threads=%s", args->threads); - if (args->max_pack_size) - strvec_pushf(&cmd->args, "--max-pack-size=%s", args->max_pack_size); - if (args->no_reuse_delta) - strvec_pushf(&cmd->args, "--no-reuse-delta"); - if (args->no_reuse_object) - strvec_pushf(&cmd->args, "--no-reuse-object"); - if (args->local) - strvec_push(&cmd->args, "--local"); - if (args->quiet) - strvec_push(&cmd->args, "--quiet"); - if (delta_base_offset) - strvec_push(&cmd->args, "--delta-base-offset"); - strvec_push(&cmd->args, packtmp); - cmd->git_cmd = 1; - cmd->out = -1; -} - -/* - * Write oid to the given struct child_process's stdin, starting it first if - * necessary. - */ -static int write_oid(const struct object_id *oid, struct packed_git *pack, - uint32_t pos, void *data) -{ - struct child_process *cmd = data; - - if (cmd->in == -1) { - if (start_command(cmd)) - die(_("could not start pack-objects to repack promisor objects")); - } - - xwrite(cmd->in, oid_to_hex(oid), the_hash_algo->hexsz); - xwrite(cmd->in, "\n", 1); - return 0; -} - -static void repack_promisor_objects(const struct pack_objects_args *args, - struct string_list *names) -{ - struct child_process cmd = CHILD_PROCESS_INIT; - FILE *out; - struct strbuf line = STRBUF_INIT; - - prepare_pack_objects(&cmd, args); - cmd.in = -1; - - /* - * NEEDSWORK: Giving pack-objects only the OIDs without any ordering - * hints may result in suboptimal deltas in the resulting pack. See if - * the OIDs can be sent with fake paths such that pack-objects can use a - * {type -> existing pack order} ordering when computing deltas instead - * of a {type -> size} ordering, which may produce better deltas. - */ - for_each_packed_object(write_oid, &cmd, - FOR_EACH_OBJECT_PROMISOR_ONLY); - - if (cmd.in == -1) - /* No packed objects; cmd was never started */ - return; - - close(cmd.in); - - out = xfdopen(cmd.out, "r"); - while (strbuf_getline_lf(&line, out) != EOF) { - char *promisor_name; - int fd; - if (line.len != the_hash_algo->hexsz) - die(_("repack: Expecting full hex object ID lines only from pack-objects.")); - string_list_append(names, line.buf); - - /* - * pack-objects creates the .pack and .idx files, but not the - * .promisor file. Create the .promisor file, which is empty. - * - * NEEDSWORK: fetch-pack sometimes generates non-empty - * .promisor files containing the ref names and associated - * hashes at the point of generation of the corresponding - * packfile, but this would not preserve their contents. Maybe - * concatenate the contents of all .promisor files instead of - * just creating a new empty file. - */ - promisor_name = mkpathdup("%s-%s.promisor", packtmp, - line.buf); - fd = open(promisor_name, O_CREAT|O_EXCL|O_WRONLY, 0600); - if (fd < 0) - die_errno(_("unable to create '%s'"), promisor_name); - close(fd); - free(promisor_name); - } - fclose(out); - if (finish_command(&cmd)) - die(_("could not finish pack-objects to repack promisor objects")); -} - -#define ALL_INTO_ONE 1 -#define LOOSEN_UNREACHABLE 2 - -int cmd_repack(int argc, const char **argv, const char *prefix) -{ - struct { - const char *name; - unsigned optional:1; - } exts[] = { - {".pack"}, - {".idx"}, - {".bitmap", 1}, - {".promisor", 1}, - }; - struct child_process cmd = CHILD_PROCESS_INIT; - struct string_list_item *item; - struct string_list names = STRING_LIST_INIT_DUP; - struct string_list rollback = STRING_LIST_INIT_NODUP; - struct string_list existing_packs = STRING_LIST_INIT_DUP; - struct strbuf line = STRBUF_INIT; - int i, ext, ret, failed; - FILE *out; - - /* variables to be filled by option parsing */ - int pack_everything = 0; - int delete_redundant = 0; - const char *unpack_unreachable = NULL; - int keep_unreachable = 0; - struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; - int no_update_server_info = 0; - struct pack_objects_args po_args = {NULL}; - - struct option builtin_repack_options[] = { - OPT_BIT('a', NULL, &pack_everything, - N_("pack everything in a single pack"), ALL_INTO_ONE), - OPT_BIT('A', NULL, &pack_everything, - N_("same as -a, and turn unreachable objects loose"), - LOOSEN_UNREACHABLE | ALL_INTO_ONE), - OPT_BOOL('d', NULL, &delete_redundant, - N_("remove redundant packs, and run git-prune-packed")), - OPT_BOOL('f', NULL, &po_args.no_reuse_delta, - N_("pass --no-reuse-delta to git-pack-objects")), - OPT_BOOL('F', NULL, &po_args.no_reuse_object, - N_("pass --no-reuse-object to git-pack-objects")), - OPT_BOOL('n', NULL, &no_update_server_info, - N_("do not run git-update-server-info")), - OPT__QUIET(&po_args.quiet, N_("be quiet")), - OPT_BOOL('l', "local", &po_args.local, - N_("pass --local to git-pack-objects")), - OPT_BOOL('b', "write-bitmap-index", &write_bitmaps, - N_("write bitmap index")), - OPT_BOOL('i', "delta-islands", &use_delta_islands, - N_("pass --delta-islands to git-pack-objects")), - OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"), - N_("with -A, do not loosen objects older than this")), - OPT_BOOL('k', "keep-unreachable", &keep_unreachable, - N_("with -a, repack unreachable objects")), - OPT_STRING(0, "window", &po_args.window, N_("n"), - N_("size of the window used for delta compression")), - OPT_STRING(0, "window-memory", &po_args.window_memory, N_("bytes"), - N_("same as the above, but limit memory size instead of entries count")), - OPT_STRING(0, "depth", &po_args.depth, N_("n"), - N_("limits the maximum delta depth")), - OPT_STRING(0, "threads", &po_args.threads, N_("n"), - N_("limits the maximum number of threads")), - OPT_STRING(0, "max-pack-size", &po_args.max_pack_size, N_("bytes"), - N_("maximum size of each packfile")), - OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects, - N_("repack objects in packs marked with .keep")), - OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"), - N_("do not repack this pack")), - OPT_END() - }; - - git_config(repack_config, NULL); - - argc = parse_options(argc, argv, prefix, builtin_repack_options, - git_repack_usage, 0); - - if (delete_redundant && repository_format_precious_objects) - die(_("cannot delete packs in a precious-objects repo")); - - if (keep_unreachable && - (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE))) - die(_("--keep-unreachable and -A are incompatible")); - - if (write_bitmaps < 0) { - if (!(pack_everything & ALL_INTO_ONE) || - !is_bare_repository()) - write_bitmaps = 0; - } - if (pack_kept_objects < 0) - pack_kept_objects = write_bitmaps > 0; - - if (write_bitmaps && !(pack_everything & ALL_INTO_ONE)) - die(_(incremental_bitmap_conflict_error)); - - packdir = mkpathdup("%s/pack", get_object_directory()); - packtmp = mkpathdup("%s/.tmp-%d-pack", packdir, (int)getpid()); - - sigchain_push_common(remove_pack_on_signal); - - prepare_pack_objects(&cmd, &po_args); - - strvec_push(&cmd.args, "--keep-true-parents"); - if (!pack_kept_objects) - strvec_push(&cmd.args, "--honor-pack-keep"); - for (i = 0; i < keep_pack_list.nr; i++) - strvec_pushf(&cmd.args, "--keep-pack=%s", - keep_pack_list.items[i].string); - strvec_push(&cmd.args, "--non-empty"); - strvec_push(&cmd.args, "--all"); - strvec_push(&cmd.args, "--reflog"); - strvec_push(&cmd.args, "--indexed-objects"); - if (has_promisor_remote()) - strvec_push(&cmd.args, "--exclude-promisor-objects"); - if (write_bitmaps > 0) - strvec_push(&cmd.args, "--write-bitmap-index"); - else if (write_bitmaps < 0) - strvec_push(&cmd.args, "--write-bitmap-index-quiet"); - if (use_delta_islands) - strvec_push(&cmd.args, "--delta-islands"); - - if (pack_everything & ALL_INTO_ONE) { - get_non_kept_pack_filenames(&existing_packs, &keep_pack_list); - - repack_promisor_objects(&po_args, &names); - - if (existing_packs.nr && delete_redundant) { - if (unpack_unreachable) { - strvec_pushf(&cmd.args, - "--unpack-unreachable=%s", - unpack_unreachable); - strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1"); - } else if (pack_everything & LOOSEN_UNREACHABLE) { - strvec_push(&cmd.args, - "--unpack-unreachable"); - } else if (keep_unreachable) { - strvec_push(&cmd.args, "--keep-unreachable"); - strvec_push(&cmd.args, "--pack-loose-unreachable"); - } else { - strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1"); - } - } - } else { - strvec_push(&cmd.args, "--unpacked"); - strvec_push(&cmd.args, "--incremental"); - } - - cmd.no_stdin = 1; - - ret = start_command(&cmd); - if (ret) - return ret; - - out = xfdopen(cmd.out, "r"); - while (strbuf_getline_lf(&line, out) != EOF) { - if (line.len != the_hash_algo->hexsz) - die(_("repack: Expecting full hex object ID lines only from pack-objects.")); - string_list_append(&names, line.buf); - } - fclose(out); - ret = finish_command(&cmd); - if (ret) - return ret; - - if (!names.nr && !po_args.quiet) - printf_ln(_("Nothing new to pack.")); - - close_object_store(the_repository->objects); - - /* - * Ok we have prepared all new packfiles. - * First see if there are packs of the same name and if so - * if we can move them out of the way (this can happen if we - * repacked immediately after packing fully. - */ - failed = 0; - for_each_string_list_item(item, &names) { - for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { - char *fname, *fname_old; - - fname = mkpathdup("%s/pack-%s%s", packdir, - item->string, exts[ext].name); - if (!file_exists(fname)) { - free(fname); - continue; - } - - fname_old = mkpathdup("%s/old-%s%s", packdir, - item->string, exts[ext].name); - if (file_exists(fname_old)) - if (unlink(fname_old)) - failed = 1; - - if (!failed && rename(fname, fname_old)) { - free(fname); - free(fname_old); - failed = 1; - break; - } else { - string_list_append(&rollback, fname); - free(fname_old); - } - } - if (failed) - break; - } - if (failed) { - struct string_list rollback_failure = STRING_LIST_INIT_DUP; - for_each_string_list_item(item, &rollback) { - char *fname, *fname_old; - fname = mkpathdup("%s/%s", packdir, item->string); - fname_old = mkpathdup("%s/old-%s", packdir, item->string); - if (rename(fname_old, fname)) - string_list_append(&rollback_failure, fname); - free(fname); - free(fname_old); - } - - if (rollback_failure.nr) { - int i; - fprintf(stderr, - _("WARNING: Some packs in use have been renamed by\n" - "WARNING: prefixing old- to their name, in order to\n" - "WARNING: replace them with the new version of the\n" - "WARNING: file. But the operation failed, and the\n" - "WARNING: attempt to rename them back to their\n" - "WARNING: original names also failed.\n" - "WARNING: Please rename them in %s manually:\n"), packdir); - for (i = 0; i < rollback_failure.nr; i++) - fprintf(stderr, "WARNING: old-%s -> %s\n", - rollback_failure.items[i].string, - rollback_failure.items[i].string); - } - exit(1); - } - - /* Now the ones with the same name are out of the way... */ - for_each_string_list_item(item, &names) { - for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { - char *fname, *fname_old; - struct stat statbuffer; - int exists = 0; - fname = mkpathdup("%s/pack-%s%s", - packdir, item->string, exts[ext].name); - fname_old = mkpathdup("%s-%s%s", - packtmp, item->string, exts[ext].name); - if (!stat(fname_old, &statbuffer)) { - statbuffer.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); - chmod(fname_old, statbuffer.st_mode); - exists = 1; - } - if (exists || !exts[ext].optional) { - if (rename(fname_old, fname)) - die_errno(_("renaming '%s' failed"), fname_old); - } - free(fname); - free(fname_old); - } - } - - /* Remove the "old-" files */ - for_each_string_list_item(item, &names) { - for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { - char *fname; - fname = mkpathdup("%s/old-%s%s", - packdir, - item->string, - exts[ext].name); - if (remove_path(fname)) - warning(_("failed to remove '%s'"), fname); - free(fname); - } - } - - /* End of pack replacement. */ - - reprepare_packed_git(the_repository); - - if (delete_redundant) { - const int hexsz = the_hash_algo->hexsz; - int opts = 0; - string_list_sort(&names); - for_each_string_list_item(item, &existing_packs) { - char *sha1; - size_t len = strlen(item->string); - if (len < hexsz) - continue; - sha1 = item->string + len - hexsz; - if (!string_list_has_string(&names, sha1)) - remove_redundant_pack(packdir, item->string); - } - if (!po_args.quiet && isatty(2)) - opts |= PRUNE_PACKED_VERBOSE; - prune_packed_objects(opts); - - if (!keep_unreachable && - (!(pack_everything & LOOSEN_UNREACHABLE) || - unpack_unreachable) && - is_repository_shallow(the_repository)) - prune_shallow(PRUNE_QUICK); - } - - if (!no_update_server_info) - update_server_info(0); - remove_temporary_files(); - - if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0)) - write_midx_file(get_object_directory(), 0); - - string_list_clear(&names, 0); - string_list_clear(&rollback, 0); - string_list_clear(&existing_packs, 0); - strbuf_release(&line); - - return 0; -} diff --git a/third_party/git/builtin/replace.c b/third_party/git/builtin/replace.c deleted file mode 100644 index cd4876591174..000000000000 --- a/third_party/git/builtin/replace.c +++ /dev/null @@ -1,624 +0,0 @@ -/* - * Builtin "git replace" - * - * Copyright (c) 2008 Christian Couder <chriscool@tuxfamily.org> - * - * Based on builtin/tag.c by Kristian Hรธgsberg <krh@redhat.com> - * and Carlos Rica <jasampler@gmail.com> that was itself based on - * git-tag.sh and mktag.c by Linus Torvalds. - */ - -#include "cache.h" -#include "config.h" -#include "builtin.h" -#include "refs.h" -#include "parse-options.h" -#include "run-command.h" -#include "object-store.h" -#include "repository.h" -#include "tag.h" - -static const char * const git_replace_usage[] = { - N_("git replace [-f] <object> <replacement>"), - N_("git replace [-f] --edit <object>"), - N_("git replace [-f] --graft <commit> [<parent>...]"), - N_("git replace [-f] --convert-graft-file"), - N_("git replace -d <object>..."), - N_("git replace [--format=<format>] [-l [<pattern>]]"), - NULL -}; - -enum replace_format { - REPLACE_FORMAT_SHORT, - REPLACE_FORMAT_MEDIUM, - REPLACE_FORMAT_LONG -}; - -struct show_data { - const char *pattern; - enum replace_format format; -}; - -static int show_reference(struct repository *r, const char *refname, - const struct object_id *oid, - int flag, void *cb_data) -{ - struct show_data *data = cb_data; - - if (!wildmatch(data->pattern, refname, 0)) { - if (data->format == REPLACE_FORMAT_SHORT) - printf("%s\n", refname); - else if (data->format == REPLACE_FORMAT_MEDIUM) - printf("%s -> %s\n", refname, oid_to_hex(oid)); - else { /* data->format == REPLACE_FORMAT_LONG */ - struct object_id object; - enum object_type obj_type, repl_type; - - if (get_oid(refname, &object)) - return error(_("failed to resolve '%s' as a valid ref"), refname); - - obj_type = oid_object_info(r, &object, NULL); - repl_type = oid_object_info(r, oid, NULL); - - printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type), - oid_to_hex(oid), type_name(repl_type)); - } - } - - return 0; -} - -static int list_replace_refs(const char *pattern, const char *format) -{ - struct show_data data; - - if (pattern == NULL) - pattern = "*"; - data.pattern = pattern; - - if (format == NULL || *format == '\0' || !strcmp(format, "short")) - data.format = REPLACE_FORMAT_SHORT; - else if (!strcmp(format, "medium")) - data.format = REPLACE_FORMAT_MEDIUM; - else if (!strcmp(format, "long")) - data.format = REPLACE_FORMAT_LONG; - /* - * Please update _git_replace() in git-completion.bash when - * you add new format - */ - else - return error(_("invalid replace format '%s'\n" - "valid formats are 'short', 'medium' and 'long'"), - format); - - for_each_replace_ref(the_repository, show_reference, (void *)&data); - - return 0; -} - -typedef int (*each_replace_name_fn)(const char *name, const char *ref, - const struct object_id *oid); - -static int for_each_replace_name(const char **argv, each_replace_name_fn fn) -{ - const char **p, *full_hex; - struct strbuf ref = STRBUF_INIT; - size_t base_len; - int had_error = 0; - struct object_id oid; - - strbuf_addstr(&ref, git_replace_ref_base); - base_len = ref.len; - - for (p = argv; *p; p++) { - if (get_oid(*p, &oid)) { - error("failed to resolve '%s' as a valid ref", *p); - had_error = 1; - continue; - } - - strbuf_setlen(&ref, base_len); - strbuf_addstr(&ref, oid_to_hex(&oid)); - full_hex = ref.buf + base_len; - - if (read_ref(ref.buf, &oid)) { - error(_("replace ref '%s' not found"), full_hex); - had_error = 1; - continue; - } - if (fn(full_hex, ref.buf, &oid)) - had_error = 1; - } - strbuf_release(&ref); - return had_error; -} - -static int delete_replace_ref(const char *name, const char *ref, - const struct object_id *oid) -{ - if (delete_ref(NULL, ref, oid, 0)) - return 1; - printf_ln(_("Deleted replace ref '%s'"), name); - return 0; -} - -static int check_ref_valid(struct object_id *object, - struct object_id *prev, - struct strbuf *ref, - int force) -{ - strbuf_reset(ref); - strbuf_addf(ref, "%s%s", git_replace_ref_base, oid_to_hex(object)); - if (check_refname_format(ref->buf, 0)) - return error(_("'%s' is not a valid ref name"), ref->buf); - - if (read_ref(ref->buf, prev)) - oidclr(prev); - else if (!force) - return error(_("replace ref '%s' already exists"), ref->buf); - return 0; -} - -static int replace_object_oid(const char *object_ref, - struct object_id *object, - const char *replace_ref, - struct object_id *repl, - int force) -{ - struct object_id prev; - enum object_type obj_type, repl_type; - struct strbuf ref = STRBUF_INIT; - struct ref_transaction *transaction; - struct strbuf err = STRBUF_INIT; - int res = 0; - - obj_type = oid_object_info(the_repository, object, NULL); - repl_type = oid_object_info(the_repository, repl, NULL); - if (!force && obj_type != repl_type) - return error(_("Objects must be of the same type.\n" - "'%s' points to a replaced object of type '%s'\n" - "while '%s' points to a replacement object of " - "type '%s'."), - object_ref, type_name(obj_type), - replace_ref, type_name(repl_type)); - - if (check_ref_valid(object, &prev, &ref, force)) { - strbuf_release(&ref); - return -1; - } - - transaction = ref_transaction_begin(&err); - if (!transaction || - ref_transaction_update(transaction, ref.buf, repl, &prev, - 0, NULL, &err) || - ref_transaction_commit(transaction, &err)) - res = error("%s", err.buf); - - ref_transaction_free(transaction); - strbuf_release(&ref); - return res; -} - -static int replace_object(const char *object_ref, const char *replace_ref, int force) -{ - struct object_id object, repl; - - if (get_oid(object_ref, &object)) - return error(_("failed to resolve '%s' as a valid ref"), - object_ref); - if (get_oid(replace_ref, &repl)) - return error(_("failed to resolve '%s' as a valid ref"), - replace_ref); - - return replace_object_oid(object_ref, &object, replace_ref, &repl, force); -} - -/* - * Write the contents of the object named by "sha1" to the file "filename". - * If "raw" is true, then the object's raw contents are printed according to - * "type". Otherwise, we pretty-print the contents for human editing. - */ -static int export_object(const struct object_id *oid, enum object_type type, - int raw, const char *filename) -{ - struct child_process cmd = CHILD_PROCESS_INIT; - int fd; - - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) - return error_errno(_("unable to open %s for writing"), filename); - - strvec_push(&cmd.args, "--no-replace-objects"); - strvec_push(&cmd.args, "cat-file"); - if (raw) - strvec_push(&cmd.args, type_name(type)); - else - strvec_push(&cmd.args, "-p"); - strvec_push(&cmd.args, oid_to_hex(oid)); - cmd.git_cmd = 1; - cmd.out = fd; - - if (run_command(&cmd)) - return error(_("cat-file reported failure")); - return 0; -} - -/* - * Read a previously-exported (and possibly edited) object back from "filename", - * interpreting it as "type", and writing the result to the object database. - * The sha1 of the written object is returned via sha1. - */ -static int import_object(struct object_id *oid, enum object_type type, - int raw, const char *filename) -{ - int fd; - - fd = open(filename, O_RDONLY); - if (fd < 0) - return error_errno(_("unable to open %s for reading"), filename); - - if (!raw && type == OBJ_TREE) { - const char *argv[] = { "mktree", NULL }; - struct child_process cmd = CHILD_PROCESS_INIT; - struct strbuf result = STRBUF_INIT; - - cmd.argv = argv; - cmd.git_cmd = 1; - cmd.in = fd; - cmd.out = -1; - - if (start_command(&cmd)) { - close(fd); - return error(_("unable to spawn mktree")); - } - - if (strbuf_read(&result, cmd.out, the_hash_algo->hexsz + 1) < 0) { - error_errno(_("unable to read from mktree")); - close(fd); - close(cmd.out); - return -1; - } - close(cmd.out); - - if (finish_command(&cmd)) { - strbuf_release(&result); - return error(_("mktree reported failure")); - } - if (get_oid_hex(result.buf, oid) < 0) { - strbuf_release(&result); - return error(_("mktree did not return an object name")); - } - - strbuf_release(&result); - } else { - struct stat st; - int flags = HASH_FORMAT_CHECK | HASH_WRITE_OBJECT; - - if (fstat(fd, &st) < 0) { - error_errno(_("unable to fstat %s"), filename); - close(fd); - return -1; - } - if (index_fd(the_repository->index, oid, fd, &st, type, NULL, flags) < 0) - return error(_("unable to write object to database")); - /* index_fd close()s fd for us */ - } - - /* - * No need to close(fd) here; both run-command and index-fd - * will have done it for us. - */ - return 0; -} - -static int edit_and_replace(const char *object_ref, int force, int raw) -{ - char *tmpfile; - enum object_type type; - struct object_id old_oid, new_oid, prev; - struct strbuf ref = STRBUF_INIT; - - if (get_oid(object_ref, &old_oid) < 0) - return error(_("not a valid object name: '%s'"), object_ref); - - type = oid_object_info(the_repository, &old_oid, NULL); - if (type < 0) - return error(_("unable to get object type for %s"), - oid_to_hex(&old_oid)); - - if (check_ref_valid(&old_oid, &prev, &ref, force)) { - strbuf_release(&ref); - return -1; - } - strbuf_release(&ref); - - tmpfile = git_pathdup("REPLACE_EDITOBJ"); - if (export_object(&old_oid, type, raw, tmpfile)) { - free(tmpfile); - return -1; - } - if (launch_editor(tmpfile, NULL, NULL) < 0) { - free(tmpfile); - return error(_("editing object file failed")); - } - if (import_object(&new_oid, type, raw, tmpfile)) { - free(tmpfile); - return -1; - } - free(tmpfile); - - if (oideq(&old_oid, &new_oid)) - return error(_("new object is the same as the old one: '%s'"), oid_to_hex(&old_oid)); - - return replace_object_oid(object_ref, &old_oid, "replacement", &new_oid, force); -} - -static int replace_parents(struct strbuf *buf, int argc, const char **argv) -{ - struct strbuf new_parents = STRBUF_INIT; - const char *parent_start, *parent_end; - int i; - const unsigned hexsz = the_hash_algo->hexsz; - - /* find existing parents */ - parent_start = buf->buf; - parent_start += hexsz + 6; /* "tree " + "hex sha1" + "\n" */ - parent_end = parent_start; - - while (starts_with(parent_end, "parent ")) - parent_end += hexsz + 8; /* "parent " + "hex sha1" + "\n" */ - - /* prepare new parents */ - for (i = 0; i < argc; i++) { - struct object_id oid; - struct commit *commit; - - if (get_oid(argv[i], &oid) < 0) { - strbuf_release(&new_parents); - return error(_("not a valid object name: '%s'"), - argv[i]); - } - commit = lookup_commit_reference(the_repository, &oid); - if (!commit) { - strbuf_release(&new_parents); - return error(_("could not parse %s as a commit"), argv[i]); - } - strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&commit->object.oid)); - } - - /* replace existing parents with new ones */ - strbuf_splice(buf, parent_start - buf->buf, parent_end - parent_start, - new_parents.buf, new_parents.len); - - strbuf_release(&new_parents); - return 0; -} - -struct check_mergetag_data { - int argc; - const char **argv; -}; - -static int check_one_mergetag(struct commit *commit, - struct commit_extra_header *extra, - void *data) -{ - struct check_mergetag_data *mergetag_data = (struct check_mergetag_data *)data; - const char *ref = mergetag_data->argv[0]; - struct object_id tag_oid; - struct tag *tag; - int i; - - hash_object_file(the_hash_algo, extra->value, extra->len, - type_name(OBJ_TAG), &tag_oid); - tag = lookup_tag(the_repository, &tag_oid); - if (!tag) - return error(_("bad mergetag in commit '%s'"), ref); - if (parse_tag_buffer(the_repository, tag, extra->value, extra->len)) - return error(_("malformed mergetag in commit '%s'"), ref); - - /* iterate over new parents */ - for (i = 1; i < mergetag_data->argc; i++) { - struct object_id oid; - if (get_oid(mergetag_data->argv[i], &oid) < 0) - return error(_("not a valid object name: '%s'"), - mergetag_data->argv[i]); - if (oideq(get_tagged_oid(tag), &oid)) - return 0; /* found */ - } - - return error(_("original commit '%s' contains mergetag '%s' that is " - "discarded; use --edit instead of --graft"), ref, - oid_to_hex(&tag_oid)); -} - -static int check_mergetags(struct commit *commit, int argc, const char **argv) -{ - struct check_mergetag_data mergetag_data; - - mergetag_data.argc = argc; - mergetag_data.argv = argv; - return for_each_mergetag(check_one_mergetag, commit, &mergetag_data); -} - -static int create_graft(int argc, const char **argv, int force, int gentle) -{ - struct object_id old_oid, new_oid; - const char *old_ref = argv[0]; - struct commit *commit; - struct strbuf buf = STRBUF_INIT; - const char *buffer; - unsigned long size; - - if (get_oid(old_ref, &old_oid) < 0) - return error(_("not a valid object name: '%s'"), old_ref); - commit = lookup_commit_reference(the_repository, &old_oid); - if (!commit) - return error(_("could not parse %s"), old_ref); - - buffer = get_commit_buffer(commit, &size); - strbuf_add(&buf, buffer, size); - unuse_commit_buffer(commit, buffer); - - if (replace_parents(&buf, argc - 1, &argv[1]) < 0) { - strbuf_release(&buf); - return -1; - } - - if (remove_signature(&buf)) { - warning(_("the original commit '%s' has a gpg signature"), old_ref); - warning(_("the signature will be removed in the replacement commit!")); - } - - if (check_mergetags(commit, argc, argv)) { - strbuf_release(&buf); - return -1; - } - - if (write_object_file(buf.buf, buf.len, commit_type, &new_oid)) { - strbuf_release(&buf); - return error(_("could not write replacement commit for: '%s'"), - old_ref); - } - - strbuf_release(&buf); - - if (oideq(&commit->object.oid, &new_oid)) { - if (gentle) { - warning(_("graft for '%s' unnecessary"), - oid_to_hex(&commit->object.oid)); - return 0; - } - return error(_("new commit is the same as the old one: '%s'"), - oid_to_hex(&commit->object.oid)); - } - - return replace_object_oid(old_ref, &commit->object.oid, - "replacement", &new_oid, force); -} - -static int convert_graft_file(int force) -{ - const char *graft_file = get_graft_file(the_repository); - FILE *fp = fopen_or_warn(graft_file, "r"); - struct strbuf buf = STRBUF_INIT, err = STRBUF_INIT; - struct strvec args = STRVEC_INIT; - - if (!fp) - return -1; - - advice_graft_file_deprecated = 0; - while (strbuf_getline(&buf, fp) != EOF) { - if (*buf.buf == '#') - continue; - - strvec_split(&args, buf.buf); - if (args.nr && create_graft(args.nr, args.v, force, 1)) - strbuf_addf(&err, "\n\t%s", buf.buf); - strvec_clear(&args); - } - fclose(fp); - - strbuf_release(&buf); - - if (!err.len) - return unlink_or_warn(graft_file); - - warning(_("could not convert the following graft(s):\n%s"), err.buf); - strbuf_release(&err); - - return -1; -} - -int cmd_replace(int argc, const char **argv, const char *prefix) -{ - int force = 0; - int raw = 0; - const char *format = NULL; - enum { - MODE_UNSPECIFIED = 0, - MODE_LIST, - MODE_DELETE, - MODE_EDIT, - MODE_GRAFT, - MODE_CONVERT_GRAFT_FILE, - MODE_REPLACE - } cmdmode = MODE_UNSPECIFIED; - struct option options[] = { - OPT_CMDMODE('l', "list", &cmdmode, N_("list replace refs"), MODE_LIST), - OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE), - OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT), - OPT_CMDMODE('g', "graft", &cmdmode, N_("change a commit's parents"), MODE_GRAFT), - OPT_CMDMODE(0, "convert-graft-file", &cmdmode, N_("convert existing graft file"), MODE_CONVERT_GRAFT_FILE), - OPT_BOOL_F('f', "force", &force, N_("replace the ref if it exists"), - PARSE_OPT_NOCOMPLETE), - OPT_BOOL(0, "raw", &raw, N_("do not pretty-print contents for --edit")), - OPT_STRING(0, "format", &format, N_("format"), N_("use this format")), - OPT_END() - }; - - read_replace_refs = 0; - git_config(git_default_config, NULL); - - argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0); - - if (!cmdmode) - cmdmode = argc ? MODE_REPLACE : MODE_LIST; - - if (format && cmdmode != MODE_LIST) - usage_msg_opt(_("--format cannot be used when not listing"), - git_replace_usage, options); - - if (force && - cmdmode != MODE_REPLACE && - cmdmode != MODE_EDIT && - cmdmode != MODE_GRAFT && - cmdmode != MODE_CONVERT_GRAFT_FILE) - usage_msg_opt(_("-f only makes sense when writing a replacement"), - git_replace_usage, options); - - if (raw && cmdmode != MODE_EDIT) - usage_msg_opt(_("--raw only makes sense with --edit"), - git_replace_usage, options); - - switch (cmdmode) { - case MODE_DELETE: - if (argc < 1) - usage_msg_opt(_("-d needs at least one argument"), - git_replace_usage, options); - return for_each_replace_name(argv, delete_replace_ref); - - case MODE_REPLACE: - if (argc != 2) - usage_msg_opt(_("bad number of arguments"), - git_replace_usage, options); - return replace_object(argv[0], argv[1], force); - - case MODE_EDIT: - if (argc != 1) - usage_msg_opt(_("-e needs exactly one argument"), - git_replace_usage, options); - return edit_and_replace(argv[0], force, raw); - - case MODE_GRAFT: - if (argc < 1) - usage_msg_opt(_("-g needs at least one argument"), - git_replace_usage, options); - return create_graft(argc, argv, force, 0); - - case MODE_CONVERT_GRAFT_FILE: - if (argc != 0) - usage_msg_opt(_("--convert-graft-file takes no argument"), - git_replace_usage, options); - return !!convert_graft_file(force); - - case MODE_LIST: - if (argc > 1) - usage_msg_opt(_("only one pattern can be given with -l"), - git_replace_usage, options); - return list_replace_refs(argv[0], format); - - default: - BUG("invalid cmdmode %d", (int)cmdmode); - } -} diff --git a/third_party/git/builtin/rerere.c b/third_party/git/builtin/rerere.c deleted file mode 100644 index fd3be17b976c..000000000000 --- a/third_party/git/builtin/rerere.c +++ /dev/null @@ -1,120 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "dir.h" -#include "parse-options.h" -#include "string-list.h" -#include "rerere.h" -#include "xdiff/xdiff.h" -#include "xdiff-interface.h" -#include "pathspec.h" - -static const char * const rerere_usage[] = { - N_("git rerere [clear | forget <path>... | status | remaining | diff | gc]"), - NULL, -}; - -static int outf(void *dummy, mmbuffer_t *ptr, int nbuf) -{ - int i; - for (i = 0; i < nbuf; i++) - if (write_in_full(1, ptr[i].ptr, ptr[i].size) < 0) - return -1; - return 0; -} - -static int diff_two(const char *file1, const char *label1, - const char *file2, const char *label2) -{ - xpparam_t xpp; - xdemitconf_t xecfg; - xdemitcb_t ecb; - mmfile_t minus, plus; - int ret; - - if (read_mmfile(&minus, file1) || read_mmfile(&plus, file2)) - return -1; - - printf("--- a/%s\n+++ b/%s\n", label1, label2); - fflush(stdout); - memset(&xpp, 0, sizeof(xpp)); - xpp.flags = 0; - memset(&xecfg, 0, sizeof(xecfg)); - xecfg.ctxlen = 3; - ecb.out_hunk = NULL; - ecb.out_line = outf; - ret = xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb); - - free(minus.ptr); - free(plus.ptr); - return ret; -} - -int cmd_rerere(int argc, const char **argv, const char *prefix) -{ - struct string_list merge_rr = STRING_LIST_INIT_DUP; - int i, autoupdate = -1, flags = 0; - - struct option options[] = { - OPT_SET_INT(0, "rerere-autoupdate", &autoupdate, - N_("register clean resolutions in index"), 1), - OPT_END(), - }; - - argc = parse_options(argc, argv, prefix, options, rerere_usage, 0); - - git_config(git_xmerge_config, NULL); - - if (autoupdate == 1) - flags = RERERE_AUTOUPDATE; - if (autoupdate == 0) - flags = RERERE_NOAUTOUPDATE; - - if (argc < 1) - return repo_rerere(the_repository, flags); - - if (!strcmp(argv[0], "forget")) { - struct pathspec pathspec; - if (argc < 2) - warning(_("'git rerere forget' without paths is deprecated")); - parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, - prefix, argv + 1); - return rerere_forget(the_repository, &pathspec); - } - - if (!strcmp(argv[0], "clear")) { - rerere_clear(the_repository, &merge_rr); - } else if (!strcmp(argv[0], "gc")) - rerere_gc(the_repository, &merge_rr); - else if (!strcmp(argv[0], "status")) { - if (setup_rerere(the_repository, &merge_rr, - flags | RERERE_READONLY) < 0) - return 0; - for (i = 0; i < merge_rr.nr; i++) - printf("%s\n", merge_rr.items[i].string); - } else if (!strcmp(argv[0], "remaining")) { - rerere_remaining(the_repository, &merge_rr); - for (i = 0; i < merge_rr.nr; i++) { - if (merge_rr.items[i].util != RERERE_RESOLVED) - printf("%s\n", merge_rr.items[i].string); - else - /* prepare for later call to - * string_list_clear() */ - merge_rr.items[i].util = NULL; - } - } else if (!strcmp(argv[0], "diff")) { - if (setup_rerere(the_repository, &merge_rr, - flags | RERERE_READONLY) < 0) - return 0; - for (i = 0; i < merge_rr.nr; i++) { - const char *path = merge_rr.items[i].string; - const struct rerere_id *id = merge_rr.items[i].util; - if (diff_two(rerere_path(id, "preimage"), path, path, path)) - die(_("unable to generate diff for '%s'"), rerere_path(id, NULL)); - } - } else - usage_with_options(rerere_usage, options); - - string_list_clear(&merge_rr, 1); - return 0; -} diff --git a/third_party/git/builtin/reset.c b/third_party/git/builtin/reset.c deleted file mode 100644 index c635b062c3a7..000000000000 --- a/third_party/git/builtin/reset.c +++ /dev/null @@ -1,454 +0,0 @@ -/* - * "git reset" builtin command - * - * Copyright (c) 2007 Carlos Rica - * - * Based on git-reset.sh, which is - * - * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "config.h" -#include "lockfile.h" -#include "tag.h" -#include "object.h" -#include "pretty.h" -#include "run-command.h" -#include "refs.h" -#include "diff.h" -#include "diffcore.h" -#include "tree.h" -#include "branch.h" -#include "parse-options.h" -#include "unpack-trees.h" -#include "cache-tree.h" -#include "submodule.h" -#include "submodule-config.h" - -#define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000) - -static const char * const git_reset_usage[] = { - N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), - N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."), - N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"), - N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"), - NULL -}; - -enum reset_type { MIXED, SOFT, HARD, MERGE, KEEP, NONE }; -static const char *reset_type_names[] = { - N_("mixed"), N_("soft"), N_("hard"), N_("merge"), N_("keep"), NULL -}; - -static inline int is_merge(void) -{ - return !access(git_path_merge_head(the_repository), F_OK); -} - -static int reset_index(const char *ref, 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; - init_checkout_metadata(&opts.meta, ref, oid, NULL); - 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(the_repository, desc + nr, &head_oid)) - return error(_("Failed to find tree of HEAD.")); - nr++; - opts.fn = twoway_merge; - } - - if (!fill_tree_descriptor(the_repository, 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_repository, the_repository->index, tree); - } - - ret = 0; - -out: - for (i = 0; i < nr; i++) - free((void *)desc[i].buffer); - return ret; -} - -static void print_new_head_line(struct commit *commit) -{ - struct strbuf buf = STRBUF_INIT; - - printf(_("HEAD is now at %s"), - find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); - - pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf); - if (buf.len > 0) - printf(" %s", buf.buf); - putchar('\n'); - strbuf_release(&buf); -} - -static void update_index_from_diff(struct diff_queue_struct *q, - struct diff_options *opt, void *data) -{ - int i; - int intent_to_add = *(int *)data; - - for (i = 0; i < q->nr; i++) { - struct diff_filespec *one = q->queue[i]->one; - int is_missing = !(one->mode && !is_null_oid(&one->oid)); - struct cache_entry *ce; - - if (is_missing && !intent_to_add) { - remove_file_from_cache(one->path); - continue; - } - - ce = make_cache_entry(&the_index, one->mode, &one->oid, one->path, - 0, 0); - if (!ce) - die(_("make_cache_entry failed for path '%s'"), - one->path); - if (is_missing) { - ce->ce_flags |= CE_INTENT_TO_ADD; - set_object_name_for_intent_to_add_entry(ce); - } - add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); - } -} - -static int read_from_tree(const struct pathspec *pathspec, - struct object_id *tree_oid, - int intent_to_add) -{ - struct diff_options opt; - - memset(&opt, 0, sizeof(opt)); - copy_pathspec(&opt.pathspec, pathspec); - opt.output_format = DIFF_FORMAT_CALLBACK; - opt.format_callback = update_index_from_diff; - opt.format_callback_data = &intent_to_add; - opt.flags.override_submodule_config = 1; - opt.repo = the_repository; - - if (do_diff_cache(tree_oid, &opt)) - return 1; - diffcore_std(&opt); - diff_flush(&opt); - clear_pathspec(&opt.pathspec); - - return 0; -} - -static void set_reflog_message(struct strbuf *sb, const char *action, - const char *rev) -{ - const char *rla = getenv("GIT_REFLOG_ACTION"); - - strbuf_reset(sb); - if (rla) - strbuf_addf(sb, "%s: %s", rla, action); - else if (rev) - strbuf_addf(sb, "reset: moving to %s", rev); - else - strbuf_addf(sb, "reset: %s", action); -} - -static void die_if_unmerged_cache(int reset_type) -{ - if (is_merge() || unmerged_cache()) - die(_("Cannot do a %s reset in the middle of a merge."), - _(reset_type_names[reset_type])); - -} - -static void parse_args(struct pathspec *pathspec, - const char **argv, const char *prefix, - int patch_mode, - const char **rev_ret) -{ - const char *rev = "HEAD"; - struct object_id unused; - /* - * Possible arguments are: - * - * git reset [-opts] [<rev>] - * git reset [-opts] <tree> [<paths>...] - * git reset [-opts] <tree> -- [<paths>...] - * git reset [-opts] -- [<paths>...] - * git reset [-opts] <paths>... - * - * At this point, argv points immediately after [-opts]. - */ - - if (argv[0]) { - if (!strcmp(argv[0], "--")) { - argv++; /* reset to HEAD, possibly with paths */ - } else if (argv[1] && !strcmp(argv[1], "--")) { - rev = argv[0]; - argv += 2; - } - /* - * Otherwise, argv[0] could be either <rev> or <paths> and - * has to be unambiguous. If there is a single argument, it - * can not be a tree - */ - else if ((!argv[1] && !get_oid_committish(argv[0], &unused)) || - (argv[1] && !get_oid_treeish(argv[0], &unused))) { - /* - * Ok, argv[0] looks like a commit/tree; it should not - * be a filename. - */ - verify_non_filename(prefix, argv[0]); - rev = *argv++; - } else { - /* Otherwise we treat this as a filename */ - verify_filename(prefix, argv[0], 1); - } - } - *rev_ret = rev; - - if (read_cache() < 0) - die(_("index file corrupt")); - - parse_pathspec(pathspec, 0, - PATHSPEC_PREFER_FULL | - (patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0), - prefix, argv); -} - -static int reset_refs(const char *rev, const struct object_id *oid) -{ - int update_ref_status; - struct strbuf msg = STRBUF_INIT; - struct object_id *orig = NULL, oid_orig, - *old_orig = NULL, oid_old_orig; - - if (!get_oid("ORIG_HEAD", &oid_old_orig)) - old_orig = &oid_old_orig; - if (!get_oid("HEAD", &oid_orig)) { - orig = &oid_orig; - set_reflog_message(&msg, "updating ORIG_HEAD", NULL); - update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, - UPDATE_REFS_MSG_ON_ERR); - } else if (old_orig) - delete_ref(NULL, "ORIG_HEAD", old_orig, 0); - set_reflog_message(&msg, "updating HEAD", rev); - update_ref_status = update_ref(msg.buf, "HEAD", oid, orig, 0, - UPDATE_REFS_MSG_ON_ERR); - strbuf_release(&msg); - return update_ref_status; -} - -static int git_reset_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "submodule.recurse")) - return git_default_submodule_config(var, value, cb); - - return git_default_config(var, value, cb); -} - -int cmd_reset(int argc, const char **argv, const char *prefix) -{ - int reset_type = NONE, update_ref_status = 0, quiet = 0; - int patch_mode = 0, pathspec_file_nul = 0, unborn; - const char *rev, *pathspec_from_file = NULL; - struct object_id oid; - struct pathspec pathspec; - int intent_to_add = 0; - const struct option options[] = { - OPT__QUIET(&quiet, N_("be quiet, only report errors")), - OPT_SET_INT(0, "mixed", &reset_type, - N_("reset HEAD and index"), MIXED), - OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT), - OPT_SET_INT(0, "hard", &reset_type, - N_("reset HEAD, index and working tree"), HARD), - OPT_SET_INT(0, "merge", &reset_type, - N_("reset HEAD, index and working tree"), MERGE), - OPT_SET_INT(0, "keep", &reset_type, - N_("reset HEAD but keep local changes"), KEEP), - OPT_CALLBACK_F(0, "recurse-submodules", NULL, - "reset", "control recursive updating of submodules", - PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), - OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), - OPT_BOOL('N', "intent-to-add", &intent_to_add, - N_("record only the fact that removed paths will be added later")), - OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), - OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), - OPT_END() - }; - - git_config(git_reset_config, NULL); - git_config_get_bool("reset.quiet", &quiet); - - argc = parse_options(argc, argv, prefix, options, git_reset_usage, - PARSE_OPT_KEEP_DASHDASH); - parse_args(&pathspec, argv, prefix, patch_mode, &rev); - - if (pathspec_from_file) { - if (patch_mode) - die(_("--pathspec-from-file is incompatible with --patch")); - - if (pathspec.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); - - parse_pathspec_file(&pathspec, 0, - PATHSPEC_PREFER_FULL, - prefix, pathspec_from_file, pathspec_file_nul); - } else if (pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); - } - - unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); - if (unborn) { - /* reset on unborn branch: treat as reset to empty tree */ - oidcpy(&oid, the_hash_algo->empty_tree); - } else if (!pathspec.nr && !patch_mode) { - struct commit *commit; - if (get_oid_committish(rev, &oid)) - die(_("Failed to resolve '%s' as a valid revision."), rev); - commit = lookup_commit_reference(the_repository, &oid); - if (!commit) - die(_("Could not parse object '%s'."), rev); - oidcpy(&oid, &commit->object.oid); - } else { - struct tree *tree; - if (get_oid_treeish(rev, &oid)) - die(_("Failed to resolve '%s' as a valid tree."), rev); - tree = parse_tree_indirect(&oid); - if (!tree) - die(_("Could not parse object '%s'."), rev); - oidcpy(&oid, &tree->object.oid); - } - - if (patch_mode) { - if (reset_type != NONE) - die(_("--patch is incompatible with --{hard,mixed,soft}")); - trace2_cmd_mode("patch-interactive"); - return run_add_interactive(rev, "--patch=reset", &pathspec); - } - - /* git reset tree [--] paths... can be used to - * load chosen paths from the tree into the index without - * affecting the working tree nor HEAD. */ - if (pathspec.nr) { - if (reset_type == MIXED) - warning(_("--mixed with paths is deprecated; use 'git reset -- <paths>' instead.")); - else if (reset_type != NONE) - die(_("Cannot do %s reset with paths."), - _(reset_type_names[reset_type])); - } - if (reset_type == NONE) - reset_type = MIXED; /* by default */ - - if (pathspec.nr) - trace2_cmd_mode("path"); - else - trace2_cmd_mode(reset_type_names[reset_type]); - - if (reset_type != SOFT && (reset_type != MIXED || get_git_work_tree())) - setup_work_tree(); - - if (reset_type == MIXED && is_bare_repository()) - die(_("%s reset is not allowed in a bare repository"), - _(reset_type_names[reset_type])); - - if (intent_to_add && reset_type != MIXED) - die(_("-N can only be used with --mixed")); - - /* Soft reset does not touch the index file nor the working tree - * at all, but requires them in a good order. Other resets reset - * the index file to the tree object we are switching to. */ - if (reset_type == SOFT || reset_type == KEEP) - die_if_unmerged_cache(reset_type); - - if (reset_type != SOFT) { - struct lock_file lock = LOCK_INIT; - hold_locked_index(&lock, LOCK_DIE_ON_ERROR); - if (reset_type == MIXED) { - int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN; - if (read_from_tree(&pathspec, &oid, intent_to_add)) - return 1; - the_index.updated_skipworktree = 1; - if (!quiet && get_git_work_tree()) { - uint64_t t_begin, t_delta_in_ms; - - t_begin = getnanotime(); - refresh_index(&the_index, flags, NULL, NULL, - _("Unstaged changes after reset:")); - t_delta_in_ms = (getnanotime() - t_begin) / 1000000; - if (advice_reset_quiet_warning && t_delta_in_ms > REFRESH_INDEX_DELAY_WARNING_IN_MS) { - printf(_("\nIt took %.2f seconds to enumerate unstaged changes after reset. You can\n" - "use '--quiet' to avoid this. Set the config setting reset.quiet to true\n" - "to make this the default.\n"), t_delta_in_ms / 1000.0); - } - } - } else { - struct object_id dummy; - char *ref = NULL; - int err; - - dwim_ref(rev, strlen(rev), &dummy, &ref, 0); - if (ref && !starts_with(ref, "refs/")) - ref = NULL; - - err = reset_index(ref, &oid, reset_type, quiet); - if (reset_type == KEEP && !err) - err = reset_index(ref, &oid, MIXED, quiet); - if (err) - die(_("Could not reset index file to revision '%s'."), rev); - free(ref); - } - - if (write_locked_index(&the_index, &lock, COMMIT_LOCK)) - die(_("Could not write new index file.")); - } - - if (!pathspec.nr && !unborn) { - /* Any resets without paths update HEAD to the head being - * switched to, saving the previous head in ORIG_HEAD before. */ - update_ref_status = reset_refs(rev, &oid); - - if (reset_type == HARD && !update_ref_status && !quiet) - print_new_head_line(lookup_commit_reference(the_repository, &oid)); - } - if (!pathspec.nr) - remove_branch_state(the_repository, 0); - - return update_ref_status; -} diff --git a/third_party/git/builtin/rev-list.c b/third_party/git/builtin/rev-list.c deleted file mode 100644 index 25c6c3b38d4b..000000000000 --- a/third_party/git/builtin/rev-list.c +++ /dev/null @@ -1,694 +0,0 @@ -#include "cache.h" -#include "config.h" -#include "commit.h" -#include "diff.h" -#include "revision.h" -#include "list-objects.h" -#include "list-objects-filter.h" -#include "list-objects-filter-options.h" -#include "object.h" -#include "object-store.h" -#include "pack.h" -#include "pack-bitmap.h" -#include "builtin.h" -#include "log-tree.h" -#include "graph.h" -#include "bisect.h" -#include "progress.h" -#include "reflog-walk.h" -#include "oidset.h" -#include "packfile.h" - -static const char rev_list_usage[] = -"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n" -" limiting output:\n" -" --max-count=<n>\n" -" --max-age=<epoch>\n" -" --min-age=<epoch>\n" -" --sparse\n" -" --no-merges\n" -" --min-parents=<n>\n" -" --no-min-parents\n" -" --max-parents=<n>\n" -" --no-max-parents\n" -" --remove-empty\n" -" --all\n" -" --branches\n" -" --tags\n" -" --remotes\n" -" --stdin\n" -" --quiet\n" -" ordering output:\n" -" --topo-order\n" -" --date-order\n" -" --reverse\n" -" formatting output:\n" -" --parents\n" -" --children\n" -" --objects | --objects-edge\n" -" --unpacked\n" -" --header | --pretty\n" -" --[no-]object-names\n" -" --abbrev=<n> | --no-abbrev\n" -" --abbrev-commit\n" -" --left-right\n" -" --count\n" -" special purpose:\n" -" --bisect\n" -" --bisect-vars\n" -" --bisect-all" -; - -static struct progress *progress; -static unsigned progress_counter; - -static struct list_objects_filter_options filter_options; -static struct oidset omitted_objects; -static int arg_print_omitted; /* print objects omitted by filter */ - -static struct oidset missing_objects; -enum missing_action { - MA_ERROR = 0, /* fail if any missing objects are encountered */ - MA_ALLOW_ANY, /* silently allow ALL missing objects */ - MA_PRINT, /* print ALL missing objects in special section */ - MA_ALLOW_PROMISOR, /* silently allow all missing PROMISOR objects */ -}; -static enum missing_action arg_missing_action; - -/* display only the oid of each object encountered */ -static int arg_show_object_names = 1; - -#define DEFAULT_OIDSET_SIZE (16*1024) - -static void finish_commit(struct commit *commit); -static void show_commit(struct commit *commit, void *data) -{ - struct rev_list_info *info = data; - struct rev_info *revs = info->revs; - - display_progress(progress, ++progress_counter); - - if (info->flags & REV_LIST_QUIET) { - finish_commit(commit); - return; - } - - graph_show_commit(revs->graph); - - if (revs->count) { - if (commit->object.flags & PATCHSAME) - revs->count_same++; - else if (commit->object.flags & SYMMETRIC_LEFT) - revs->count_left++; - else - revs->count_right++; - finish_commit(commit); - return; - } - - if (info->show_timestamp) - printf("%"PRItime" ", commit->date); - if (info->header_prefix) - fputs(info->header_prefix, stdout); - - if (!revs->graph) - fputs(get_revision_mark(revs, commit), stdout); - if (revs->abbrev_commit && revs->abbrev) - fputs(find_unique_abbrev(&commit->object.oid, revs->abbrev), - stdout); - else - fputs(oid_to_hex(&commit->object.oid), stdout); - if (revs->print_parents) { - struct commit_list *parents = commit->parents; - while (parents) { - printf(" %s", oid_to_hex(&parents->item->object.oid)); - parents = parents->next; - } - } - if (revs->children.name) { - struct commit_list *children; - - children = lookup_decoration(&revs->children, &commit->object); - while (children) { - printf(" %s", oid_to_hex(&children->item->object.oid)); - children = children->next; - } - } - show_decorations(revs, commit); - if (revs->commit_format == CMIT_FMT_ONELINE) - putchar(' '); - else - putchar('\n'); - - if (revs->verbose_header) { - struct strbuf buf = STRBUF_INIT; - struct pretty_print_context ctx = {0}; - ctx.abbrev = revs->abbrev; - ctx.date_mode = revs->date_mode; - ctx.date_mode_explicit = revs->date_mode_explicit; - ctx.fmt = revs->commit_format; - ctx.output_encoding = get_log_output_encoding(); - ctx.color = revs->diffopt.use_color; - pretty_print_commit(&ctx, commit, &buf); - if (buf.len) { - if (revs->commit_format != CMIT_FMT_ONELINE) - graph_show_oneline(revs->graph); - - graph_show_commit_msg(revs->graph, stdout, &buf); - - /* - * Add a newline after the commit message. - * - * Usually, this newline produces a blank - * padding line between entries, in which case - * we need to add graph padding on this line. - * - * However, the commit message may not end in a - * newline. In this case the newline simply - * ends the last line of the commit message, - * and we don't need any graph output. (This - * always happens with CMIT_FMT_ONELINE, and it - * happens with CMIT_FMT_USERFORMAT when the - * format doesn't explicitly end in a newline.) - */ - if (buf.len && buf.buf[buf.len - 1] == '\n') - graph_show_padding(revs->graph); - putchar(info->hdr_termination); - } else { - /* - * If the message buffer is empty, just show - * the rest of the graph output for this - * commit. - */ - if (graph_show_remainder(revs->graph)) - putchar('\n'); - if (revs->commit_format == CMIT_FMT_ONELINE) - putchar('\n'); - } - strbuf_release(&buf); - } else { - if (graph_show_remainder(revs->graph)) - putchar('\n'); - } - maybe_flush_or_die(stdout, "stdout"); - finish_commit(commit); -} - -static void finish_commit(struct commit *commit) -{ - if (commit->parents) { - free_commit_list(commit->parents); - commit->parents = NULL; - } - free_commit_buffer(the_repository->parsed_objects, - commit); -} - -static inline void finish_object__ma(struct object *obj) -{ - /* - * Whether or not we try to dynamically fetch missing objects - * from the server, we currently DO NOT have the object. We - * can either print, allow (ignore), or conditionally allow - * (ignore) them. - */ - switch (arg_missing_action) { - case MA_ERROR: - die("missing %s object '%s'", - type_name(obj->type), oid_to_hex(&obj->oid)); - return; - - case MA_ALLOW_ANY: - return; - - case MA_PRINT: - oidset_insert(&missing_objects, &obj->oid); - return; - - case MA_ALLOW_PROMISOR: - if (is_promisor_object(&obj->oid)) - return; - die("unexpected missing %s object '%s'", - type_name(obj->type), oid_to_hex(&obj->oid)); - return; - - default: - BUG("unhandled missing_action"); - return; - } -} - -static int finish_object(struct object *obj, const char *name, void *cb_data) -{ - struct rev_list_info *info = cb_data; - if (oid_object_info_extended(the_repository, &obj->oid, NULL, 0) < 0) { - finish_object__ma(obj); - return 1; - } - if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT) - parse_object(the_repository, &obj->oid); - return 0; -} - -static void show_object(struct object *obj, const char *name, void *cb_data) -{ - struct rev_list_info *info = cb_data; - struct rev_info *revs = info->revs; - - if (finish_object(obj, name, cb_data)) - return; - display_progress(progress, ++progress_counter); - if (info->flags & REV_LIST_QUIET) - return; - - if (revs->count) { - /* - * The object count is always accumulated in the .count_right - * field for traversal that is not a left-right traversal, - * and cmd_rev_list() made sure that a .count request that - * wants to count non-commit objects, which is handled by - * the show_object() callback, does not ask for .left_right. - */ - revs->count_right++; - return; - } - - if (arg_show_object_names) - show_object_with_name(stdout, obj, name); - else - printf("%s\n", oid_to_hex(&obj->oid)); -} - -static void show_edge(struct commit *commit) -{ - printf("-%s\n", oid_to_hex(&commit->object.oid)); -} - -static void print_var_str(const char *var, const char *val) -{ - printf("%s='%s'\n", var, val); -} - -static void print_var_int(const char *var, int val) -{ - printf("%s=%d\n", var, val); -} - -static int show_bisect_vars(struct rev_list_info *info, int reaches, int all) -{ - int cnt, flags = info->flags; - char hex[GIT_MAX_HEXSZ + 1] = ""; - struct commit_list *tried; - struct rev_info *revs = info->revs; - - if (!revs->commits) - return 1; - - revs->commits = filter_skipped(revs->commits, &tried, - flags & BISECT_SHOW_ALL, - NULL, NULL); - - /* - * revs->commits can reach "reaches" commits among - * "all" commits. If it is good, then there are - * (all-reaches) commits left to be bisected. - * On the other hand, if it is bad, then the set - * to bisect is "reaches". - * A bisect set of size N has (N-1) commits further - * to test, as we already know one bad one. - */ - cnt = all - reaches; - if (cnt < reaches) - cnt = reaches; - - if (revs->commits) - oid_to_hex_r(hex, &revs->commits->item->object.oid); - - if (flags & BISECT_SHOW_ALL) { - traverse_commit_list(revs, show_commit, show_object, info); - printf("------\n"); - } - - print_var_str("bisect_rev", hex); - print_var_int("bisect_nr", cnt - 1); - print_var_int("bisect_good", all - reaches - 1); - print_var_int("bisect_bad", reaches - 1); - print_var_int("bisect_all", all); - print_var_int("bisect_steps", estimate_bisect_steps(all)); - - return 0; -} - -static int show_object_fast( - const struct object_id *oid, - enum object_type type, - int exclude, - uint32_t name_hash, - struct packed_git *found_pack, - off_t found_offset) -{ - fprintf(stdout, "%s\n", oid_to_hex(oid)); - return 1; -} - -static inline int parse_missing_action_value(const char *value) -{ - if (!strcmp(value, "error")) { - arg_missing_action = MA_ERROR; - return 1; - } - - if (!strcmp(value, "allow-any")) { - arg_missing_action = MA_ALLOW_ANY; - fetch_if_missing = 0; - return 1; - } - - if (!strcmp(value, "print")) { - arg_missing_action = MA_PRINT; - fetch_if_missing = 0; - return 1; - } - - if (!strcmp(value, "allow-promisor")) { - arg_missing_action = MA_ALLOW_PROMISOR; - fetch_if_missing = 0; - return 1; - } - - return 0; -} - -static int try_bitmap_count(struct rev_info *revs, - struct list_objects_filter_options *filter) -{ - uint32_t commit_count = 0, - tag_count = 0, - tree_count = 0, - blob_count = 0; - int max_count; - struct bitmap_index *bitmap_git; - - /* This function only handles counting, not general traversal. */ - if (!revs->count) - return -1; - - /* - * A bitmap result can't know left/right, etc, because we don't - * actually traverse. - */ - if (revs->left_right || revs->cherry_mark) - return -1; - - /* - * If we're counting reachable objects, we can't handle a max count of - * commits to traverse, since we don't know which objects go with which - * commit. - */ - if (revs->max_count >= 0 && - (revs->tag_objects || revs->tree_objects || revs->blob_objects)) - return -1; - - /* - * This must be saved before doing any walking, since the revision - * machinery will count it down to zero while traversing. - */ - max_count = revs->max_count; - - bitmap_git = prepare_bitmap_walk(revs, filter); - if (!bitmap_git) - return -1; - - count_bitmap_commit_list(bitmap_git, &commit_count, - revs->tree_objects ? &tree_count : NULL, - revs->blob_objects ? &blob_count : NULL, - revs->tag_objects ? &tag_count : NULL); - if (max_count >= 0 && max_count < commit_count) - commit_count = max_count; - - printf("%d\n", commit_count + tree_count + blob_count + tag_count); - free_bitmap_index(bitmap_git); - return 0; -} - -static int try_bitmap_traversal(struct rev_info *revs, - struct list_objects_filter_options *filter) -{ - struct bitmap_index *bitmap_git; - - /* - * We can't use a bitmap result with a traversal limit, since the set - * of commits we'd get would be essentially random. - */ - if (revs->max_count >= 0) - return -1; - - bitmap_git = prepare_bitmap_walk(revs, filter); - if (!bitmap_git) - return -1; - - traverse_bitmap_commit_list(bitmap_git, revs, &show_object_fast); - free_bitmap_index(bitmap_git); - return 0; -} - -int cmd_rev_list(int argc, const char **argv, const char *prefix) -{ - struct rev_info revs; - struct rev_list_info info; - struct setup_revision_opt s_r_opt = { - .allow_exclude_promisor_objects = 1, - }; - int i; - int bisect_list = 0; - int bisect_show_vars = 0; - int bisect_find_all = 0; - int use_bitmap_index = 0; - const char *show_progress = NULL; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage(rev_list_usage); - - git_config(git_default_config, NULL); - repo_init_revisions(the_repository, &revs, prefix); - revs.abbrev = DEFAULT_ABBREV; - revs.commit_format = CMIT_FMT_UNSPECIFIED; - - /* - * Scan the argument list before invoking setup_revisions(), so that we - * know if fetch_if_missing needs to be set to 0. - * - * "--exclude-promisor-objects" acts as a pre-filter on missing objects - * by not crossing the boundary from realized objects to promisor - * objects. - * - * Let "--missing" to conditionally set fetch_if_missing. - */ - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!strcmp(arg, "--exclude-promisor-objects")) { - fetch_if_missing = 0; - revs.exclude_promisor_objects = 1; - break; - } - } - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (skip_prefix(arg, "--missing=", &arg)) { - if (revs.exclude_promisor_objects) - die(_("cannot combine --exclude-promisor-objects and --missing")); - if (parse_missing_action_value(arg)) - break; - } - } - - if (arg_missing_action) - revs.do_not_die_on_missing_tree = 1; - - argc = setup_revisions(argc, argv, &revs, &s_r_opt); - - memset(&info, 0, sizeof(info)); - info.revs = &revs; - if (revs.bisect) - bisect_list = 1; - - if (revs.diffopt.flags.quick) - info.flags |= REV_LIST_QUIET; - for (i = 1 ; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--header")) { - revs.verbose_header = 1; - continue; - } - if (!strcmp(arg, "--timestamp")) { - info.show_timestamp = 1; - continue; - } - if (!strcmp(arg, "--bisect")) { - bisect_list = 1; - continue; - } - if (!strcmp(arg, "--bisect-all")) { - bisect_list = 1; - bisect_find_all = 1; - info.flags |= BISECT_SHOW_ALL; - revs.show_decorations = 1; - continue; - } - if (!strcmp(arg, "--bisect-vars")) { - bisect_list = 1; - bisect_show_vars = 1; - continue; - } - if (!strcmp(arg, "--use-bitmap-index")) { - use_bitmap_index = 1; - continue; - } - if (!strcmp(arg, "--test-bitmap")) { - test_bitmap_walk(&revs); - return 0; - } - if (skip_prefix(arg, "--progress=", &arg)) { - show_progress = arg; - continue; - } - - if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) { - parse_list_objects_filter(&filter_options, arg); - if (filter_options.choice && !revs.blob_objects) - die(_("object filtering requires --objects")); - continue; - } - if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) { - list_objects_filter_set_no_filter(&filter_options); - continue; - } - if (!strcmp(arg, "--filter-print-omitted")) { - arg_print_omitted = 1; - continue; - } - - if (!strcmp(arg, "--exclude-promisor-objects")) - continue; /* already handled above */ - if (skip_prefix(arg, "--missing=", &arg)) - continue; /* already handled above */ - - if (!strcmp(arg, ("--no-object-names"))) { - arg_show_object_names = 0; - continue; - } - - if (!strcmp(arg, ("--object-names"))) { - arg_show_object_names = 1; - continue; - } - - usage(rev_list_usage); - - } - if (revs.commit_format != CMIT_FMT_UNSPECIFIED) { - /* The command line has a --pretty */ - info.hdr_termination = '\n'; - if (revs.commit_format == CMIT_FMT_ONELINE) - info.header_prefix = ""; - else - info.header_prefix = "commit "; - } - else if (revs.verbose_header) - /* Only --header was specified */ - revs.commit_format = CMIT_FMT_RAW; - - if ((!revs.commits && reflog_walk_empty(revs.reflog_info) && - (!(revs.tag_objects || revs.tree_objects || revs.blob_objects) && - !revs.pending.nr) && - !revs.rev_input_given && !revs.read_from_stdin) || - revs.diff) - usage(rev_list_usage); - - if (revs.show_notes) - die(_("rev-list does not support display of notes")); - - if (revs.count && - (revs.tag_objects || revs.tree_objects || revs.blob_objects) && - (revs.left_right || revs.cherry_mark)) - die(_("marked counting is incompatible with --objects")); - - save_commit_buffer = (revs.verbose_header || - revs.grep_filter.pattern_list || - revs.grep_filter.header_list); - if (bisect_list) - revs.limited = 1; - - if (show_progress) - progress = start_delayed_progress(show_progress, 0); - - if (use_bitmap_index) { - if (!try_bitmap_count(&revs, &filter_options)) - return 0; - if (!try_bitmap_traversal(&revs, &filter_options)) - return 0; - } - - if (prepare_revision_walk(&revs)) - die("revision walk setup failed"); - if (revs.tree_objects) - mark_edges_uninteresting(&revs, show_edge, 0); - - if (bisect_list) { - int reaches, all; - unsigned bisect_flags = 0; - - if (bisect_find_all) - bisect_flags |= FIND_BISECTION_ALL; - - if (revs.first_parent_only) - bisect_flags |= FIND_BISECTION_FIRST_PARENT_ONLY; - - find_bisection(&revs.commits, &reaches, &all, bisect_flags); - - if (bisect_show_vars) - return show_bisect_vars(&info, reaches, all); - } - - if (arg_print_omitted) - oidset_init(&omitted_objects, DEFAULT_OIDSET_SIZE); - if (arg_missing_action == MA_PRINT) - oidset_init(&missing_objects, DEFAULT_OIDSET_SIZE); - - traverse_commit_list_filtered( - &filter_options, &revs, show_commit, show_object, &info, - (arg_print_omitted ? &omitted_objects : NULL)); - - if (arg_print_omitted) { - struct oidset_iter iter; - struct object_id *oid; - oidset_iter_init(&omitted_objects, &iter); - while ((oid = oidset_iter_next(&iter))) - printf("~%s\n", oid_to_hex(oid)); - oidset_clear(&omitted_objects); - } - if (arg_missing_action == MA_PRINT) { - struct oidset_iter iter; - struct object_id *oid; - oidset_iter_init(&missing_objects, &iter); - while ((oid = oidset_iter_next(&iter))) - printf("?%s\n", oid_to_hex(oid)); - oidset_clear(&missing_objects); - } - - stop_progress(&progress); - - if (revs.count) { - if (revs.left_right && revs.cherry_mark) - printf("%d\t%d\t%d\n", revs.count_left, revs.count_right, revs.count_same); - else if (revs.left_right) - printf("%d\t%d\n", revs.count_left, revs.count_right); - else if (revs.cherry_mark) - printf("%d\t%d\n", revs.count_left + revs.count_right, revs.count_same); - else - printf("%d\n", revs.count_left + revs.count_right); - } - - return 0; -} diff --git a/third_party/git/builtin/rev-parse.c b/third_party/git/builtin/rev-parse.c deleted file mode 100644 index ed200c8af128..000000000000 --- a/third_party/git/builtin/rev-parse.c +++ /dev/null @@ -1,984 +0,0 @@ -/* - * rev-parse.c - * - * Copyright (C) Linus Torvalds, 2005 - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "commit.h" -#include "refs.h" -#include "quote.h" -#include "builtin.h" -#include "parse-options.h" -#include "diff.h" -#include "revision.h" -#include "split-index.h" -#include "submodule.h" -#include "commit-reach.h" -#include "shallow.h" - -#define DO_REVS 1 -#define DO_NOREV 2 -#define DO_FLAGS 4 -#define DO_NONFLAGS 8 -static int filter = ~0; - -static const char *def; - -#define NORMAL 0 -#define REVERSED 1 -static int show_type = NORMAL; - -#define SHOW_SYMBOLIC_ASIS 1 -#define SHOW_SYMBOLIC_FULL 2 -static int symbolic; -static int abbrev; -static int abbrev_ref; -static int abbrev_ref_strict; -static int output_sq; - -static int stuck_long; -static struct string_list *ref_excludes; - -/* - * Some arguments are relevant "revision" arguments, - * others are about output format or other details. - * This sorts it all out. - */ -static int is_rev_argument(const char *arg) -{ - static const char *rev_args[] = { - "--all", - "--bisect", - "--dense", - "--branches=", - "--branches", - "--header", - "--ignore-missing", - "--max-age=", - "--max-count=", - "--min-age=", - "--no-merges", - "--min-parents=", - "--no-min-parents", - "--max-parents=", - "--no-max-parents", - "--objects", - "--objects-edge", - "--parents", - "--pretty", - "--remotes=", - "--remotes", - "--glob=", - "--sparse", - "--tags=", - "--tags", - "--topo-order", - "--date-order", - "--unpacked", - NULL - }; - const char **p = rev_args; - - /* accept -<digit>, like traditional "head" */ - if ((*arg == '-') && isdigit(arg[1])) - return 1; - - for (;;) { - const char *str = *p++; - int len; - if (!str) - return 0; - len = strlen(str); - if (!strcmp(arg, str) || - (str[len-1] == '=' && !strncmp(arg, str, len))) - return 1; - } -} - -/* Output argument as a string, either SQ or normal */ -static void show(const char *arg) -{ - if (output_sq) { - int sq = '\'', ch; - - putchar(sq); - while ((ch = *arg++)) { - if (ch == sq) - fputs("'\\'", stdout); - putchar(ch); - } - putchar(sq); - putchar(' '); - } - else - puts(arg); -} - -/* Like show(), but with a negation prefix according to type */ -static void show_with_type(int type, const char *arg) -{ - if (type != show_type) - putchar('^'); - show(arg); -} - -/* Output a revision, only if filter allows it */ -static void show_rev(int type, const struct object_id *oid, const char *name) -{ - if (!(filter & DO_REVS)) - return; - def = NULL; - - if ((symbolic || abbrev_ref) && name) { - if (symbolic == SHOW_SYMBOLIC_FULL || abbrev_ref) { - struct object_id discard; - char *full; - - switch (dwim_ref(name, strlen(name), &discard, &full, 0)) { - case 0: - /* - * Not found -- not a ref. We could - * emit "name" here, but symbolic-full - * users are interested in finding the - * refs spelled in full, and they would - * need to filter non-refs if we did so. - */ - break; - case 1: /* happy */ - if (abbrev_ref) - full = shorten_unambiguous_ref(full, - abbrev_ref_strict); - show_with_type(type, full); - break; - default: /* ambiguous */ - error("refname '%s' is ambiguous", name); - break; - } - free(full); - } else { - show_with_type(type, name); - } - } - else if (abbrev) - show_with_type(type, find_unique_abbrev(oid, abbrev)); - else - show_with_type(type, oid_to_hex(oid)); -} - -/* Output a flag, only if filter allows it. */ -static int show_flag(const char *arg) -{ - if (!(filter & DO_FLAGS)) - return 0; - if (filter & (is_rev_argument(arg) ? DO_REVS : DO_NOREV)) { - show(arg); - return 1; - } - return 0; -} - -static int show_default(void) -{ - const char *s = def; - - if (s) { - struct object_id oid; - - def = NULL; - if (!get_oid(s, &oid)) { - show_rev(NORMAL, &oid, s); - return 1; - } - } - return 0; -} - -static int show_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data) -{ - if (ref_excluded(ref_excludes, refname)) - return 0; - show_rev(NORMAL, oid, refname); - return 0; -} - -static int anti_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data) -{ - show_rev(REVERSED, oid, refname); - return 0; -} - -static int show_abbrev(const struct object_id *oid, void *cb_data) -{ - show_rev(NORMAL, oid, NULL); - return 0; -} - -static void show_datestring(const char *flag, const char *datestr) -{ - char *buffer; - - /* date handling requires both flags and revs */ - if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS)) - return; - buffer = xstrfmt("%s%"PRItime, flag, approxidate(datestr)); - show(buffer); - free(buffer); -} - -static int show_file(const char *arg, int output_prefix) -{ - show_default(); - if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) { - if (output_prefix) { - const char *prefix = startup_info->prefix; - char *fname = prefix_filename(prefix, arg); - show(fname); - free(fname); - } else - show(arg); - return 1; - } - return 0; -} - -static int try_difference(const char *arg) -{ - char *dotdot; - struct object_id start_oid; - struct object_id end_oid; - const char *end; - const char *start; - int symmetric; - static const char head_by_default[] = "HEAD"; - - if (!(dotdot = strstr(arg, ".."))) - return 0; - end = dotdot + 2; - start = arg; - symmetric = (*end == '.'); - - *dotdot = 0; - end += symmetric; - - if (!*end) - end = head_by_default; - if (dotdot == arg) - start = head_by_default; - - if (start == head_by_default && end == head_by_default && - !symmetric) { - /* - * Just ".."? That is not a range but the - * pathspec for the parent directory. - */ - *dotdot = '.'; - return 0; - } - - if (!get_oid_committish(start, &start_oid) && !get_oid_committish(end, &end_oid)) { - show_rev(NORMAL, &end_oid, end); - show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start); - if (symmetric) { - struct commit_list *exclude; - struct commit *a, *b; - a = lookup_commit_reference(the_repository, &start_oid); - b = lookup_commit_reference(the_repository, &end_oid); - if (!a || !b) { - *dotdot = '.'; - return 0; - } - exclude = get_merge_bases(a, b); - while (exclude) { - struct commit *commit = pop_commit(&exclude); - show_rev(REVERSED, &commit->object.oid, NULL); - } - } - *dotdot = '.'; - return 1; - } - *dotdot = '.'; - return 0; -} - -static int try_parent_shorthands(const char *arg) -{ - char *dotdot; - struct object_id oid; - struct commit *commit; - struct commit_list *parents; - int parent_number; - int include_rev = 0; - int include_parents = 0; - int exclude_parent = 0; - - if ((dotdot = strstr(arg, "^!"))) { - include_rev = 1; - if (dotdot[2]) - return 0; - } else if ((dotdot = strstr(arg, "^@"))) { - include_parents = 1; - if (dotdot[2]) - return 0; - } else if ((dotdot = strstr(arg, "^-"))) { - include_rev = 1; - exclude_parent = 1; - - if (dotdot[2]) { - char *end; - exclude_parent = strtoul(dotdot + 2, &end, 10); - if (*end != '\0' || !exclude_parent) - return 0; - } - } else - return 0; - - *dotdot = 0; - if (get_oid_committish(arg, &oid) || - !(commit = lookup_commit_reference(the_repository, &oid))) { - *dotdot = '^'; - return 0; - } - - if (exclude_parent && - exclude_parent > commit_list_count(commit->parents)) { - *dotdot = '^'; - return 0; - } - - if (include_rev) - show_rev(NORMAL, &oid, arg); - for (parents = commit->parents, parent_number = 1; - parents; - parents = parents->next, parent_number++) { - char *name = NULL; - - if (exclude_parent && parent_number != exclude_parent) - continue; - - if (symbolic) - name = xstrfmt("%s^%d", arg, parent_number); - show_rev(include_parents ? NORMAL : REVERSED, - &parents->item->object.oid, name); - free(name); - } - - *dotdot = '^'; - return 1; -} - -static int parseopt_dump(const struct option *o, const char *arg, int unset) -{ - struct strbuf *parsed = o->value; - if (unset) - strbuf_addf(parsed, " --no-%s", o->long_name); - else if (o->short_name && (o->long_name == NULL || !stuck_long)) - strbuf_addf(parsed, " -%c", o->short_name); - else - strbuf_addf(parsed, " --%s", o->long_name); - if (arg) { - if (!stuck_long) - strbuf_addch(parsed, ' '); - else if (o->long_name) - strbuf_addch(parsed, '='); - sq_quote_buf(parsed, arg); - } - return 0; -} - -static const char *skipspaces(const char *s) -{ - while (isspace(*s)) - s++; - return s; -} - -static char *findspace(const char *s) -{ - for (; *s; s++) - if (isspace(*s)) - return (char*)s; - return NULL; -} - -static int cmd_parseopt(int argc, const char **argv, const char *prefix) -{ - static int keep_dashdash = 0, stop_at_non_option = 0; - static char const * const parseopt_usage[] = { - N_("git rev-parse --parseopt [<options>] -- [<args>...]"), - NULL - }; - static struct option parseopt_opts[] = { - OPT_BOOL(0, "keep-dashdash", &keep_dashdash, - N_("keep the `--` passed as an arg")), - OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option, - N_("stop parsing after the " - "first non-option argument")), - OPT_BOOL(0, "stuck-long", &stuck_long, - N_("output in stuck long form")), - OPT_END(), - }; - static const char * const flag_chars = "*=?!"; - - struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT; - const char **usage = NULL; - struct option *opts = NULL; - int onb = 0, osz = 0, unb = 0, usz = 0; - - strbuf_addstr(&parsed, "set --"); - argc = parse_options(argc, argv, prefix, parseopt_opts, parseopt_usage, - PARSE_OPT_KEEP_DASHDASH); - if (argc < 1 || strcmp(argv[0], "--")) - usage_with_options(parseopt_usage, parseopt_opts); - - /* get the usage up to the first line with a -- on it */ - for (;;) { - if (strbuf_getline(&sb, stdin) == EOF) - die("premature end of input"); - ALLOC_GROW(usage, unb + 1, usz); - if (!strcmp("--", sb.buf)) { - if (unb < 1) - die("no usage string given before the `--' separator"); - usage[unb] = NULL; - break; - } - usage[unb++] = strbuf_detach(&sb, NULL); - } - - /* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */ - while (strbuf_getline(&sb, stdin) != EOF) { - const char *s; - char *help; - struct option *o; - - if (!sb.len) - continue; - - ALLOC_GROW(opts, onb + 1, osz); - memset(opts + onb, 0, sizeof(opts[onb])); - - o = &opts[onb++]; - help = findspace(sb.buf); - if (!help || sb.buf == help) { - o->type = OPTION_GROUP; - o->help = xstrdup(skipspaces(sb.buf)); - continue; - } - - *help = '\0'; - - o->type = OPTION_CALLBACK; - o->help = xstrdup(skipspaces(help+1)); - o->value = &parsed; - o->flags = PARSE_OPT_NOARG; - o->callback = &parseopt_dump; - - /* name(s) */ - s = strpbrk(sb.buf, flag_chars); - if (s == NULL) - s = help; - - if (s - sb.buf == 1) /* short option only */ - o->short_name = *sb.buf; - else if (sb.buf[1] != ',') /* long option only */ - o->long_name = xmemdupz(sb.buf, s - sb.buf); - else { - o->short_name = *sb.buf; - o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2); - } - - /* flags */ - while (s < help) { - switch (*s++) { - case '=': - o->flags &= ~PARSE_OPT_NOARG; - continue; - case '?': - o->flags &= ~PARSE_OPT_NOARG; - o->flags |= PARSE_OPT_OPTARG; - continue; - case '!': - o->flags |= PARSE_OPT_NONEG; - continue; - case '*': - o->flags |= PARSE_OPT_HIDDEN; - continue; - } - s--; - break; - } - - if (s < help) - o->argh = xmemdupz(s, help - s); - } - strbuf_release(&sb); - - /* put an OPT_END() */ - ALLOC_GROW(opts, onb + 1, osz); - memset(opts + onb, 0, sizeof(opts[onb])); - argc = parse_options(argc, argv, prefix, opts, usage, - (keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0) | - (stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) | - PARSE_OPT_SHELL_EVAL); - - strbuf_addstr(&parsed, " --"); - sq_quote_argv(&parsed, argv); - puts(parsed.buf); - return 0; -} - -static int cmd_sq_quote(int argc, const char **argv) -{ - struct strbuf buf = STRBUF_INIT; - - if (argc) - sq_quote_argv(&buf, argv); - printf("%s\n", buf.buf); - strbuf_release(&buf); - - return 0; -} - -static void die_no_single_rev(int quiet) -{ - if (quiet) - exit(1); - else - die("Needed a single revision"); -} - -static const char builtin_rev_parse_usage[] = -N_("git rev-parse --parseopt [<options>] -- [<args>...]\n" - " or: git rev-parse --sq-quote [<arg>...]\n" - " or: git rev-parse [<options>] [<arg>...]\n" - "\n" - "Run \"git rev-parse --parseopt -h\" for more information on the first usage."); - -/* - * Parse "opt" or "opt=<value>", setting value respectively to either - * NULL or the string after "=". - */ -static int opt_with_value(const char *arg, const char *opt, const char **value) -{ - if (skip_prefix(arg, opt, &arg)) { - if (!*arg) { - *value = NULL; - return 1; - } - if (*arg++ == '=') { - *value = arg; - return 1; - } - } - return 0; -} - -static void handle_ref_opt(const char *pattern, const char *prefix) -{ - if (pattern) - for_each_glob_ref_in(show_reference, pattern, prefix, NULL); - else - for_each_ref_in(prefix, show_reference, NULL); - clear_ref_exclusion(&ref_excludes); -} - -int cmd_rev_parse(int argc, const char **argv, const char *prefix) -{ - int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0; - int did_repo_setup = 0; - int has_dashdash = 0; - int output_prefix = 0; - struct object_id oid; - unsigned int flags = 0; - const char *name = NULL; - struct object_context unused; - struct strbuf buf = STRBUF_INIT; - const int hexsz = the_hash_algo->hexsz; - - if (argc > 1 && !strcmp("--parseopt", argv[1])) - return cmd_parseopt(argc - 1, argv + 1, prefix); - - if (argc > 1 && !strcmp("--sq-quote", argv[1])) - return cmd_sq_quote(argc - 2, argv + 2); - - if (argc > 1 && !strcmp("-h", argv[1])) - usage(builtin_rev_parse_usage); - - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "--")) { - has_dashdash = 1; - break; - } - } - - /* No options; just report on whether we're in a git repo or not. */ - if (argc == 1) { - setup_git_directory(); - git_config(git_default_config, NULL); - return 0; - } - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--local-env-vars")) { - int i; - for (i = 0; local_repo_env[i]; i++) - printf("%s\n", local_repo_env[i]); - continue; - } - if (!strcmp(arg, "--resolve-git-dir")) { - const char *gitdir = argv[++i]; - if (!gitdir) - die("--resolve-git-dir requires an argument"); - gitdir = resolve_gitdir(gitdir); - if (!gitdir) - die("not a gitdir '%s'", argv[i]); - puts(gitdir); - continue; - } - - /* The rest of the options require a git repository. */ - if (!did_repo_setup) { - prefix = setup_git_directory(); - git_config(git_default_config, NULL); - did_repo_setup = 1; - } - - if (!strcmp(arg, "--git-path")) { - if (!argv[i + 1]) - die("--git-path requires an argument"); - strbuf_reset(&buf); - puts(relative_path(git_path("%s", argv[i + 1]), - prefix, &buf)); - i++; - continue; - } - if (as_is) { - if (show_file(arg, output_prefix) && as_is < 2) - verify_filename(prefix, arg, 0); - continue; - } - if (!strcmp(arg,"-n")) { - if (++i >= argc) - die("-n requires an argument"); - if ((filter & DO_FLAGS) && (filter & DO_REVS)) { - show(arg); - show(argv[i]); - } - continue; - } - if (starts_with(arg, "-n")) { - if ((filter & DO_FLAGS) && (filter & DO_REVS)) - show(arg); - continue; - } - - if (*arg == '-') { - if (!strcmp(arg, "--")) { - as_is = 2; - /* Pass on the "--" if we show anything but files.. */ - if (filter & (DO_FLAGS | DO_REVS)) - show_file(arg, 0); - continue; - } - if (!strcmp(arg, "--default")) { - def = argv[++i]; - if (!def) - die("--default requires an argument"); - continue; - } - if (!strcmp(arg, "--prefix")) { - prefix = argv[++i]; - if (!prefix) - die("--prefix requires an argument"); - startup_info->prefix = prefix; - output_prefix = 1; - continue; - } - if (!strcmp(arg, "--revs-only")) { - filter &= ~DO_NOREV; - continue; - } - if (!strcmp(arg, "--no-revs")) { - filter &= ~DO_REVS; - continue; - } - if (!strcmp(arg, "--flags")) { - filter &= ~DO_NONFLAGS; - continue; - } - if (!strcmp(arg, "--no-flags")) { - filter &= ~DO_FLAGS; - continue; - } - if (!strcmp(arg, "--verify")) { - filter &= ~(DO_FLAGS|DO_NOREV); - verify = 1; - continue; - } - if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) { - quiet = 1; - flags |= GET_OID_QUIETLY; - continue; - } - if (opt_with_value(arg, "--short", &arg)) { - filter &= ~(DO_FLAGS|DO_NOREV); - verify = 1; - abbrev = DEFAULT_ABBREV; - if (!arg) - continue; - abbrev = strtoul(arg, NULL, 10); - if (abbrev < MINIMUM_ABBREV) - abbrev = MINIMUM_ABBREV; - else if (hexsz <= abbrev) - abbrev = hexsz; - continue; - } - if (!strcmp(arg, "--sq")) { - output_sq = 1; - continue; - } - if (!strcmp(arg, "--not")) { - show_type ^= REVERSED; - continue; - } - if (!strcmp(arg, "--symbolic")) { - symbolic = SHOW_SYMBOLIC_ASIS; - continue; - } - if (!strcmp(arg, "--symbolic-full-name")) { - symbolic = SHOW_SYMBOLIC_FULL; - continue; - } - if (opt_with_value(arg, "--abbrev-ref", &arg)) { - abbrev_ref = 1; - abbrev_ref_strict = warn_ambiguous_refs; - if (arg) { - if (!strcmp(arg, "strict")) - abbrev_ref_strict = 1; - else if (!strcmp(arg, "loose")) - abbrev_ref_strict = 0; - else - die("unknown mode for --abbrev-ref: %s", - arg); - } - continue; - } - if (!strcmp(arg, "--all")) { - for_each_ref(show_reference, NULL); - clear_ref_exclusion(&ref_excludes); - continue; - } - if (skip_prefix(arg, "--disambiguate=", &arg)) { - for_each_abbrev(arg, show_abbrev, NULL); - continue; - } - if (!strcmp(arg, "--bisect")) { - for_each_fullref_in("refs/bisect/bad", show_reference, NULL, 0); - for_each_fullref_in("refs/bisect/good", anti_reference, NULL, 0); - continue; - } - if (opt_with_value(arg, "--branches", &arg)) { - handle_ref_opt(arg, "refs/heads/"); - continue; - } - if (opt_with_value(arg, "--tags", &arg)) { - handle_ref_opt(arg, "refs/tags/"); - continue; - } - if (skip_prefix(arg, "--glob=", &arg)) { - handle_ref_opt(arg, NULL); - continue; - } - if (opt_with_value(arg, "--remotes", &arg)) { - handle_ref_opt(arg, "refs/remotes/"); - continue; - } - if (skip_prefix(arg, "--exclude=", &arg)) { - add_ref_exclusion(&ref_excludes, arg); - continue; - } - if (!strcmp(arg, "--show-toplevel")) { - const char *work_tree = get_git_work_tree(); - if (work_tree) - puts(work_tree); - else - die("this operation must be run in a work tree"); - continue; - } - if (!strcmp(arg, "--show-superproject-working-tree")) { - struct strbuf superproject = STRBUF_INIT; - if (get_superproject_working_tree(&superproject)) - puts(superproject.buf); - strbuf_release(&superproject); - continue; - } - if (!strcmp(arg, "--show-prefix")) { - if (prefix) - puts(prefix); - else - putchar('\n'); - continue; - } - if (!strcmp(arg, "--show-cdup")) { - const char *pfx = prefix; - if (!is_inside_work_tree()) { - const char *work_tree = - get_git_work_tree(); - if (work_tree) - printf("%s\n", work_tree); - continue; - } - while (pfx) { - pfx = strchr(pfx, '/'); - if (pfx) { - pfx++; - printf("../"); - } - } - putchar('\n'); - continue; - } - if (!strcmp(arg, "--git-dir") || - !strcmp(arg, "--absolute-git-dir")) { - const char *gitdir = getenv(GIT_DIR_ENVIRONMENT); - char *cwd; - int len; - if (arg[2] == 'g') { /* --git-dir */ - if (gitdir) { - puts(gitdir); - continue; - } - if (!prefix) { - puts(".git"); - continue; - } - } else { /* --absolute-git-dir */ - if (!gitdir && !prefix) - gitdir = ".git"; - if (gitdir) { - struct strbuf realpath = STRBUF_INIT; - strbuf_realpath(&realpath, gitdir, 1); - puts(realpath.buf); - strbuf_release(&realpath); - continue; - } - } - cwd = xgetcwd(); - len = strlen(cwd); - printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : ""); - free(cwd); - continue; - } - if (!strcmp(arg, "--git-common-dir")) { - strbuf_reset(&buf); - puts(relative_path(get_git_common_dir(), - prefix, &buf)); - continue; - } - if (!strcmp(arg, "--is-inside-git-dir")) { - printf("%s\n", is_inside_git_dir() ? "true" - : "false"); - continue; - } - if (!strcmp(arg, "--is-inside-work-tree")) { - printf("%s\n", is_inside_work_tree() ? "true" - : "false"); - continue; - } - if (!strcmp(arg, "--is-bare-repository")) { - printf("%s\n", is_bare_repository() ? "true" - : "false"); - continue; - } - if (!strcmp(arg, "--is-shallow-repository")) { - printf("%s\n", - is_repository_shallow(the_repository) ? "true" - : "false"); - continue; - } - if (!strcmp(arg, "--shared-index-path")) { - if (read_cache() < 0) - die(_("Could not read the index")); - if (the_index.split_index) { - const struct object_id *oid = &the_index.split_index->base_oid; - const char *path = git_path("sharedindex.%s", oid_to_hex(oid)); - strbuf_reset(&buf); - puts(relative_path(path, prefix, &buf)); - } - continue; - } - if (skip_prefix(arg, "--since=", &arg)) { - show_datestring("--max-age=", arg); - continue; - } - if (skip_prefix(arg, "--after=", &arg)) { - show_datestring("--max-age=", arg); - continue; - } - if (skip_prefix(arg, "--before=", &arg)) { - show_datestring("--min-age=", arg); - continue; - } - if (skip_prefix(arg, "--until=", &arg)) { - show_datestring("--min-age=", arg); - continue; - } - if (opt_with_value(arg, "--show-object-format", &arg)) { - const char *val = arg ? arg : "storage"; - - if (strcmp(val, "storage") && - strcmp(val, "input") && - strcmp(val, "output")) - die("unknown mode for --show-object-format: %s", - arg); - puts(the_hash_algo->name); - continue; - } - if (show_flag(arg) && verify) - die_no_single_rev(quiet); - continue; - } - - /* Not a flag argument */ - if (try_difference(arg)) - continue; - if (try_parent_shorthands(arg)) - continue; - name = arg; - type = NORMAL; - if (*arg == '^') { - name++; - type = REVERSED; - } - if (!get_oid_with_context(the_repository, name, - flags, &oid, &unused)) { - if (verify) - revs_count++; - else - show_rev(type, &oid, name); - continue; - } - if (verify) - die_no_single_rev(quiet); - if (has_dashdash) - die("bad revision '%s'", arg); - as_is = 1; - if (!show_file(arg, output_prefix)) - continue; - verify_filename(prefix, arg, 1); - } - strbuf_release(&buf); - if (verify) { - if (revs_count == 1) { - show_rev(type, &oid, name); - return 0; - } else if (revs_count == 0 && show_default()) - return 0; - die_no_single_rev(quiet); - } else - show_default(); - return 0; -} diff --git a/third_party/git/builtin/revert.c b/third_party/git/builtin/revert.c deleted file mode 100644 index f61cc5d82cf2..000000000000 --- a/third_party/git/builtin/revert.c +++ /dev/null @@ -1,247 +0,0 @@ -#include "cache.h" -#include "config.h" -#include "builtin.h" -#include "parse-options.h" -#include "diff.h" -#include "revision.h" -#include "rerere.h" -#include "dir.h" -#include "sequencer.h" -#include "branch.h" - -/* - * This implements the builtins revert and cherry-pick. - * - * Copyright (c) 2007 Johannes E. Schindelin - * - * Based on git-revert.sh, which is - * - * Copyright (c) 2005 Linus Torvalds - * Copyright (c) 2005 Junio C Hamano - */ - -static const char * const revert_usage[] = { - N_("git revert [<options>] <commit-ish>..."), - N_("git revert <subcommand>"), - NULL -}; - -static const char * const cherry_pick_usage[] = { - N_("git cherry-pick [<options>] <commit-ish>..."), - N_("git cherry-pick <subcommand>"), - NULL -}; - -static const char *action_name(const struct replay_opts *opts) -{ - return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick"; -} - -static const char * const *revert_or_cherry_pick_usage(struct replay_opts *opts) -{ - return opts->action == REPLAY_REVERT ? revert_usage : cherry_pick_usage; -} - -static int option_parse_x(const struct option *opt, - const char *arg, int unset) -{ - struct replay_opts **opts_ptr = opt->value; - struct replay_opts *opts = *opts_ptr; - - if (unset) - return 0; - - ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc); - opts->xopts[opts->xopts_nr++] = xstrdup(arg); - return 0; -} - -static int option_parse_m(const struct option *opt, - const char *arg, int unset) -{ - struct replay_opts *replay = opt->value; - char *end; - - if (unset) { - replay->mainline = 0; - return 0; - } - - replay->mainline = strtol(arg, &end, 10); - if (*end || replay->mainline <= 0) - return error(_("option `%s' expects a number greater than zero"), - opt->long_name); - - return 0; -} - -LAST_ARG_MUST_BE_NULL -static void verify_opt_compatible(const char *me, const char *base_opt, ...) -{ - const char *this_opt; - va_list ap; - - va_start(ap, base_opt); - while ((this_opt = va_arg(ap, const char *))) { - if (va_arg(ap, int)) - break; - } - va_end(ap); - - if (this_opt) - die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt); -} - -static int run_sequencer(int argc, const char **argv, struct replay_opts *opts) -{ - const char * const * usage_str = revert_or_cherry_pick_usage(opts); - const char *me = action_name(opts); - const char *cleanup_arg = NULL; - int cmd = 0; - struct option base_options[] = { - OPT_CMDMODE(0, "quit", &cmd, N_("end revert or cherry-pick sequence"), 'q'), - OPT_CMDMODE(0, "continue", &cmd, N_("resume revert or cherry-pick sequence"), 'c'), - OPT_CMDMODE(0, "abort", &cmd, N_("cancel revert or cherry-pick sequence"), 'a'), - OPT_CMDMODE(0, "skip", &cmd, N_("skip current commit and continue"), 's'), - OPT_CLEANUP(&cleanup_arg), - OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")), - OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")), - OPT_NOOP_NOARG('r', NULL), - OPT_BOOL('s', "signoff", &opts->signoff, N_("add Signed-off-by:")), - OPT_CALLBACK('m', "mainline", opts, N_("parent-number"), - N_("select mainline parent"), option_parse_m), - OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto), - OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")), - OPT_CALLBACK('X', "strategy-option", &opts, N_("option"), - N_("option for merge strategy"), option_parse_x), - { OPTION_STRING, 'S', "gpg-sign", &opts->gpg_sign, N_("key-id"), - N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, - OPT_END() - }; - struct option *options = base_options; - - if (opts->action == REPLAY_PICK) { - struct option cp_extra[] = { - OPT_BOOL('x', NULL, &opts->record_origin, N_("append commit name")), - OPT_BOOL(0, "ff", &opts->allow_ff, N_("allow fast-forward")), - OPT_BOOL(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")), - OPT_BOOL(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")), - OPT_BOOL(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")), - OPT_END(), - }; - options = parse_options_concat(options, cp_extra); - } - - argc = parse_options(argc, argv, NULL, options, usage_str, - PARSE_OPT_KEEP_ARGV0 | - PARSE_OPT_KEEP_UNKNOWN); - - /* implies allow_empty */ - if (opts->keep_redundant_commits) - opts->allow_empty = 1; - - if (cleanup_arg) { - opts->default_msg_cleanup = get_cleanup_mode(cleanup_arg, 1); - opts->explicit_cleanup = 1; - } - - /* Check for incompatible command line arguments */ - if (cmd) { - char *this_operation; - if (cmd == 'q') - this_operation = "--quit"; - else if (cmd == 'c') - this_operation = "--continue"; - else if (cmd == 's') - this_operation = "--skip"; - else { - assert(cmd == 'a'); - this_operation = "--abort"; - } - - verify_opt_compatible(me, this_operation, - "--no-commit", opts->no_commit, - "--signoff", opts->signoff, - "--mainline", opts->mainline, - "--strategy", opts->strategy ? 1 : 0, - "--strategy-option", opts->xopts ? 1 : 0, - "-x", opts->record_origin, - "--ff", opts->allow_ff, - "--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE, - "--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE, - NULL); - } - - if (opts->allow_ff) - verify_opt_compatible(me, "--ff", - "--signoff", opts->signoff, - "--no-commit", opts->no_commit, - "-x", opts->record_origin, - "--edit", opts->edit, - NULL); - - if (cmd) { - opts->revs = NULL; - } else { - struct setup_revision_opt s_r_opt; - opts->revs = xmalloc(sizeof(*opts->revs)); - repo_init_revisions(the_repository, opts->revs, NULL); - opts->revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED; - if (argc < 2) - usage_with_options(usage_str, options); - if (!strcmp(argv[1], "-")) - argv[1] = "@{-1}"; - memset(&s_r_opt, 0, sizeof(s_r_opt)); - s_r_opt.assume_dashdash = 1; - argc = setup_revisions(argc, argv, opts->revs, &s_r_opt); - } - - if (argc > 1) - usage_with_options(usage_str, options); - - /* These option values will be free()d */ - opts->gpg_sign = xstrdup_or_null(opts->gpg_sign); - opts->strategy = xstrdup_or_null(opts->strategy); - - if (cmd == 'q') { - int ret = sequencer_remove_state(opts); - if (!ret) - remove_branch_state(the_repository, 0); - return ret; - } - if (cmd == 'c') - return sequencer_continue(the_repository, opts); - if (cmd == 'a') - return sequencer_rollback(the_repository, opts); - if (cmd == 's') - return sequencer_skip(the_repository, opts); - return sequencer_pick_revisions(the_repository, opts); -} - -int cmd_revert(int argc, const char **argv, const char *prefix) -{ - struct replay_opts opts = REPLAY_OPTS_INIT; - int res; - - if (isatty(0)) - opts.edit = 1; - opts.action = REPLAY_REVERT; - sequencer_init_config(&opts); - res = run_sequencer(argc, argv, &opts); - if (res < 0) - die(_("revert failed")); - return res; -} - -int cmd_cherry_pick(int argc, const char **argv, const char *prefix) -{ - struct replay_opts opts = REPLAY_OPTS_INIT; - int res; - - opts.action = REPLAY_PICK; - sequencer_init_config(&opts); - res = run_sequencer(argc, argv, &opts); - if (res < 0) - die(_("cherry-pick failed")); - return res; -} diff --git a/third_party/git/builtin/rm.c b/third_party/git/builtin/rm.c deleted file mode 100644 index 4858631e0f02..000000000000 --- a/third_party/git/builtin/rm.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - * "git rm" builtin command - * - * Copyright (C) Linus Torvalds 2006 - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "config.h" -#include "lockfile.h" -#include "dir.h" -#include "cache-tree.h" -#include "tree-walk.h" -#include "parse-options.h" -#include "string-list.h" -#include "submodule.h" -#include "pathspec.h" - -static const char * const builtin_rm_usage[] = { - N_("git rm [<options>] [--] <file>..."), - NULL -}; - -static struct { - int nr, alloc; - struct { - const char *name; - char is_submodule; - } *entry; -} list; - -static int get_ours_cache_pos(const char *path, int pos) -{ - int i = -pos - 1; - - while ((i < active_nr) && !strcmp(active_cache[i]->name, path)) { - if (ce_stage(active_cache[i]) == 2) - return i; - i++; - } - return -1; -} - -static void print_error_files(struct string_list *files_list, - const char *main_msg, - const char *hints_msg, - int *errs) -{ - if (files_list->nr) { - int i; - struct strbuf err_msg = STRBUF_INIT; - - strbuf_addstr(&err_msg, main_msg); - for (i = 0; i < files_list->nr; i++) - strbuf_addf(&err_msg, - "\n %s", - files_list->items[i].string); - if (advice_rm_hints) - strbuf_addstr(&err_msg, hints_msg); - *errs = error("%s", err_msg.buf); - strbuf_release(&err_msg); - } -} - -static void submodules_absorb_gitdir_if_needed(void) -{ - int i; - for (i = 0; i < list.nr; i++) { - const char *name = list.entry[i].name; - int pos; - const struct cache_entry *ce; - - pos = cache_name_pos(name, strlen(name)); - if (pos < 0) { - pos = get_ours_cache_pos(name, pos); - if (pos < 0) - continue; - } - ce = active_cache[pos]; - - if (!S_ISGITLINK(ce->ce_mode) || - !file_exists(ce->name) || - is_empty_dir(name)) - continue; - - if (!submodule_uses_gitfile(name)) - absorb_git_dir_into_superproject(name, - ABSORB_GITDIR_RECURSE_SUBMODULES); - } -} - -static int check_local_mod(struct object_id *head, int index_only) -{ - /* - * Items in list are already sorted in the cache order, - * so we could do this a lot more efficiently by using - * tree_desc based traversal if we wanted to, but I am - * lazy, and who cares if removal of files is a tad - * slower than the theoretical maximum speed? - */ - int i, no_head; - int errs = 0; - struct string_list files_staged = STRING_LIST_INIT_NODUP; - struct string_list files_cached = STRING_LIST_INIT_NODUP; - struct string_list files_local = STRING_LIST_INIT_NODUP; - - no_head = is_null_oid(head); - for (i = 0; i < list.nr; i++) { - struct stat st; - int pos; - const struct cache_entry *ce; - const char *name = list.entry[i].name; - struct object_id oid; - unsigned short mode; - int local_changes = 0; - int staged_changes = 0; - - pos = cache_name_pos(name, strlen(name)); - if (pos < 0) { - /* - * Skip unmerged entries except for populated submodules - * that could lose history when removed. - */ - pos = get_ours_cache_pos(name, pos); - if (pos < 0) - continue; - - if (!S_ISGITLINK(active_cache[pos]->ce_mode) || - is_empty_dir(name)) - continue; - } - ce = active_cache[pos]; - - if (lstat(ce->name, &st) < 0) { - if (!is_missing_file_error(errno)) - warning_errno(_("failed to stat '%s'"), ce->name); - /* It already vanished from the working tree */ - continue; - } - else if (S_ISDIR(st.st_mode)) { - /* if a file was removed and it is now a - * directory, that is the same as ENOENT as - * far as git is concerned; we do not track - * directories unless they are submodules. - */ - if (!S_ISGITLINK(ce->ce_mode)) - continue; - } - - /* - * "rm" of a path that has changes need to be treated - * carefully not to allow losing local changes - * accidentally. A local change could be (1) file in - * work tree is different since the index; and/or (2) - * the user staged a content that is different from - * the current commit in the index. - * - * In such a case, you would need to --force the - * removal. However, "rm --cached" (remove only from - * the index) is safe if the index matches the file in - * the work tree or the HEAD commit, as it means that - * the content being removed is available elsewhere. - */ - - /* - * Is the index different from the file in the work tree? - * If it's a submodule, is its work tree modified? - */ - if (ce_match_stat(ce, &st, 0) || - (S_ISGITLINK(ce->ce_mode) && - bad_to_remove_submodule(ce->name, - SUBMODULE_REMOVAL_DIE_ON_ERROR | - SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED))) - local_changes = 1; - - /* - * Is the index different from the HEAD commit? By - * definition, before the very initial commit, - * anything staged in the index is treated by the same - * way as changed from the HEAD. - */ - if (no_head - || get_tree_entry(the_repository, head, name, &oid, &mode) - || ce->ce_mode != create_ce_mode(mode) - || !oideq(&ce->oid, &oid)) - staged_changes = 1; - - /* - * If the index does not match the file in the work - * tree and if it does not match the HEAD commit - * either, (1) "git rm" without --cached definitely - * will lose information; (2) "git rm --cached" will - * lose information unless it is about removing an - * "intent to add" entry. - */ - if (local_changes && staged_changes) { - if (!index_only || !ce_intent_to_add(ce)) - string_list_append(&files_staged, name); - } - else if (!index_only) { - if (staged_changes) - string_list_append(&files_cached, name); - if (local_changes) - string_list_append(&files_local, name); - } - } - print_error_files(&files_staged, - Q_("the following file has staged content different " - "from both the\nfile and the HEAD:", - "the following files have staged content different" - " from both the\nfile and the HEAD:", - files_staged.nr), - _("\n(use -f to force removal)"), - &errs); - string_list_clear(&files_staged, 0); - print_error_files(&files_cached, - Q_("the following file has changes " - "staged in the index:", - "the following files have changes " - "staged in the index:", files_cached.nr), - _("\n(use --cached to keep the file," - " or -f to force removal)"), - &errs); - string_list_clear(&files_cached, 0); - - print_error_files(&files_local, - Q_("the following file has local modifications:", - "the following files have local modifications:", - files_local.nr), - _("\n(use --cached to keep the file," - " or -f to force removal)"), - &errs); - string_list_clear(&files_local, 0); - - return errs; -} - -static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0; -static int ignore_unmatch = 0, pathspec_file_nul; -static char *pathspec_from_file; - -static struct option builtin_rm_options[] = { - OPT__DRY_RUN(&show_only, N_("dry run")), - OPT__QUIET(&quiet, N_("do not list removed files")), - OPT_BOOL( 0 , "cached", &index_only, N_("only remove from the index")), - OPT__FORCE(&force, N_("override the up-to-date check"), PARSE_OPT_NOCOMPLETE), - OPT_BOOL('r', NULL, &recursive, N_("allow recursive removal")), - OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch, - N_("exit with a zero status even if nothing matched")), - OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), - OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), - OPT_END(), -}; - -int cmd_rm(int argc, const char **argv, const char *prefix) -{ - struct lock_file lock_file = LOCK_INIT; - int i; - struct pathspec pathspec; - char *seen; - - git_config(git_default_config, NULL); - - argc = parse_options(argc, argv, prefix, builtin_rm_options, - builtin_rm_usage, 0); - - parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_CWD, - prefix, argv); - - if (pathspec_from_file) { - if (pathspec.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); - - parse_pathspec_file(&pathspec, 0, - PATHSPEC_PREFER_CWD, - prefix, pathspec_from_file, pathspec_file_nul); - } else if (pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); - } - - if (!pathspec.nr) - die(_("No pathspec was given. Which files should I remove?")); - - if (!index_only) - setup_work_tree(); - - hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); - - if (read_cache() < 0) - die(_("index file corrupt")); - - refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL); - - seen = xcalloc(pathspec.nr, 1); - - for (i = 0; i < active_nr; i++) { - const struct cache_entry *ce = active_cache[i]; - if (!ce_path_match(&the_index, ce, &pathspec, seen)) - continue; - ALLOC_GROW(list.entry, list.nr + 1, list.alloc); - list.entry[list.nr].name = xstrdup(ce->name); - list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode); - if (list.entry[list.nr++].is_submodule && - !is_staging_gitmodules_ok(&the_index)) - die(_("please stage your changes to .gitmodules or stash them to proceed")); - } - - if (pathspec.nr) { - const char *original; - int seen_any = 0; - for (i = 0; i < pathspec.nr; i++) { - original = pathspec.items[i].original; - if (!seen[i]) { - if (!ignore_unmatch) { - die(_("pathspec '%s' did not match any files"), - original); - } - } - else { - seen_any = 1; - } - if (!recursive && seen[i] == MATCHED_RECURSIVELY) - die(_("not removing '%s' recursively without -r"), - *original ? original : "."); - } - - if (!seen_any) - exit(0); - } - - if (!index_only) - submodules_absorb_gitdir_if_needed(); - - /* - * If not forced, the file, the index and the HEAD (if exists) - * must match; but the file can already been removed, since - * this sequence is a natural "novice" way: - * - * rm F; git rm F - * - * Further, if HEAD commit exists, "diff-index --cached" must - * report no changes unless forced. - */ - if (!force) { - struct object_id oid; - if (get_oid("HEAD", &oid)) - oidclr(&oid); - if (check_local_mod(&oid, index_only)) - exit(1); - } - - /* - * First remove the names from the index: we won't commit - * the index unless all of them succeed. - */ - for (i = 0; i < list.nr; i++) { - const char *path = list.entry[i].name; - if (!quiet) - printf("rm '%s'\n", path); - - if (remove_file_from_cache(path)) - die(_("git rm: unable to remove %s"), path); - } - - if (show_only) - return 0; - - /* - * Then, unless we used "--cached", remove the filenames from - * the workspace. If we fail to remove the first one, we - * abort the "git rm" (but once we've successfully removed - * any file at all, we'll go ahead and commit to it all: - * by then we've already committed ourselves and can't fail - * in the middle) - */ - if (!index_only) { - int removed = 0, gitmodules_modified = 0; - struct strbuf buf = STRBUF_INIT; - for (i = 0; i < list.nr; i++) { - const char *path = list.entry[i].name; - if (list.entry[i].is_submodule) { - strbuf_reset(&buf); - strbuf_addstr(&buf, path); - if (remove_dir_recursively(&buf, 0)) - die(_("could not remove '%s'"), path); - - removed = 1; - if (!remove_path_from_gitmodules(path)) - gitmodules_modified = 1; - continue; - } - if (!remove_path(path)) { - removed = 1; - continue; - } - if (!removed) - die_errno("git rm: '%s'", path); - } - strbuf_release(&buf); - if (gitmodules_modified) - stage_updated_gitmodules(&the_index); - } - - if (write_locked_index(&the_index, &lock_file, - COMMIT_LOCK | SKIP_IF_UNCHANGED)) - die(_("Unable to write new index file")); - - return 0; -} diff --git a/third_party/git/builtin/send-pack.c b/third_party/git/builtin/send-pack.c deleted file mode 100644 index 7af148d7332f..000000000000 --- a/third_party/git/builtin/send-pack.c +++ /dev/null @@ -1,328 +0,0 @@ -#include "builtin.h" -#include "config.h" -#include "commit.h" -#include "refs.h" -#include "pkt-line.h" -#include "sideband.h" -#include "run-command.h" -#include "remote.h" -#include "connect.h" -#include "send-pack.h" -#include "quote.h" -#include "transport.h" -#include "version.h" -#include "oid-array.h" -#include "gpg-interface.h" -#include "gettext.h" -#include "protocol.h" - -static const char * const send_pack_usage[] = { - N_("git send-pack [--all | --mirror] [--dry-run] [--force] " - "[--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] " - "[<host>:]<directory> [<ref>...]\n" - " --all and explicit <ref> specification are mutually exclusive."), - NULL, -}; - -static struct send_pack_args args; - -static void print_helper_status(struct ref *ref) -{ - struct strbuf buf = STRBUF_INIT; - struct ref_push_report *report; - - for (; ref; ref = ref->next) { - const char *msg = NULL; - const char *res; - int count = 0; - - switch(ref->status) { - case REF_STATUS_NONE: - res = "error"; - msg = "no match"; - break; - - case REF_STATUS_OK: - res = "ok"; - break; - - case REF_STATUS_UPTODATE: - res = "ok"; - msg = "up to date"; - break; - - case REF_STATUS_REJECT_NONFASTFORWARD: - res = "error"; - msg = "non-fast forward"; - break; - - case REF_STATUS_REJECT_FETCH_FIRST: - res = "error"; - msg = "fetch first"; - break; - - case REF_STATUS_REJECT_NEEDS_FORCE: - res = "error"; - msg = "needs force"; - break; - - case REF_STATUS_REJECT_STALE: - res = "error"; - msg = "stale info"; - break; - - case REF_STATUS_REJECT_ALREADY_EXISTS: - res = "error"; - msg = "already exists"; - break; - - case REF_STATUS_REJECT_NODELETE: - case REF_STATUS_REMOTE_REJECT: - res = "error"; - break; - - case REF_STATUS_EXPECTING_REPORT: - default: - continue; - } - - strbuf_reset(&buf); - strbuf_addf(&buf, "%s %s", res, ref->name); - if (ref->remote_status) - msg = ref->remote_status; - if (msg) { - strbuf_addch(&buf, ' '); - quote_two_c_style(&buf, "", msg, 0); - } - strbuf_addch(&buf, '\n'); - - if (ref->status == REF_STATUS_OK) { - for (report = ref->report; report; report = report->next) { - if (count++ > 0) - strbuf_addf(&buf, "ok %s\n", ref->name); - if (report->ref_name) - strbuf_addf(&buf, "option refname %s\n", - report->ref_name); - if (report->old_oid) - strbuf_addf(&buf, "option old-oid %s\n", - oid_to_hex(report->old_oid)); - if (report->new_oid) - strbuf_addf(&buf, "option new-oid %s\n", - oid_to_hex(report->new_oid)); - if (report->forced_update) - strbuf_addstr(&buf, "option forced-update\n"); - } - } - write_or_die(1, buf.buf, buf.len); - } - strbuf_release(&buf); -} - -static int send_pack_config(const char *k, const char *v, void *cb) -{ - git_gpg_config(k, v, NULL); - - if (!strcmp(k, "push.gpgsign")) { - const char *value; - if (!git_config_get_value("push.gpgsign", &value)) { - switch (git_parse_maybe_bool(value)) { - case 0: - args.push_cert = SEND_PACK_PUSH_CERT_NEVER; - break; - case 1: - args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS; - break; - default: - if (value && !strcasecmp(value, "if-asked")) - args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED; - else - return error("Invalid value for '%s'", k); - } - } - } - return git_default_config(k, v, cb); -} - -int cmd_send_pack(int argc, const char **argv, const char *prefix) -{ - struct refspec rs = REFSPEC_INIT_PUSH; - const char *remote_name = NULL; - struct remote *remote = NULL; - const char *dest = NULL; - int fd[2]; - struct child_process *conn; - struct oid_array extra_have = OID_ARRAY_INIT; - struct oid_array shallow = OID_ARRAY_INIT; - struct ref *remote_refs, *local_refs; - int ret; - int helper_status = 0; - int send_all = 0; - int verbose = 0; - const char *receivepack = "git-receive-pack"; - unsigned dry_run = 0; - unsigned send_mirror = 0; - unsigned force_update = 0; - unsigned quiet = 0; - int push_cert = 0; - struct string_list push_options = STRING_LIST_INIT_NODUP; - unsigned use_thin_pack = 0; - unsigned atomic = 0; - unsigned stateless_rpc = 0; - int flags; - unsigned int reject_reasons; - int progress = -1; - int from_stdin = 0; - struct push_cas_option cas = {0}; - struct packet_reader reader; - - struct option options[] = { - OPT__VERBOSITY(&verbose), - OPT_STRING(0, "receive-pack", &receivepack, "receive-pack", N_("receive pack program")), - OPT_STRING(0, "exec", &receivepack, "receive-pack", N_("receive pack program")), - OPT_STRING(0, "remote", &remote_name, "remote", N_("remote name")), - OPT_BOOL(0, "all", &send_all, N_("push all refs")), - OPT_BOOL('n' , "dry-run", &dry_run, N_("dry run")), - OPT_BOOL(0, "mirror", &send_mirror, N_("mirror all refs")), - OPT_BOOL('f', "force", &force_update, N_("force updates")), - OPT_CALLBACK_F(0, "signed", &push_cert, "(yes|no|if-asked)", N_("GPG sign the push"), - PARSE_OPT_OPTARG, option_parse_push_signed), - OPT_STRING_LIST(0, "push-option", &push_options, - N_("server-specific"), - N_("option to transmit")), - OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), - OPT_BOOL(0, "thin", &use_thin_pack, N_("use thin pack")), - OPT_BOOL(0, "atomic", &atomic, N_("request atomic transaction on remote side")), - OPT_BOOL(0, "stateless-rpc", &stateless_rpc, N_("use stateless RPC protocol")), - OPT_BOOL(0, "stdin", &from_stdin, N_("read refs from stdin")), - OPT_BOOL(0, "helper-status", &helper_status, N_("print status from remote helper")), - OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"), - N_("require old value of ref to be at this value"), - PARSE_OPT_OPTARG, parseopt_push_cas_option), - OPT_END() - }; - - git_config(send_pack_config, NULL); - argc = parse_options(argc, argv, prefix, options, send_pack_usage, 0); - if (argc > 0) { - dest = argv[0]; - refspec_appendn(&rs, argv + 1, argc - 1); - } - - if (!dest) - usage_with_options(send_pack_usage, options); - - args.verbose = verbose; - args.dry_run = dry_run; - args.send_mirror = send_mirror; - args.force_update = force_update; - args.quiet = quiet; - args.push_cert = push_cert; - args.progress = progress; - args.use_thin_pack = use_thin_pack; - args.atomic = atomic; - args.stateless_rpc = stateless_rpc; - args.push_options = push_options.nr ? &push_options : NULL; - - if (from_stdin) { - if (args.stateless_rpc) { - const char *buf; - while ((buf = packet_read_line(0, NULL))) - refspec_append(&rs, buf); - } else { - struct strbuf line = STRBUF_INIT; - while (strbuf_getline(&line, stdin) != EOF) - refspec_append(&rs, line.buf); - strbuf_release(&line); - } - } - - /* - * --all and --mirror are incompatible; neither makes sense - * with any refspecs. - */ - if ((rs.nr > 0 && (send_all || args.send_mirror)) || - (send_all && args.send_mirror)) - usage_with_options(send_pack_usage, options); - - if (remote_name) { - remote = remote_get(remote_name); - if (!remote_has_url(remote, dest)) { - die("Destination %s is not a uri for %s", - dest, remote_name); - } - } - - if (progress == -1) - progress = !args.quiet && isatty(2); - args.progress = progress; - - if (args.stateless_rpc) { - conn = NULL; - fd[0] = 0; - fd[1] = 1; - } else { - conn = git_connect(fd, dest, receivepack, - args.verbose ? CONNECT_VERBOSE : 0); - } - - packet_reader_init(&reader, fd[0], NULL, 0, - PACKET_READ_CHOMP_NEWLINE | - PACKET_READ_GENTLE_ON_EOF | - PACKET_READ_DIE_ON_ERR_PACKET); - - switch (discover_version(&reader)) { - case protocol_v2: - die("support for protocol v2 not implemented yet"); - break; - case protocol_v1: - case protocol_v0: - get_remote_heads(&reader, &remote_refs, REF_NORMAL, - &extra_have, &shallow); - break; - case protocol_unknown_version: - BUG("unknown protocol version"); - } - - local_refs = get_local_heads(); - - flags = MATCH_REFS_NONE; - - if (send_all) - flags |= MATCH_REFS_ALL; - if (args.send_mirror) - flags |= MATCH_REFS_MIRROR; - - /* match them up */ - if (match_push_refs(local_refs, &remote_refs, &rs, flags)) - return -1; - - if (!is_empty_cas(&cas)) - apply_push_cas(&cas, remote, remote_refs); - - set_ref_status_for_push(remote_refs, args.send_mirror, - args.force_update); - - ret = send_pack(&args, fd, conn, remote_refs, &extra_have); - - if (helper_status) - print_helper_status(remote_refs); - - close(fd[1]); - close(fd[0]); - - ret |= finish_connect(conn); - - if (!helper_status) - transport_print_push_status(dest, remote_refs, args.verbose, 0, &reject_reasons); - - if (!args.dry_run && remote) { - struct ref *ref; - for (ref = remote_refs; ref; ref = ref->next) - transport_update_tracking_ref(remote, ref, args.verbose); - } - - if (!ret && !transport_refs_pushed(remote_refs)) - fprintf(stderr, "Everything up-to-date\n"); - - return ret; -} diff --git a/third_party/git/builtin/shortlog.c b/third_party/git/builtin/shortlog.c deleted file mode 100644 index 0a5c4968f64e..000000000000 --- a/third_party/git/builtin/shortlog.c +++ /dev/null @@ -1,538 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "commit.h" -#include "diff.h" -#include "string-list.h" -#include "revision.h" -#include "utf8.h" -#include "mailmap.h" -#include "shortlog.h" -#include "parse-options.h" -#include "trailer.h" - -static char const * const shortlog_usage[] = { - N_("git shortlog [<options>] [<revision-range>] [[--] <path>...]"), - N_("git log --pretty=short | git shortlog [<options>]"), - NULL -}; - -/* - * The util field of our string_list_items will contain one of two things: - * - * - if --summary is not in use, it will point to a string list of the - * oneline subjects assigned to this author - * - * - if --summary is in use, we don't need that list; we only need to know - * its size. So we abuse the pointer slot to store our integer counter. - * - * This macro accesses the latter. - */ -#define UTIL_TO_INT(x) ((intptr_t)(x)->util) - -static int compare_by_counter(const void *a1, const void *a2) -{ - const struct string_list_item *i1 = a1, *i2 = a2; - return UTIL_TO_INT(i2) - UTIL_TO_INT(i1); -} - -static int compare_by_list(const void *a1, const void *a2) -{ - const struct string_list_item *i1 = a1, *i2 = a2; - const struct string_list *l1 = i1->util, *l2 = i2->util; - - if (l1->nr < l2->nr) - return 1; - else if (l1->nr == l2->nr) - return 0; - else - return -1; -} - -static void insert_one_record(struct shortlog *log, - const char *ident, - const char *oneline) -{ - struct string_list_item *item; - - item = string_list_insert(&log->list, ident); - - if (log->summary) - item->util = (void *)(UTIL_TO_INT(item) + 1); - else { - const char *dot3 = log->common_repo_prefix; - char *buffer, *p; - struct strbuf subject = STRBUF_INIT; - const char *eol; - - /* Skip any leading whitespace, including any blank lines. */ - while (*oneline && isspace(*oneline)) - oneline++; - eol = strchr(oneline, '\n'); - if (!eol) - eol = oneline + strlen(oneline); - if (starts_with(oneline, "[PATCH")) { - char *eob = strchr(oneline, ']'); - if (eob && (!eol || eob < eol)) - oneline = eob + 1; - } - while (*oneline && isspace(*oneline) && *oneline != '\n') - oneline++; - format_subject(&subject, oneline, " "); - buffer = strbuf_detach(&subject, NULL); - - if (dot3) { - int dot3len = strlen(dot3); - if (dot3len > 5) { - while ((p = strstr(buffer, dot3)) != NULL) { - int taillen = strlen(p) - dot3len; - memcpy(p, "/.../", 5); - memmove(p + 5, p + dot3len, taillen + 1); - } - } - } - - if (item->util == NULL) - item->util = xcalloc(1, sizeof(struct string_list)); - string_list_append(item->util, buffer); - } -} - -static int parse_ident(struct shortlog *log, - struct strbuf *out, const char *in) -{ - const char *mailbuf, *namebuf; - size_t namelen, maillen; - struct ident_split ident; - - if (split_ident_line(&ident, in, strlen(in))) - return -1; - - namebuf = ident.name_begin; - mailbuf = ident.mail_begin; - namelen = ident.name_end - ident.name_begin; - maillen = ident.mail_end - ident.mail_begin; - - map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen); - strbuf_add(out, namebuf, namelen); - if (log->email) - strbuf_addf(out, " <%.*s>", (int)maillen, mailbuf); - - return 0; -} - -static void read_from_stdin(struct shortlog *log) -{ - struct strbuf ident = STRBUF_INIT; - struct strbuf mapped_ident = STRBUF_INIT; - struct strbuf oneline = STRBUF_INIT; - static const char *author_match[2] = { "Author: ", "author " }; - static const char *committer_match[2] = { "Commit: ", "committer " }; - const char **match; - - if (HAS_MULTI_BITS(log->groups)) - die(_("using multiple --group options with stdin is not supported")); - - switch (log->groups) { - case SHORTLOG_GROUP_AUTHOR: - match = author_match; - break; - case SHORTLOG_GROUP_COMMITTER: - match = committer_match; - break; - case SHORTLOG_GROUP_TRAILER: - die(_("using --group=trailer with stdin is not supported")); - default: - BUG("unhandled shortlog group"); - } - - while (strbuf_getline_lf(&ident, stdin) != EOF) { - const char *v; - if (!skip_prefix(ident.buf, match[0], &v) && - !skip_prefix(ident.buf, match[1], &v)) - continue; - while (strbuf_getline_lf(&oneline, stdin) != EOF && - oneline.len) - ; /* discard headers */ - while (strbuf_getline_lf(&oneline, stdin) != EOF && - !oneline.len) - ; /* discard blanks */ - - strbuf_reset(&mapped_ident); - if (parse_ident(log, &mapped_ident, v) < 0) - continue; - - insert_one_record(log, mapped_ident.buf, oneline.buf); - } - strbuf_release(&ident); - strbuf_release(&mapped_ident); - strbuf_release(&oneline); -} - -struct strset_item { - struct hashmap_entry ent; - char value[FLEX_ARRAY]; -}; - -struct strset { - struct hashmap map; -}; - -#define STRSET_INIT { { NULL } } - -static int strset_item_hashcmp(const void *hash_data, - const struct hashmap_entry *entry, - const struct hashmap_entry *entry_or_key, - const void *keydata) -{ - const struct strset_item *a, *b; - - a = container_of(entry, const struct strset_item, ent); - if (keydata) - return strcmp(a->value, keydata); - - b = container_of(entry_or_key, const struct strset_item, ent); - return strcmp(a->value, b->value); -} - -/* - * Adds "str" to the set if it was not already present; returns true if it was - * already there. - */ -static int strset_check_and_add(struct strset *ss, const char *str) -{ - unsigned int hash = strhash(str); - struct strset_item *item; - - if (!ss->map.table) - hashmap_init(&ss->map, strset_item_hashcmp, NULL, 0); - - if (hashmap_get_from_hash(&ss->map, hash, str)) - return 1; - - FLEX_ALLOC_STR(item, value, str); - hashmap_entry_init(&item->ent, hash); - hashmap_add(&ss->map, &item->ent); - return 0; -} - -static void strset_clear(struct strset *ss) -{ - if (!ss->map.table) - return; - hashmap_free_entries(&ss->map, struct strset_item, ent); -} - -static void insert_records_from_trailers(struct shortlog *log, - struct strset *dups, - struct commit *commit, - struct pretty_print_context *ctx, - const char *oneline) -{ - struct trailer_iterator iter; - const char *commit_buffer, *body; - struct strbuf ident = STRBUF_INIT; - - /* - * Using format_commit_message("%B") would be simpler here, but - * this saves us copying the message. - */ - commit_buffer = logmsg_reencode(commit, NULL, ctx->output_encoding); - body = strstr(commit_buffer, "\n\n"); - if (!body) - return; - - trailer_iterator_init(&iter, body); - while (trailer_iterator_advance(&iter)) { - const char *value = iter.val.buf; - - if (!string_list_has_string(&log->trailers, iter.key.buf)) - continue; - - strbuf_reset(&ident); - if (!parse_ident(log, &ident, value)) - value = ident.buf; - - if (strset_check_and_add(dups, value)) - continue; - insert_one_record(log, value, oneline); - } - trailer_iterator_release(&iter); - - strbuf_release(&ident); - unuse_commit_buffer(commit, commit_buffer); -} - -void shortlog_add_commit(struct shortlog *log, struct commit *commit) -{ - struct strbuf ident = STRBUF_INIT; - struct strbuf oneline = STRBUF_INIT; - struct strset dups = STRSET_INIT; - struct pretty_print_context ctx = {0}; - const char *oneline_str; - - ctx.fmt = CMIT_FMT_USERFORMAT; - ctx.abbrev = log->abbrev; - ctx.print_email_subject = 1; - ctx.date_mode.type = DATE_NORMAL; - ctx.output_encoding = get_log_output_encoding(); - - if (!log->summary) { - if (log->user_format) - pretty_print_commit(&ctx, commit, &oneline); - else - format_commit_message(commit, "%s", &oneline, &ctx); - } - oneline_str = oneline.len ? oneline.buf : "<none>"; - - if (log->groups & SHORTLOG_GROUP_AUTHOR) { - strbuf_reset(&ident); - format_commit_message(commit, - log->email ? "%aN <%aE>" : "%aN", - &ident, &ctx); - if (!HAS_MULTI_BITS(log->groups) || - !strset_check_and_add(&dups, ident.buf)) - insert_one_record(log, ident.buf, oneline_str); - } - if (log->groups & SHORTLOG_GROUP_COMMITTER) { - strbuf_reset(&ident); - format_commit_message(commit, - log->email ? "%cN <%cE>" : "%cN", - &ident, &ctx); - if (!HAS_MULTI_BITS(log->groups) || - !strset_check_and_add(&dups, ident.buf)) - insert_one_record(log, ident.buf, oneline_str); - } - if (log->groups & SHORTLOG_GROUP_TRAILER) { - insert_records_from_trailers(log, &dups, commit, &ctx, oneline_str); - } - - strset_clear(&dups); - strbuf_release(&ident); - strbuf_release(&oneline); -} - -static void get_from_rev(struct rev_info *rev, struct shortlog *log) -{ - struct commit *commit; - - if (prepare_revision_walk(rev)) - die(_("revision walk setup failed")); - while ((commit = get_revision(rev)) != NULL) - shortlog_add_commit(log, commit); -} - -static int parse_uint(char const **arg, int comma, int defval) -{ - unsigned long ul; - int ret; - char *endp; - - ul = strtoul(*arg, &endp, 10); - if (*endp && *endp != comma) - return -1; - if (ul > INT_MAX) - return -1; - ret = *arg == endp ? defval : (int)ul; - *arg = *endp ? endp + 1 : endp; - return ret; -} - -static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]"; -#define DEFAULT_WRAPLEN 76 -#define DEFAULT_INDENT1 6 -#define DEFAULT_INDENT2 9 - -static int parse_wrap_args(const struct option *opt, const char *arg, int unset) -{ - struct shortlog *log = opt->value; - - log->wrap_lines = !unset; - if (unset) - return 0; - if (!arg) { - log->wrap = DEFAULT_WRAPLEN; - log->in1 = DEFAULT_INDENT1; - log->in2 = DEFAULT_INDENT2; - return 0; - } - - log->wrap = parse_uint(&arg, ',', DEFAULT_WRAPLEN); - log->in1 = parse_uint(&arg, ',', DEFAULT_INDENT1); - log->in2 = parse_uint(&arg, '\0', DEFAULT_INDENT2); - if (log->wrap < 0 || log->in1 < 0 || log->in2 < 0) - return error(wrap_arg_usage); - if (log->wrap && - ((log->in1 && log->wrap <= log->in1) || - (log->in2 && log->wrap <= log->in2))) - return error(wrap_arg_usage); - return 0; -} - -static int parse_group_option(const struct option *opt, const char *arg, int unset) -{ - struct shortlog *log = opt->value; - const char *field; - - if (unset) { - log->groups = 0; - string_list_clear(&log->trailers, 0); - } else if (!strcasecmp(arg, "author")) - log->groups |= SHORTLOG_GROUP_AUTHOR; - else if (!strcasecmp(arg, "committer")) - log->groups |= SHORTLOG_GROUP_COMMITTER; - else if (skip_prefix(arg, "trailer:", &field)) { - log->groups |= SHORTLOG_GROUP_TRAILER; - string_list_append(&log->trailers, field); - } else - return error(_("unknown group type: %s"), arg); - - return 0; -} - - -void shortlog_init(struct shortlog *log) -{ - memset(log, 0, sizeof(*log)); - - read_mailmap(&log->mailmap, &log->common_repo_prefix); - - log->list.strdup_strings = 1; - log->wrap = DEFAULT_WRAPLEN; - log->in1 = DEFAULT_INDENT1; - log->in2 = DEFAULT_INDENT2; - log->trailers.strdup_strings = 1; - log->trailers.cmp = strcasecmp; -} - -int cmd_shortlog(int argc, const char **argv, const char *prefix) -{ - struct shortlog log = { STRING_LIST_INIT_NODUP }; - struct rev_info rev; - int nongit = !startup_info->have_repository; - - const struct option options[] = { - OPT_BIT('c', "committer", &log.groups, - N_("Group by committer rather than author"), - SHORTLOG_GROUP_COMMITTER), - OPT_BOOL('n', "numbered", &log.sort_by_number, - N_("sort output according to the number of commits per author")), - OPT_BOOL('s', "summary", &log.summary, - N_("Suppress commit descriptions, only provides commit count")), - OPT_BOOL('e', "email", &log.email, - N_("Show the email address of each author")), - OPT_CALLBACK_F('w', NULL, &log, N_("<w>[,<i1>[,<i2>]]"), - N_("Linewrap output"), PARSE_OPT_OPTARG, - &parse_wrap_args), - OPT_CALLBACK(0, "group", &log, N_("field"), - N_("Group by field"), parse_group_option), - OPT_END(), - }; - - struct parse_opt_ctx_t ctx; - - git_config(git_default_config, NULL); - shortlog_init(&log); - repo_init_revisions(the_repository, &rev, prefix); - parse_options_start(&ctx, argc, argv, prefix, options, - PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0); - - for (;;) { - switch (parse_options_step(&ctx, options, shortlog_usage)) { - case PARSE_OPT_HELP: - case PARSE_OPT_ERROR: - exit(129); - case PARSE_OPT_COMPLETE: - exit(0); - case PARSE_OPT_DONE: - goto parse_done; - } - parse_revision_opt(&rev, &ctx, options, shortlog_usage); - } -parse_done: - argc = parse_options_end(&ctx); - - if (nongit && argc > 1) { - error(_("too many arguments given outside repository")); - usage_with_options(shortlog_usage, options); - } - - if (setup_revisions(argc, argv, &rev, NULL) != 1) { - error(_("unrecognized argument: %s"), argv[1]); - usage_with_options(shortlog_usage, options); - } - - log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT; - log.abbrev = rev.abbrev; - log.file = rev.diffopt.file; - - if (!log.groups) - log.groups = SHORTLOG_GROUP_AUTHOR; - string_list_sort(&log.trailers); - - /* assume HEAD if from a tty */ - if (!nongit && !rev.pending.nr && isatty(0)) - add_head_to_pending(&rev); - if (rev.pending.nr == 0) { - if (isatty(0)) - fprintf(stderr, _("(reading log message from standard input)\n")); - read_from_stdin(&log); - } - else - get_from_rev(&rev, &log); - - shortlog_output(&log); - if (log.file != stdout) - fclose(log.file); - return 0; -} - -static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s, - const struct shortlog *log) -{ - strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap); - strbuf_addch(sb, '\n'); -} - -void shortlog_output(struct shortlog *log) -{ - int i, j; - struct strbuf sb = STRBUF_INIT; - - if (log->sort_by_number) - QSORT(log->list.items, log->list.nr, - log->summary ? compare_by_counter : compare_by_list); - for (i = 0; i < log->list.nr; i++) { - const struct string_list_item *item = &log->list.items[i]; - if (log->summary) { - fprintf(log->file, "%6d\t%s\n", - (int)UTIL_TO_INT(item), item->string); - } else { - struct string_list *onelines = item->util; - fprintf(log->file, "%s (%d):\n", - item->string, onelines->nr); - for (j = onelines->nr - 1; j >= 0; j--) { - const char *msg = onelines->items[j].string; - - if (log->wrap_lines) { - strbuf_reset(&sb); - add_wrapped_shortlog_msg(&sb, msg, log); - fwrite(sb.buf, sb.len, 1, log->file); - } - else - fprintf(log->file, " %s\n", msg); - } - putc('\n', log->file); - onelines->strdup_strings = 1; - string_list_clear(onelines, 0); - free(onelines); - } - - log->list.items[i].util = NULL; - } - - strbuf_release(&sb); - log->list.strdup_strings = 1; - string_list_clear(&log->list, 1); - clear_mailmap(&log->mailmap); -} diff --git a/third_party/git/builtin/show-branch.c b/third_party/git/builtin/show-branch.c deleted file mode 100644 index d6d2dabeca87..000000000000 --- a/third_party/git/builtin/show-branch.c +++ /dev/null @@ -1,954 +0,0 @@ -#include "cache.h" -#include "config.h" -#include "pretty.h" -#include "refs.h" -#include "builtin.h" -#include "color.h" -#include "strvec.h" -#include "parse-options.h" -#include "dir.h" -#include "commit-slab.h" - -static const char* show_branch_usage[] = { - N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n" - " [--current] [--color[=<when>] | --no-color] [--sparse]\n" - " [--more=<n> | --list | --independent | --merge-base]\n" - " [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"), - N_("git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"), - NULL -}; - -static int showbranch_use_color = -1; - -static struct strvec default_args = STRVEC_INIT; - -/* - * TODO: convert this use of commit->object.flags to commit-slab - * instead to store a pointer to ref name directly. Then use the same - * UNINTERESTING definition from revision.h here. - */ -#define UNINTERESTING 01 - -#define REV_SHIFT 2 -#define MAX_REVS (FLAG_BITS - REV_SHIFT) /* should not exceed bits_per_int - REV_SHIFT */ - -#define DEFAULT_REFLOG 4 - -static const char *get_color_code(int idx) -{ - if (want_color(showbranch_use_color)) - return column_colors_ansi[idx % column_colors_ansi_max]; - return ""; -} - -static const char *get_color_reset_code(void) -{ - if (want_color(showbranch_use_color)) - return GIT_COLOR_RESET; - return ""; -} - -static struct commit *interesting(struct commit_list *list) -{ - while (list) { - struct commit *commit = list->item; - list = list->next; - if (commit->object.flags & UNINTERESTING) - continue; - return commit; - } - return NULL; -} - -struct commit_name { - const char *head_name; /* which head's ancestor? */ - int generation; /* how many parents away from head_name */ -}; - -define_commit_slab(commit_name_slab, struct commit_name *); -static struct commit_name_slab name_slab; - -static struct commit_name *commit_to_name(struct commit *commit) -{ - return *commit_name_slab_at(&name_slab, commit); -} - - -/* Name the commit as nth generation ancestor of head_name; - * we count only the first-parent relationship for naming purposes. - */ -static void name_commit(struct commit *commit, const char *head_name, int nth) -{ - struct commit_name *name; - - name = *commit_name_slab_at(&name_slab, commit); - if (!name) { - name = xmalloc(sizeof(*name)); - *commit_name_slab_at(&name_slab, commit) = name; - } - name->head_name = head_name; - name->generation = nth; -} - -/* Parent is the first parent of the commit. We may name it - * as (n+1)th generation ancestor of the same head_name as - * commit is nth generation ancestor of, if that generation - * number is better than the name it already has. - */ -static void name_parent(struct commit *commit, struct commit *parent) -{ - struct commit_name *commit_name = commit_to_name(commit); - struct commit_name *parent_name = commit_to_name(parent); - if (!commit_name) - return; - if (!parent_name || - commit_name->generation + 1 < parent_name->generation) - name_commit(parent, commit_name->head_name, - commit_name->generation + 1); -} - -static int name_first_parent_chain(struct commit *c) -{ - int i = 0; - while (c) { - struct commit *p; - if (!commit_to_name(c)) - break; - if (!c->parents) - break; - p = c->parents->item; - if (!commit_to_name(p)) { - name_parent(c, p); - i++; - } - else - break; - c = p; - } - return i; -} - -static void name_commits(struct commit_list *list, - struct commit **rev, - char **ref_name, - int num_rev) -{ - struct commit_list *cl; - struct commit *c; - int i; - - /* First give names to the given heads */ - for (cl = list; cl; cl = cl->next) { - c = cl->item; - if (commit_to_name(c)) - continue; - for (i = 0; i < num_rev; i++) { - if (rev[i] == c) { - name_commit(c, ref_name[i], 0); - break; - } - } - } - - /* Then commits on the first parent ancestry chain */ - do { - i = 0; - for (cl = list; cl; cl = cl->next) { - i += name_first_parent_chain(cl->item); - } - } while (i); - - /* Finally, any unnamed commits */ - do { - i = 0; - for (cl = list; cl; cl = cl->next) { - struct commit_list *parents; - struct commit_name *n; - int nth; - c = cl->item; - if (!commit_to_name(c)) - continue; - n = commit_to_name(c); - parents = c->parents; - nth = 0; - while (parents) { - struct commit *p = parents->item; - struct strbuf newname = STRBUF_INIT; - parents = parents->next; - nth++; - if (commit_to_name(p)) - continue; - switch (n->generation) { - case 0: - strbuf_addstr(&newname, n->head_name); - break; - case 1: - strbuf_addf(&newname, "%s^", n->head_name); - break; - default: - strbuf_addf(&newname, "%s~%d", - n->head_name, n->generation); - break; - } - if (nth == 1) - strbuf_addch(&newname, '^'); - else - strbuf_addf(&newname, "^%d", nth); - name_commit(p, strbuf_detach(&newname, NULL), 0); - i++; - name_first_parent_chain(p); - } - } - } while (i); -} - -static int mark_seen(struct commit *commit, struct commit_list **seen_p) -{ - if (!commit->object.flags) { - commit_list_insert(commit, seen_p); - return 1; - } - return 0; -} - -static void join_revs(struct commit_list **list_p, - struct commit_list **seen_p, - int num_rev, int extra) -{ - int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); - int all_revs = all_mask & ~((1u << REV_SHIFT) - 1); - - while (*list_p) { - struct commit_list *parents; - int still_interesting = !!interesting(*list_p); - struct commit *commit = pop_commit(list_p); - int flags = commit->object.flags & all_mask; - - if (!still_interesting && extra <= 0) - break; - - mark_seen(commit, seen_p); - if ((flags & all_revs) == all_revs) - flags |= UNINTERESTING; - parents = commit->parents; - - while (parents) { - struct commit *p = parents->item; - int this_flag = p->object.flags; - parents = parents->next; - if ((this_flag & flags) == flags) - continue; - parse_commit(p); - if (mark_seen(p, seen_p) && !still_interesting) - extra--; - p->object.flags |= flags; - commit_list_insert_by_date(p, list_p); - } - } - - /* - * Postprocess to complete well-poisoning. - * - * At this point we have all the commits we have seen in - * seen_p list. Mark anything that can be reached from - * uninteresting commits not interesting. - */ - for (;;) { - int changed = 0; - struct commit_list *s; - for (s = *seen_p; s; s = s->next) { - struct commit *c = s->item; - struct commit_list *parents; - - if (((c->object.flags & all_revs) != all_revs) && - !(c->object.flags & UNINTERESTING)) - continue; - - /* The current commit is either a merge base or - * already uninteresting one. Mark its parents - * as uninteresting commits _only_ if they are - * already parsed. No reason to find new ones - * here. - */ - parents = c->parents; - while (parents) { - struct commit *p = parents->item; - parents = parents->next; - if (!(p->object.flags & UNINTERESTING)) { - p->object.flags |= UNINTERESTING; - changed = 1; - } - } - } - if (!changed) - break; - } -} - -static void show_one_commit(struct commit *commit, int no_name) -{ - struct strbuf pretty = STRBUF_INIT; - const char *pretty_str = "(unavailable)"; - struct commit_name *name = commit_to_name(commit); - - if (commit->object.parsed) { - pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty); - pretty_str = pretty.buf; - } - skip_prefix(pretty_str, "[PATCH] ", &pretty_str); - - if (!no_name) { - if (name && name->head_name) { - printf("[%s", name->head_name); - if (name->generation) { - if (name->generation == 1) - printf("^"); - else - printf("~%d", name->generation); - } - printf("] "); - } - else - printf("[%s] ", - find_unique_abbrev(&commit->object.oid, - DEFAULT_ABBREV)); - } - puts(pretty_str); - strbuf_release(&pretty); -} - -static char *ref_name[MAX_REVS + 1]; -static int ref_name_cnt; - -static const char *find_digit_prefix(const char *s, int *v) -{ - const char *p; - int ver; - char ch; - - for (p = s, ver = 0; - '0' <= (ch = *p) && ch <= '9'; - p++) - ver = ver * 10 + ch - '0'; - *v = ver; - return p; -} - - -static int version_cmp(const char *a, const char *b) -{ - while (1) { - int va, vb; - - a = find_digit_prefix(a, &va); - b = find_digit_prefix(b, &vb); - if (va != vb) - return va - vb; - - while (1) { - int ca = *a; - int cb = *b; - if ('0' <= ca && ca <= '9') - ca = 0; - if ('0' <= cb && cb <= '9') - cb = 0; - if (ca != cb) - return ca - cb; - if (!ca) - break; - a++; - b++; - } - if (!*a && !*b) - return 0; - } -} - -static int compare_ref_name(const void *a_, const void *b_) -{ - const char * const*a = a_, * const*b = b_; - return version_cmp(*a, *b); -} - -static void sort_ref_range(int bottom, int top) -{ - QSORT(ref_name + bottom, top - bottom, compare_ref_name); -} - -static int append_ref(const char *refname, const struct object_id *oid, - int allow_dups) -{ - struct commit *commit = lookup_commit_reference_gently(the_repository, - oid, 1); - int i; - - if (!commit) - return 0; - - if (!allow_dups) { - /* Avoid adding the same thing twice */ - for (i = 0; i < ref_name_cnt; i++) - if (!strcmp(refname, ref_name[i])) - return 0; - } - if (MAX_REVS <= ref_name_cnt) { - warning(Q_("ignoring %s; cannot handle more than %d ref", - "ignoring %s; cannot handle more than %d refs", - MAX_REVS), refname, MAX_REVS); - return 0; - } - ref_name[ref_name_cnt++] = xstrdup(refname); - ref_name[ref_name_cnt] = NULL; - return 0; -} - -static int append_head_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) -{ - struct object_id tmp; - int ofs = 11; - if (!starts_with(refname, "refs/heads/")) - return 0; - /* If both heads/foo and tags/foo exists, get_sha1 would - * get confused. - */ - if (get_oid(refname + ofs, &tmp) || !oideq(&tmp, oid)) - ofs = 5; - return append_ref(refname + ofs, oid, 0); -} - -static int append_remote_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) -{ - struct object_id tmp; - int ofs = 13; - if (!starts_with(refname, "refs/remotes/")) - return 0; - /* If both heads/foo and tags/foo exists, get_sha1 would - * get confused. - */ - if (get_oid(refname + ofs, &tmp) || !oideq(&tmp, oid)) - ofs = 5; - return append_ref(refname + ofs, oid, 0); -} - -static int append_tag_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) -{ - if (!starts_with(refname, "refs/tags/")) - return 0; - return append_ref(refname + 5, oid, 0); -} - -static const char *match_ref_pattern = NULL; -static int match_ref_slash = 0; - -static int append_matching_ref(const char *refname, const struct object_id *oid, - int flag, void *cb_data) -{ - /* we want to allow pattern hold/<asterisk> to show all - * branches under refs/heads/hold/, and v0.99.9? to show - * refs/tags/v0.99.9a and friends. - */ - const char *tail; - int slash = count_slashes(refname); - for (tail = refname; *tail && match_ref_slash < slash; ) - if (*tail++ == '/') - slash--; - if (!*tail) - return 0; - if (wildmatch(match_ref_pattern, tail, 0)) - return 0; - if (starts_with(refname, "refs/heads/")) - return append_head_ref(refname, oid, flag, cb_data); - if (starts_with(refname, "refs/tags/")) - return append_tag_ref(refname, oid, flag, cb_data); - return append_ref(refname, oid, 0); -} - -static void snarf_refs(int head, int remotes) -{ - if (head) { - int orig_cnt = ref_name_cnt; - - for_each_ref(append_head_ref, NULL); - sort_ref_range(orig_cnt, ref_name_cnt); - } - if (remotes) { - int orig_cnt = ref_name_cnt; - - for_each_ref(append_remote_ref, NULL); - sort_ref_range(orig_cnt, ref_name_cnt); - } -} - -static int rev_is_head(const char *head, const char *name, - unsigned char *head_sha1, unsigned char *sha1) -{ - if (!head || (head_sha1 && sha1 && !hasheq(head_sha1, sha1))) - return 0; - skip_prefix(head, "refs/heads/", &head); - if (!skip_prefix(name, "refs/heads/", &name)) - skip_prefix(name, "heads/", &name); - return !strcmp(head, name); -} - -static int show_merge_base(struct commit_list *seen, int num_rev) -{ - int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); - int all_revs = all_mask & ~((1u << REV_SHIFT) - 1); - int exit_status = 1; - - while (seen) { - struct commit *commit = pop_commit(&seen); - int flags = commit->object.flags & all_mask; - if (!(flags & UNINTERESTING) && - ((flags & all_revs) == all_revs)) { - puts(oid_to_hex(&commit->object.oid)); - exit_status = 0; - commit->object.flags |= UNINTERESTING; - } - } - return exit_status; -} - -static int show_independent(struct commit **rev, - int num_rev, - unsigned int *rev_mask) -{ - int i; - - for (i = 0; i < num_rev; i++) { - struct commit *commit = rev[i]; - unsigned int flag = rev_mask[i]; - - if (commit->object.flags == flag) - puts(oid_to_hex(&commit->object.oid)); - commit->object.flags |= UNINTERESTING; - } - return 0; -} - -static void append_one_rev(const char *av) -{ - struct object_id revkey; - if (!get_oid(av, &revkey)) { - append_ref(av, &revkey, 0); - return; - } - if (strpbrk(av, "*?[")) { - /* glob style match */ - int saved_matches = ref_name_cnt; - - match_ref_pattern = av; - match_ref_slash = count_slashes(av); - for_each_ref(append_matching_ref, NULL); - if (saved_matches == ref_name_cnt && - ref_name_cnt < MAX_REVS) - error(_("no matching refs with %s"), av); - sort_ref_range(saved_matches, ref_name_cnt); - return; - } - die("bad sha1 reference %s", av); -} - -static int git_show_branch_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "showbranch.default")) { - if (!value) - return config_error_nonbool(var); - /* - * default_arg is now passed to parse_options(), so we need to - * mimic the real argv a bit better. - */ - if (!default_args.nr) - strvec_push(&default_args, "show-branch"); - strvec_push(&default_args, value); - return 0; - } - - if (!strcmp(var, "color.showbranch")) { - showbranch_use_color = git_config_colorbool(var, value); - return 0; - } - - return git_color_default_config(var, value, cb); -} - -static int omit_in_dense(struct commit *commit, struct commit **rev, int n) -{ - /* If the commit is tip of the named branches, do not - * omit it. - * Otherwise, if it is a merge that is reachable from only one - * tip, it is not that interesting. - */ - int i, flag, count; - for (i = 0; i < n; i++) - if (rev[i] == commit) - return 0; - flag = commit->object.flags; - for (i = count = 0; i < n; i++) { - if (flag & (1u << (i + REV_SHIFT))) - count++; - } - if (count == 1) - return 1; - return 0; -} - -static int reflog = 0; - -static int parse_reflog_param(const struct option *opt, const char *arg, - int unset) -{ - char *ep; - const char **base = (const char **)opt->value; - BUG_ON_OPT_NEG(unset); - if (!arg) - arg = ""; - reflog = strtoul(arg, &ep, 10); - if (*ep == ',') - *base = ep + 1; - else if (*ep) - return error("unrecognized reflog param '%s'", arg); - else - *base = NULL; - if (reflog <= 0) - reflog = DEFAULT_REFLOG; - return 0; -} - -int cmd_show_branch(int ac, const char **av, const char *prefix) -{ - struct commit *rev[MAX_REVS], *commit; - char *reflog_msg[MAX_REVS]; - struct commit_list *list = NULL, *seen = NULL; - unsigned int rev_mask[MAX_REVS]; - int num_rev, i, extra = 0; - int all_heads = 0, all_remotes = 0; - int all_mask, all_revs; - enum rev_sort_order sort_order = REV_SORT_IN_GRAPH_ORDER; - char *head; - struct object_id head_oid; - int merge_base = 0; - int independent = 0; - int no_name = 0; - int sha1_name = 0; - int shown_merge_point = 0; - int with_current_branch = 0; - int head_at = -1; - int topics = 0; - int dense = 1; - const char *reflog_base = NULL; - struct option builtin_show_branch_options[] = { - OPT_BOOL('a', "all", &all_heads, - N_("show remote-tracking and local branches")), - OPT_BOOL('r', "remotes", &all_remotes, - N_("show remote-tracking branches")), - OPT__COLOR(&showbranch_use_color, - N_("color '*!+-' corresponding to the branch")), - { OPTION_INTEGER, 0, "more", &extra, N_("n"), - N_("show <n> more commits after the common ancestor"), - PARSE_OPT_OPTARG, NULL, (intptr_t)1 }, - OPT_SET_INT(0, "list", &extra, N_("synonym to more=-1"), -1), - OPT_BOOL(0, "no-name", &no_name, N_("suppress naming strings")), - OPT_BOOL(0, "current", &with_current_branch, - N_("include the current branch")), - OPT_BOOL(0, "sha1-name", &sha1_name, - N_("name commits with their object names")), - OPT_BOOL(0, "merge-base", &merge_base, - N_("show possible merge bases")), - OPT_BOOL(0, "independent", &independent, - N_("show refs unreachable from any other ref")), - OPT_SET_INT(0, "topo-order", &sort_order, - N_("show commits in topological order"), - REV_SORT_IN_GRAPH_ORDER), - OPT_BOOL(0, "topics", &topics, - N_("show only commits not on the first branch")), - OPT_SET_INT(0, "sparse", &dense, - N_("show merges reachable from only one tip"), 0), - OPT_SET_INT(0, "date-order", &sort_order, - N_("topologically sort, maintaining date order " - "where possible"), - REV_SORT_BY_COMMIT_DATE), - OPT_CALLBACK_F('g', "reflog", &reflog_base, N_("<n>[,<base>]"), - N_("show <n> most recent ref-log entries starting at " - "base"), - PARSE_OPT_OPTARG | PARSE_OPT_NONEG, - parse_reflog_param), - OPT_END() - }; - - init_commit_name_slab(&name_slab); - - git_config(git_show_branch_config, NULL); - - /* If nothing is specified, try the default first */ - if (ac == 1 && default_args.nr) { - ac = default_args.nr; - av = default_args.v; - } - - ac = parse_options(ac, av, prefix, builtin_show_branch_options, - show_branch_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (all_heads) - all_remotes = 1; - - if (extra || reflog) { - /* "listing" mode is incompatible with - * independent nor merge-base modes. - */ - if (independent || merge_base) - usage_with_options(show_branch_usage, - builtin_show_branch_options); - if (reflog && ((0 < extra) || all_heads || all_remotes)) - /* - * Asking for --more in reflog mode does not - * make sense. --list is Ok. - * - * Also --all and --remotes do not make sense either. - */ - die(_("--reflog is incompatible with --all, --remotes, " - "--independent or --merge-base")); - } - - /* If nothing is specified, show all branches by default */ - if (ac <= topics && all_heads + all_remotes == 0) - all_heads = 1; - - if (reflog) { - struct object_id oid; - char *ref; - int base = 0; - unsigned int flags = 0; - - if (ac == 0) { - static const char *fake_av[2]; - - fake_av[0] = resolve_refdup("HEAD", - RESOLVE_REF_READING, &oid, - NULL); - fake_av[1] = NULL; - av = fake_av; - ac = 1; - if (!*av) - die(_("no branches given, and HEAD is not valid")); - } - if (ac != 1) - die(_("--reflog option needs one branch name")); - - if (MAX_REVS < reflog) - die(Q_("only %d entry can be shown at one time.", - "only %d entries can be shown at one time.", - MAX_REVS), MAX_REVS); - if (!dwim_ref(*av, strlen(*av), &oid, &ref, 0)) - die(_("no such ref %s"), *av); - - /* Has the base been specified? */ - if (reflog_base) { - char *ep; - base = strtoul(reflog_base, &ep, 10); - if (*ep) { - /* Ah, that is a date spec... */ - timestamp_t at; - at = approxidate(reflog_base); - read_ref_at(get_main_ref_store(the_repository), - ref, flags, at, -1, &oid, NULL, - NULL, NULL, &base); - } - } - - for (i = 0; i < reflog; i++) { - char *logmsg; - char *nth_desc; - const char *msg; - timestamp_t timestamp; - int tz; - - if (read_ref_at(get_main_ref_store(the_repository), - ref, flags, 0, base + i, &oid, &logmsg, - ×tamp, &tz, NULL)) { - reflog = i; - break; - } - msg = strchr(logmsg, '\t'); - if (!msg) - msg = "(none)"; - else - msg++; - reflog_msg[i] = xstrfmt("(%s) %s", - show_date(timestamp, tz, - DATE_MODE(RELATIVE)), - msg); - free(logmsg); - - nth_desc = xstrfmt("%s@{%d}", *av, base+i); - append_ref(nth_desc, &oid, 1); - free(nth_desc); - } - free(ref); - } - else { - while (0 < ac) { - append_one_rev(*av); - ac--; av++; - } - if (all_heads + all_remotes) - snarf_refs(all_heads, all_remotes); - } - - head = resolve_refdup("HEAD", RESOLVE_REF_READING, - &head_oid, NULL); - - if (with_current_branch && head) { - int has_head = 0; - for (i = 0; !has_head && i < ref_name_cnt; i++) { - /* We are only interested in adding the branch - * HEAD points at. - */ - if (rev_is_head(head, - ref_name[i], - head_oid.hash, NULL)) - has_head++; - } - if (!has_head) { - const char *name = head; - skip_prefix(name, "refs/heads/", &name); - append_one_rev(name); - } - } - - if (!ref_name_cnt) { - fprintf(stderr, "No revs to be shown.\n"); - exit(0); - } - - for (num_rev = 0; ref_name[num_rev]; num_rev++) { - struct object_id revkey; - unsigned int flag = 1u << (num_rev + REV_SHIFT); - - if (MAX_REVS <= num_rev) - die(Q_("cannot handle more than %d rev.", - "cannot handle more than %d revs.", - MAX_REVS), MAX_REVS); - if (get_oid(ref_name[num_rev], &revkey)) - die(_("'%s' is not a valid ref."), ref_name[num_rev]); - commit = lookup_commit_reference(the_repository, &revkey); - if (!commit) - die(_("cannot find commit %s (%s)"), - ref_name[num_rev], oid_to_hex(&revkey)); - parse_commit(commit); - mark_seen(commit, &seen); - - /* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1, - * and so on. REV_SHIFT bits from bit 0 are used for - * internal bookkeeping. - */ - commit->object.flags |= flag; - if (commit->object.flags == flag) - commit_list_insert_by_date(commit, &list); - rev[num_rev] = commit; - } - for (i = 0; i < num_rev; i++) - rev_mask[i] = rev[i]->object.flags; - - if (0 <= extra) - join_revs(&list, &seen, num_rev, extra); - - commit_list_sort_by_date(&seen); - - if (merge_base) - return show_merge_base(seen, num_rev); - - if (independent) - return show_independent(rev, num_rev, rev_mask); - - /* Show list; --more=-1 means list-only */ - if (1 < num_rev || extra < 0) { - for (i = 0; i < num_rev; i++) { - int j; - int is_head = rev_is_head(head, - ref_name[i], - head_oid.hash, - rev[i]->object.oid.hash); - if (extra < 0) - printf("%c [%s] ", - is_head ? '*' : ' ', ref_name[i]); - else { - for (j = 0; j < i; j++) - putchar(' '); - printf("%s%c%s [%s] ", - get_color_code(i), - is_head ? '*' : '!', - get_color_reset_code(), ref_name[i]); - } - - if (!reflog) { - /* header lines never need name */ - show_one_commit(rev[i], 1); - } - else - puts(reflog_msg[i]); - - if (is_head) - head_at = i; - } - if (0 <= extra) { - for (i = 0; i < num_rev; i++) - putchar('-'); - putchar('\n'); - } - } - if (extra < 0) - exit(0); - - /* Sort topologically */ - sort_in_topological_order(&seen, sort_order); - - /* Give names to commits */ - if (!sha1_name && !no_name) - name_commits(seen, rev, ref_name, num_rev); - - all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); - all_revs = all_mask & ~((1u << REV_SHIFT) - 1); - - while (seen) { - struct commit *commit = pop_commit(&seen); - int this_flag = commit->object.flags; - int is_merge_point = ((this_flag & all_revs) == all_revs); - - shown_merge_point |= is_merge_point; - - if (1 < num_rev) { - int is_merge = !!(commit->parents && - commit->parents->next); - if (topics && - !is_merge_point && - (this_flag & (1u << REV_SHIFT))) - continue; - if (dense && is_merge && - omit_in_dense(commit, rev, num_rev)) - continue; - for (i = 0; i < num_rev; i++) { - int mark; - if (!(this_flag & (1u << (i + REV_SHIFT)))) - mark = ' '; - else if (is_merge) - mark = '-'; - else if (i == head_at) - mark = '*'; - else - mark = '+'; - printf("%s%c%s", - get_color_code(i), - mark, get_color_reset_code()); - } - putchar(' '); - } - show_one_commit(commit, no_name); - - if (shown_merge_point && --extra < 0) - break; - } - return 0; -} diff --git a/third_party/git/builtin/show-index.c b/third_party/git/builtin/show-index.c deleted file mode 100644 index 8106b03a6b32..000000000000 --- a/third_party/git/builtin/show-index.c +++ /dev/null @@ -1,106 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "pack.h" -#include "parse-options.h" - -static const char *const show_index_usage[] = { - "git show-index [--object-format=<hash-algorithm>]", - NULL -}; - -int cmd_show_index(int argc, const char **argv, const char *prefix) -{ - int i; - unsigned nr; - unsigned int version; - static unsigned int top_index[256]; - unsigned hashsz; - const char *hash_name = NULL; - int hash_algo; - const struct option show_index_options[] = { - OPT_STRING(0, "object-format", &hash_name, N_("hash-algorithm"), - N_("specify the hash algorithm to use")), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, show_index_options, show_index_usage, 0); - - if (hash_name) { - hash_algo = hash_algo_by_name(hash_name); - if (hash_algo == GIT_HASH_UNKNOWN) - die(_("Unknown hash algorithm")); - repo_set_hash_algo(the_repository, hash_algo); - } - - hashsz = the_hash_algo->rawsz; - - if (fread(top_index, 2 * 4, 1, stdin) != 1) - die("unable to read header"); - if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) { - version = ntohl(top_index[1]); - if (version < 2 || version > 2) - die("unknown index version"); - if (fread(top_index, 256 * 4, 1, stdin) != 1) - die("unable to read index"); - } else { - version = 1; - if (fread(&top_index[2], 254 * 4, 1, stdin) != 1) - die("unable to read index"); - } - nr = 0; - for (i = 0; i < 256; i++) { - unsigned n = ntohl(top_index[i]); - if (n < nr) - die("corrupt index file"); - nr = n; - } - if (version == 1) { - for (i = 0; i < nr; i++) { - unsigned int offset, entry[(GIT_MAX_RAWSZ + 4) / sizeof(unsigned int)]; - - if (fread(entry, 4 + hashsz, 1, stdin) != 1) - die("unable to read entry %u/%u", i, nr); - offset = ntohl(entry[0]); - printf("%u %s\n", offset, hash_to_hex((void *)(entry+1))); - } - } else { - unsigned off64_nr = 0; - struct { - struct object_id oid; - uint32_t crc; - uint32_t off; - } *entries; - ALLOC_ARRAY(entries, nr); - for (i = 0; i < nr; i++) - if (fread(entries[i].oid.hash, hashsz, 1, stdin) != 1) - die("unable to read sha1 %u/%u", i, nr); - for (i = 0; i < nr; i++) - if (fread(&entries[i].crc, 4, 1, stdin) != 1) - die("unable to read crc %u/%u", i, nr); - for (i = 0; i < nr; i++) - if (fread(&entries[i].off, 4, 1, stdin) != 1) - die("unable to read 32b offset %u/%u", i, nr); - for (i = 0; i < nr; i++) { - uint64_t offset; - uint32_t off = ntohl(entries[i].off); - if (!(off & 0x80000000)) { - offset = off; - } else { - uint32_t off64[2]; - if ((off & 0x7fffffff) != off64_nr) - die("inconsistent 64b offset index"); - if (fread(off64, 8, 1, stdin) != 1) - die("unable to read 64b offset %u", off64_nr); - offset = (((uint64_t)ntohl(off64[0])) << 32) | - ntohl(off64[1]); - off64_nr++; - } - printf("%" PRIuMAX " %s (%08"PRIx32")\n", - (uintmax_t) offset, - oid_to_hex(&entries[i].oid), - ntohl(entries[i].crc)); - } - free(entries); - } - return 0; -} diff --git a/third_party/git/builtin/show-ref.c b/third_party/git/builtin/show-ref.c deleted file mode 100644 index ae60b4acf2f4..000000000000 --- a/third_party/git/builtin/show-ref.c +++ /dev/null @@ -1,226 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "refs.h" -#include "object-store.h" -#include "object.h" -#include "tag.h" -#include "string-list.h" -#include "parse-options.h" - -static const char * const show_ref_usage[] = { - N_("git show-ref [-q | --quiet] [--verify] [--head] [-d | --dereference] [-s | --hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [<pattern>...]"), - N_("git show-ref --exclude-existing[=<pattern>]"), - NULL -}; - -static int deref_tags, show_head, tags_only, heads_only, found_match, verify, - quiet, hash_only, abbrev, exclude_arg; -static const char **pattern; -static const char *exclude_existing_arg; - -static void show_one(const char *refname, const struct object_id *oid) -{ - const char *hex; - struct object_id peeled; - - if (!has_object_file(oid)) - die("git show-ref: bad ref %s (%s)", refname, - oid_to_hex(oid)); - - if (quiet) - return; - - hex = find_unique_abbrev(oid, abbrev); - if (hash_only) - printf("%s\n", hex); - else - printf("%s %s\n", hex, refname); - - if (!deref_tags) - return; - - if (!peel_ref(refname, &peeled)) { - hex = find_unique_abbrev(&peeled, abbrev); - printf("%s %s^{}\n", hex, refname); - } -} - -static int show_ref(const char *refname, const struct object_id *oid, - int flag, void *cbdata) -{ - if (show_head && !strcmp(refname, "HEAD")) - goto match; - - if (tags_only || heads_only) { - int match; - - match = heads_only && starts_with(refname, "refs/heads/"); - match |= tags_only && starts_with(refname, "refs/tags/"); - if (!match) - return 0; - } - if (pattern) { - int reflen = strlen(refname); - const char **p = pattern, *m; - while ((m = *p++) != NULL) { - int len = strlen(m); - if (len > reflen) - continue; - if (memcmp(m, refname + reflen - len, len)) - continue; - if (len == reflen) - goto match; - if (refname[reflen - len - 1] == '/') - goto match; - } - return 0; - } - -match: - found_match++; - - show_one(refname, oid); - - return 0; -} - -static int add_existing(const char *refname, const struct object_id *oid, - int flag, void *cbdata) -{ - struct string_list *list = (struct string_list *)cbdata; - string_list_insert(list, refname); - return 0; -} - -/* - * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input, - * and - * (1) strip "^{}" at the end of line if any; - * (2) ignore if match is provided and does not head-match refname; - * (3) warn if refname is not a well-formed refname and skip; - * (4) ignore if refname is a ref that exists in the local repository; - * (5) otherwise output the line. - */ -static int exclude_existing(const char *match) -{ - static struct string_list existing_refs = STRING_LIST_INIT_DUP; - char buf[1024]; - int matchlen = match ? strlen(match) : 0; - - for_each_ref(add_existing, &existing_refs); - while (fgets(buf, sizeof(buf), stdin)) { - char *ref; - int len = strlen(buf); - - if (len > 0 && buf[len - 1] == '\n') - buf[--len] = '\0'; - if (3 <= len && !strcmp(buf + len - 3, "^{}")) { - len -= 3; - buf[len] = '\0'; - } - for (ref = buf + len; buf < ref; ref--) - if (isspace(ref[-1])) - break; - if (match) { - int reflen = buf + len - ref; - if (reflen < matchlen) - continue; - if (strncmp(ref, match, matchlen)) - continue; - } - if (check_refname_format(ref, 0)) { - warning("ref '%s' ignored", ref); - continue; - } - if (!string_list_has_string(&existing_refs, ref)) { - printf("%s\n", buf); - } - } - return 0; -} - -static int hash_callback(const struct option *opt, const char *arg, int unset) -{ - hash_only = 1; - /* Use full length SHA1 if no argument */ - if (!arg) - return 0; - return parse_opt_abbrev_cb(opt, arg, unset); -} - -static int exclude_existing_callback(const struct option *opt, const char *arg, - int unset) -{ - BUG_ON_OPT_NEG(unset); - exclude_arg = 1; - *(const char **)opt->value = arg; - return 0; -} - -static const struct option show_ref_options[] = { - OPT_BOOL(0, "tags", &tags_only, N_("only show tags (can be combined with heads)")), - OPT_BOOL(0, "heads", &heads_only, N_("only show heads (can be combined with tags)")), - OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, " - "requires exact ref path")), - OPT_HIDDEN_BOOL('h', NULL, &show_head, - N_("show the HEAD reference, even if it would be filtered out")), - OPT_BOOL(0, "head", &show_head, - N_("show the HEAD reference, even if it would be filtered out")), - OPT_BOOL('d', "dereference", &deref_tags, - N_("dereference tags into object IDs")), - OPT_CALLBACK_F('s', "hash", &abbrev, N_("n"), - N_("only show SHA1 hash using <n> digits"), - PARSE_OPT_OPTARG, &hash_callback), - OPT__ABBREV(&abbrev), - OPT__QUIET(&quiet, - N_("do not print results to stdout (useful with --verify)")), - OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_arg, - N_("pattern"), N_("show refs from stdin that aren't in local repository"), - PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback), - OPT_END() -}; - -int cmd_show_ref(int argc, const char **argv, const char *prefix) -{ - git_config(git_default_config, NULL); - - argc = parse_options(argc, argv, prefix, show_ref_options, - show_ref_usage, 0); - - if (exclude_arg) - return exclude_existing(exclude_existing_arg); - - pattern = argv; - if (!*pattern) - pattern = NULL; - - if (verify) { - if (!pattern) - die("--verify requires a reference"); - while (*pattern) { - struct object_id oid; - - if ((starts_with(*pattern, "refs/") || !strcmp(*pattern, "HEAD")) && - !read_ref(*pattern, &oid)) { - show_one(*pattern, &oid); - } - else if (!quiet) - die("'%s' - not a valid ref", *pattern); - else - return 1; - pattern++; - } - return 0; - } - - if (show_head) - head_ref(show_ref, NULL); - for_each_ref(show_ref, NULL); - if (!found_match) { - if (verify && !quiet) - die("No match"); - return 1; - } - return 0; -} diff --git a/third_party/git/builtin/sparse-checkout.c b/third_party/git/builtin/sparse-checkout.c deleted file mode 100644 index e3140db2a0a6..000000000000 --- a/third_party/git/builtin/sparse-checkout.c +++ /dev/null @@ -1,663 +0,0 @@ -#include "builtin.h" -#include "config.h" -#include "dir.h" -#include "parse-options.h" -#include "pathspec.h" -#include "repository.h" -#include "run-command.h" -#include "strbuf.h" -#include "string-list.h" -#include "cache.h" -#include "cache-tree.h" -#include "lockfile.h" -#include "resolve-undo.h" -#include "unpack-trees.h" -#include "wt-status.h" -#include "quote.h" - -static const char *empty_base = ""; - -static char const * const builtin_sparse_checkout_usage[] = { - N_("git sparse-checkout (init|list|set|add|reapply|disable) <options>"), - NULL -}; - -static char *get_sparse_checkout_filename(void) -{ - return git_pathdup("info/sparse-checkout"); -} - -static void write_patterns_to_file(FILE *fp, struct pattern_list *pl) -{ - int i; - - for (i = 0; i < pl->nr; i++) { - struct path_pattern *p = pl->patterns[i]; - - if (p->flags & PATTERN_FLAG_NEGATIVE) - fprintf(fp, "!"); - - fprintf(fp, "%s", p->pattern); - - if (p->flags & PATTERN_FLAG_MUSTBEDIR) - fprintf(fp, "/"); - - fprintf(fp, "\n"); - } -} - -static char const * const builtin_sparse_checkout_list_usage[] = { - N_("git sparse-checkout list"), - NULL -}; - -static int sparse_checkout_list(int argc, const char **argv) -{ - static struct option builtin_sparse_checkout_list_options[] = { - OPT_END(), - }; - struct pattern_list pl; - char *sparse_filename; - int res; - - argc = parse_options(argc, argv, NULL, - builtin_sparse_checkout_list_options, - builtin_sparse_checkout_list_usage, 0); - - memset(&pl, 0, sizeof(pl)); - - pl.use_cone_patterns = core_sparse_checkout_cone; - - sparse_filename = get_sparse_checkout_filename(); - res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL); - free(sparse_filename); - - if (res < 0) { - warning(_("this worktree is not sparse (sparse-checkout file may not exist)")); - return 0; - } - - if (pl.use_cone_patterns) { - int i; - struct pattern_entry *pe; - struct hashmap_iter iter; - struct string_list sl = STRING_LIST_INIT_DUP; - - hashmap_for_each_entry(&pl.recursive_hashmap, &iter, pe, ent) { - /* pe->pattern starts with "/", skip it */ - string_list_insert(&sl, pe->pattern + 1); - } - - string_list_sort(&sl); - - for (i = 0; i < sl.nr; i++) { - quote_c_style(sl.items[i].string, NULL, stdout, 0); - printf("\n"); - } - - return 0; - } - - write_patterns_to_file(stdout, &pl); - clear_pattern_list(&pl); - - return 0; -} - -static int update_working_directory(struct pattern_list *pl) -{ - enum update_sparsity_result result; - struct unpack_trees_options o; - struct lock_file lock_file = LOCK_INIT; - struct repository *r = the_repository; - - /* If no branch has been checked out, there are no updates to make. */ - if (is_index_unborn(r->index)) - return UPDATE_SPARSITY_SUCCESS; - - memset(&o, 0, sizeof(o)); - o.verbose_update = isatty(2); - o.update = 1; - o.head_idx = -1; - o.src_index = r->index; - o.dst_index = r->index; - o.skip_sparse_checkout = 0; - o.pl = pl; - - setup_work_tree(); - - repo_hold_locked_index(r, &lock_file, LOCK_DIE_ON_ERROR); - - setup_unpack_trees_porcelain(&o, "sparse-checkout"); - result = update_sparsity(&o); - clear_unpack_trees_porcelain(&o); - - if (result == UPDATE_SPARSITY_WARNINGS) - /* - * We don't do any special handling of warnings from untracked - * files in the way or dirty entries that can't be removed. - */ - result = UPDATE_SPARSITY_SUCCESS; - if (result == UPDATE_SPARSITY_SUCCESS) - write_locked_index(r->index, &lock_file, COMMIT_LOCK); - else - rollback_lock_file(&lock_file); - - return result; -} - -static char *escaped_pattern(char *pattern) -{ - char *p = pattern; - struct strbuf final = STRBUF_INIT; - - while (*p) { - if (is_glob_special(*p)) - strbuf_addch(&final, '\\'); - - strbuf_addch(&final, *p); - p++; - } - - return strbuf_detach(&final, NULL); -} - -static void write_cone_to_file(FILE *fp, struct pattern_list *pl) -{ - int i; - struct pattern_entry *pe; - struct hashmap_iter iter; - struct string_list sl = STRING_LIST_INIT_DUP; - struct strbuf parent_pattern = STRBUF_INIT; - - hashmap_for_each_entry(&pl->parent_hashmap, &iter, pe, ent) { - if (hashmap_get_entry(&pl->recursive_hashmap, pe, ent, NULL)) - continue; - - if (!hashmap_contains_parent(&pl->recursive_hashmap, - pe->pattern, - &parent_pattern)) - string_list_insert(&sl, pe->pattern); - } - - string_list_sort(&sl); - string_list_remove_duplicates(&sl, 0); - - fprintf(fp, "/*\n!/*/\n"); - - for (i = 0; i < sl.nr; i++) { - char *pattern = escaped_pattern(sl.items[i].string); - - if (strlen(pattern)) - fprintf(fp, "%s/\n!%s/*/\n", pattern, pattern); - free(pattern); - } - - string_list_clear(&sl, 0); - - hashmap_for_each_entry(&pl->recursive_hashmap, &iter, pe, ent) { - if (!hashmap_contains_parent(&pl->recursive_hashmap, - pe->pattern, - &parent_pattern)) - string_list_insert(&sl, pe->pattern); - } - - strbuf_release(&parent_pattern); - - string_list_sort(&sl); - string_list_remove_duplicates(&sl, 0); - - for (i = 0; i < sl.nr; i++) { - char *pattern = escaped_pattern(sl.items[i].string); - fprintf(fp, "%s/\n", pattern); - free(pattern); - } -} - -static int write_patterns_and_update(struct pattern_list *pl) -{ - char *sparse_filename; - FILE *fp; - int fd; - struct lock_file lk = LOCK_INIT; - int result; - - sparse_filename = get_sparse_checkout_filename(); - - if (safe_create_leading_directories(sparse_filename)) - die(_("failed to create directory for sparse-checkout file")); - - fd = hold_lock_file_for_update(&lk, sparse_filename, - LOCK_DIE_ON_ERROR); - - result = update_working_directory(pl); - if (result) { - rollback_lock_file(&lk); - free(sparse_filename); - clear_pattern_list(pl); - update_working_directory(NULL); - return result; - } - - fp = xfdopen(fd, "w"); - - if (core_sparse_checkout_cone) - write_cone_to_file(fp, pl); - else - write_patterns_to_file(fp, pl); - - fflush(fp); - commit_lock_file(&lk); - - free(sparse_filename); - clear_pattern_list(pl); - - return 0; -} - -enum sparse_checkout_mode { - MODE_NO_PATTERNS = 0, - MODE_ALL_PATTERNS = 1, - MODE_CONE_PATTERNS = 2, -}; - -static int set_config(enum sparse_checkout_mode mode) -{ - const char *config_path; - - if (upgrade_repository_format(1) < 0) - die(_("unable to upgrade repository format to enable worktreeConfig")); - if (git_config_set_gently("extensions.worktreeConfig", "true")) { - error(_("failed to set extensions.worktreeConfig setting")); - return 1; - } - - config_path = git_path("config.worktree"); - git_config_set_in_file_gently(config_path, - "core.sparseCheckout", - mode ? "true" : NULL); - - git_config_set_in_file_gently(config_path, - "core.sparseCheckoutCone", - mode == MODE_CONE_PATTERNS ? "true" : NULL); - - return 0; -} - -static char const * const builtin_sparse_checkout_init_usage[] = { - N_("git sparse-checkout init [--cone]"), - NULL -}; - -static struct sparse_checkout_init_opts { - int cone_mode; -} init_opts; - -static int sparse_checkout_init(int argc, const char **argv) -{ - struct pattern_list pl; - char *sparse_filename; - int res; - struct object_id oid; - int mode; - struct strbuf pattern = STRBUF_INIT; - - static struct option builtin_sparse_checkout_init_options[] = { - OPT_BOOL(0, "cone", &init_opts.cone_mode, - N_("initialize the sparse-checkout in cone mode")), - OPT_END(), - }; - - repo_read_index(the_repository); - - argc = parse_options(argc, argv, NULL, - builtin_sparse_checkout_init_options, - builtin_sparse_checkout_init_usage, 0); - - if (init_opts.cone_mode) { - mode = MODE_CONE_PATTERNS; - core_sparse_checkout_cone = 1; - } else - mode = MODE_ALL_PATTERNS; - - if (set_config(mode)) - return 1; - - memset(&pl, 0, sizeof(pl)); - - sparse_filename = get_sparse_checkout_filename(); - res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL); - - /* If we already have a sparse-checkout file, use it. */ - if (res >= 0) { - free(sparse_filename); - core_apply_sparse_checkout = 1; - return update_working_directory(NULL); - } - - if (get_oid("HEAD", &oid)) { - FILE *fp; - - /* assume we are in a fresh repo, but update the sparse-checkout file */ - fp = xfopen(sparse_filename, "w"); - if (!fp) - die(_("failed to open '%s'"), sparse_filename); - - free(sparse_filename); - fprintf(fp, "/*\n!/*/\n"); - fclose(fp); - return 0; - } - - strbuf_addstr(&pattern, "/*"); - add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0); - strbuf_addstr(&pattern, "!/*/"); - add_pattern(strbuf_detach(&pattern, NULL), empty_base, 0, &pl, 0); - - return write_patterns_and_update(&pl); -} - -static void insert_recursive_pattern(struct pattern_list *pl, struct strbuf *path) -{ - struct pattern_entry *e = xmalloc(sizeof(*e)); - e->patternlen = path->len; - e->pattern = strbuf_detach(path, NULL); - hashmap_entry_init(&e->ent, - ignore_case ? - strihash(e->pattern) : - strhash(e->pattern)); - - hashmap_add(&pl->recursive_hashmap, &e->ent); - - while (e->patternlen) { - char *slash = strrchr(e->pattern, '/'); - char *oldpattern = e->pattern; - size_t newlen; - - if (slash == e->pattern) - break; - - newlen = slash - e->pattern; - e = xmalloc(sizeof(struct pattern_entry)); - e->patternlen = newlen; - e->pattern = xstrndup(oldpattern, newlen); - hashmap_entry_init(&e->ent, - ignore_case ? - strihash(e->pattern) : - strhash(e->pattern)); - - if (!hashmap_get_entry(&pl->parent_hashmap, e, ent, NULL)) - hashmap_add(&pl->parent_hashmap, &e->ent); - } -} - -static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl) -{ - strbuf_trim(line); - - strbuf_trim_trailing_dir_sep(line); - - if (strbuf_normalize_path(line)) - die(_("could not normalize path %s"), line->buf); - - if (!line->len) - return; - - if (line->buf[0] != '/') - strbuf_insertstr(line, 0, "/"); - - insert_recursive_pattern(pl, line); -} - -static char const * const builtin_sparse_checkout_set_usage[] = { - N_("git sparse-checkout (set|add) (--stdin | <patterns>)"), - NULL -}; - -static struct sparse_checkout_set_opts { - int use_stdin; -} set_opts; - -static void add_patterns_from_input(struct pattern_list *pl, - int argc, const char **argv) -{ - int i; - if (core_sparse_checkout_cone) { - struct strbuf line = STRBUF_INIT; - - hashmap_init(&pl->recursive_hashmap, pl_hashmap_cmp, NULL, 0); - hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0); - pl->use_cone_patterns = 1; - - if (set_opts.use_stdin) { - struct strbuf unquoted = STRBUF_INIT; - while (!strbuf_getline(&line, stdin)) { - if (line.buf[0] == '"') { - strbuf_reset(&unquoted); - if (unquote_c_style(&unquoted, line.buf, NULL)) - die(_("unable to unquote C-style string '%s'"), - line.buf); - - strbuf_swap(&unquoted, &line); - } - - strbuf_to_cone_pattern(&line, pl); - } - - strbuf_release(&unquoted); - } else { - for (i = 0; i < argc; i++) { - strbuf_setlen(&line, 0); - strbuf_addstr(&line, argv[i]); - strbuf_to_cone_pattern(&line, pl); - } - } - } else { - if (set_opts.use_stdin) { - struct strbuf line = STRBUF_INIT; - - while (!strbuf_getline(&line, stdin)) { - size_t len; - char *buf = strbuf_detach(&line, &len); - add_pattern(buf, empty_base, 0, pl, 0); - } - } else { - for (i = 0; i < argc; i++) - add_pattern(argv[i], empty_base, 0, pl, 0); - } - } -} - -enum modify_type { - REPLACE, - ADD, -}; - -static void add_patterns_cone_mode(int argc, const char **argv, - struct pattern_list *pl) -{ - struct strbuf buffer = STRBUF_INIT; - struct pattern_entry *pe; - struct hashmap_iter iter; - struct pattern_list existing; - char *sparse_filename = get_sparse_checkout_filename(); - - add_patterns_from_input(pl, argc, argv); - - memset(&existing, 0, sizeof(existing)); - existing.use_cone_patterns = core_sparse_checkout_cone; - - if (add_patterns_from_file_to_list(sparse_filename, "", 0, - &existing, NULL)) - die(_("unable to load existing sparse-checkout patterns")); - free(sparse_filename); - - hashmap_for_each_entry(&existing.recursive_hashmap, &iter, pe, ent) { - if (!hashmap_contains_parent(&pl->recursive_hashmap, - pe->pattern, &buffer) || - !hashmap_contains_parent(&pl->parent_hashmap, - pe->pattern, &buffer)) { - strbuf_reset(&buffer); - strbuf_addstr(&buffer, pe->pattern); - insert_recursive_pattern(pl, &buffer); - } - } - - clear_pattern_list(&existing); - strbuf_release(&buffer); -} - -static void add_patterns_literal(int argc, const char **argv, - struct pattern_list *pl) -{ - char *sparse_filename = get_sparse_checkout_filename(); - if (add_patterns_from_file_to_list(sparse_filename, "", 0, - pl, NULL)) - die(_("unable to load existing sparse-checkout patterns")); - free(sparse_filename); - add_patterns_from_input(pl, argc, argv); -} - -static int modify_pattern_list(int argc, const char **argv, enum modify_type m) -{ - int result; - int changed_config = 0; - struct pattern_list pl; - memset(&pl, 0, sizeof(pl)); - - switch (m) { - case ADD: - if (core_sparse_checkout_cone) - add_patterns_cone_mode(argc, argv, &pl); - else - add_patterns_literal(argc, argv, &pl); - break; - - case REPLACE: - add_patterns_from_input(&pl, argc, argv); - break; - } - - if (!core_apply_sparse_checkout) { - set_config(MODE_ALL_PATTERNS); - core_apply_sparse_checkout = 1; - changed_config = 1; - } - - result = write_patterns_and_update(&pl); - - if (result && changed_config) - set_config(MODE_NO_PATTERNS); - - clear_pattern_list(&pl); - return result; -} - -static int sparse_checkout_set(int argc, const char **argv, const char *prefix, - enum modify_type m) -{ - static struct option builtin_sparse_checkout_set_options[] = { - OPT_BOOL(0, "stdin", &set_opts.use_stdin, - N_("read patterns from standard in")), - OPT_END(), - }; - - repo_read_index(the_repository); - - argc = parse_options(argc, argv, prefix, - builtin_sparse_checkout_set_options, - builtin_sparse_checkout_set_usage, - PARSE_OPT_KEEP_UNKNOWN); - - return modify_pattern_list(argc, argv, m); -} - -static char const * const builtin_sparse_checkout_reapply_usage[] = { - N_("git sparse-checkout reapply"), - NULL -}; - -static int sparse_checkout_reapply(int argc, const char **argv) -{ - static struct option builtin_sparse_checkout_reapply_options[] = { - OPT_END(), - }; - - argc = parse_options(argc, argv, NULL, - builtin_sparse_checkout_reapply_options, - builtin_sparse_checkout_reapply_usage, 0); - - repo_read_index(the_repository); - return update_working_directory(NULL); -} - -static char const * const builtin_sparse_checkout_disable_usage[] = { - N_("git sparse-checkout disable"), - NULL -}; - -static int sparse_checkout_disable(int argc, const char **argv) -{ - static struct option builtin_sparse_checkout_disable_options[] = { - OPT_END(), - }; - struct pattern_list pl; - struct strbuf match_all = STRBUF_INIT; - - argc = parse_options(argc, argv, NULL, - builtin_sparse_checkout_disable_options, - builtin_sparse_checkout_disable_usage, 0); - - repo_read_index(the_repository); - - memset(&pl, 0, sizeof(pl)); - hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0); - hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0); - pl.use_cone_patterns = 0; - core_apply_sparse_checkout = 1; - - strbuf_addstr(&match_all, "/*"); - add_pattern(strbuf_detach(&match_all, NULL), empty_base, 0, &pl, 0); - - if (update_working_directory(&pl)) - die(_("error while refreshing working directory")); - - clear_pattern_list(&pl); - return set_config(MODE_NO_PATTERNS); -} - -int cmd_sparse_checkout(int argc, const char **argv, const char *prefix) -{ - static struct option builtin_sparse_checkout_options[] = { - OPT_END(), - }; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage_with_options(builtin_sparse_checkout_usage, - builtin_sparse_checkout_options); - - argc = parse_options(argc, argv, prefix, - builtin_sparse_checkout_options, - builtin_sparse_checkout_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - - git_config(git_default_config, NULL); - - if (argc > 0) { - if (!strcmp(argv[0], "list")) - return sparse_checkout_list(argc, argv); - if (!strcmp(argv[0], "init")) - return sparse_checkout_init(argc, argv); - if (!strcmp(argv[0], "set")) - return sparse_checkout_set(argc, argv, prefix, REPLACE); - if (!strcmp(argv[0], "add")) - return sparse_checkout_set(argc, argv, prefix, ADD); - if (!strcmp(argv[0], "reapply")) - return sparse_checkout_reapply(argc, argv); - if (!strcmp(argv[0], "disable")) - return sparse_checkout_disable(argc, argv); - } - - usage_with_options(builtin_sparse_checkout_usage, - builtin_sparse_checkout_options); -} diff --git a/third_party/git/builtin/stash.c b/third_party/git/builtin/stash.c deleted file mode 100644 index 3f811f30506a..000000000000 --- a/third_party/git/builtin/stash.c +++ /dev/null @@ -1,1612 +0,0 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "config.h" -#include "parse-options.h" -#include "refs.h" -#include "lockfile.h" -#include "cache-tree.h" -#include "unpack-trees.h" -#include "merge-recursive.h" -#include "strvec.h" -#include "run-command.h" -#include "dir.h" -#include "rerere.h" -#include "revision.h" -#include "log-tree.h" -#include "diffcore.h" -#include "exec-cmd.h" - -#define INCLUDE_ALL_FILES 2 - -static const char * const git_stash_usage[] = { - N_("git stash list [<options>]"), - N_("git stash show [<options>] [<stash>]"), - N_("git stash drop [-q|--quiet] [<stash>]"), - N_("git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"), - N_("git stash branch <branchname> [<stash>]"), - N_("git stash clear"), - N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" - " [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n" - " [--pathspec-from-file=<file> [--pathspec-file-nul]]\n" - " [--] [<pathspec>...]]"), - N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" - " [-u|--include-untracked] [-a|--all] [<message>]"), - NULL -}; - -static const char * const git_stash_list_usage[] = { - N_("git stash list [<options>]"), - NULL -}; - -static const char * const git_stash_show_usage[] = { - N_("git stash show [<options>] [<stash>]"), - NULL -}; - -static const char * const git_stash_drop_usage[] = { - N_("git stash drop [-q|--quiet] [<stash>]"), - NULL -}; - -static const char * const git_stash_pop_usage[] = { - N_("git stash pop [--index] [-q|--quiet] [<stash>]"), - NULL -}; - -static const char * const git_stash_apply_usage[] = { - N_("git stash apply [--index] [-q|--quiet] [<stash>]"), - NULL -}; - -static const char * const git_stash_branch_usage[] = { - N_("git stash branch <branchname> [<stash>]"), - NULL -}; - -static const char * const git_stash_clear_usage[] = { - N_("git stash clear"), - NULL -}; - -static const char * const git_stash_store_usage[] = { - N_("git stash store [-m|--message <message>] [-q|--quiet] <commit>"), - NULL -}; - -static const char * const git_stash_push_usage[] = { - N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" - " [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n" - " [--] [<pathspec>...]]"), - NULL -}; - -static const char * const git_stash_save_usage[] = { - N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n" - " [-u|--include-untracked] [-a|--all] [<message>]"), - NULL -}; - -static const char *ref_stash = "refs/stash"; -static struct strbuf stash_index_path = STRBUF_INIT; - -/* - * w_commit is set to the commit containing the working tree - * b_commit is set to the base commit - * i_commit is set to the commit containing the index tree - * u_commit is set to the commit containing the untracked files tree - * w_tree is set to the working tree - * b_tree is set to the base tree - * i_tree is set to the index tree - * u_tree is set to the untracked files tree - */ -struct stash_info { - struct object_id w_commit; - struct object_id b_commit; - struct object_id i_commit; - struct object_id u_commit; - struct object_id w_tree; - struct object_id b_tree; - struct object_id i_tree; - struct object_id u_tree; - struct strbuf revision; - int is_stash_ref; - int has_u; -}; - -static void free_stash_info(struct stash_info *info) -{ - strbuf_release(&info->revision); -} - -static void assert_stash_like(struct stash_info *info, const char *revision) -{ - if (get_oidf(&info->b_commit, "%s^1", revision) || - get_oidf(&info->w_tree, "%s:", revision) || - get_oidf(&info->b_tree, "%s^1:", revision) || - get_oidf(&info->i_tree, "%s^2:", revision)) - die(_("'%s' is not a stash-like commit"), revision); -} - -static int get_stash_info(struct stash_info *info, int argc, const char **argv) -{ - int ret; - char *end_of_rev; - char *expanded_ref; - const char *revision; - const char *commit = NULL; - struct object_id dummy; - struct strbuf symbolic = STRBUF_INIT; - - if (argc > 1) { - int i; - struct strbuf refs_msg = STRBUF_INIT; - - for (i = 0; i < argc; i++) - strbuf_addf(&refs_msg, " '%s'", argv[i]); - - fprintf_ln(stderr, _("Too many revisions specified:%s"), - refs_msg.buf); - strbuf_release(&refs_msg); - - return -1; - } - - if (argc == 1) - commit = argv[0]; - - strbuf_init(&info->revision, 0); - if (!commit) { - if (!ref_exists(ref_stash)) { - free_stash_info(info); - fprintf_ln(stderr, _("No stash entries found.")); - return -1; - } - - strbuf_addf(&info->revision, "%s@{0}", ref_stash); - } else if (strspn(commit, "0123456789") == strlen(commit)) { - strbuf_addf(&info->revision, "%s@{%s}", ref_stash, commit); - } else { - strbuf_addstr(&info->revision, commit); - } - - revision = info->revision.buf; - - if (get_oid(revision, &info->w_commit)) { - error(_("%s is not a valid reference"), revision); - free_stash_info(info); - return -1; - } - - assert_stash_like(info, revision); - - info->has_u = !get_oidf(&info->u_tree, "%s^3:", revision); - - end_of_rev = strchrnul(revision, '@'); - strbuf_add(&symbolic, revision, end_of_rev - revision); - - ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref, 0); - strbuf_release(&symbolic); - switch (ret) { - case 0: /* Not found, but valid ref */ - info->is_stash_ref = 0; - break; - case 1: - info->is_stash_ref = !strcmp(expanded_ref, ref_stash); - break; - default: /* Invalid or ambiguous */ - free_stash_info(info); - } - - free(expanded_ref); - return !(ret == 0 || ret == 1); -} - -static int do_clear_stash(void) -{ - struct object_id obj; - if (get_oid(ref_stash, &obj)) - return 0; - - return delete_ref(NULL, ref_stash, &obj, 0); -} - -static int clear_stash(int argc, const char **argv, const char *prefix) -{ - struct option options[] = { - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, - git_stash_clear_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - - if (argc) - return error(_("git stash clear with parameters is " - "unimplemented")); - - return do_clear_stash(); -} - -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 int diff_tree_binary(struct strbuf *out, struct object_id *w_commit) -{ - struct child_process cp = CHILD_PROCESS_INIT; - const char *w_commit_hex = oid_to_hex(w_commit); - - /* - * Diff-tree would not be very hard to replace with a native function, - * however it should be done together with apply_cached. - */ - cp.git_cmd = 1; - strvec_pushl(&cp.args, "diff-tree", "--binary", NULL); - strvec_pushf(&cp.args, "%s^2^..%s^2", w_commit_hex, w_commit_hex); - - return pipe_command(&cp, NULL, 0, out, 0, NULL, 0); -} - -static int apply_cached(struct strbuf *out) -{ - struct child_process cp = CHILD_PROCESS_INIT; - - /* - * Apply currently only reads either from stdin or a file, thus - * apply_all_patches would have to be updated to optionally take a - * buffer. - */ - cp.git_cmd = 1; - strvec_pushl(&cp.args, "apply", "--cached", NULL); - return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0); -} - -static int reset_head(void) -{ - struct child_process cp = CHILD_PROCESS_INIT; - - /* - * Reset is overall quite simple, however there is no current public - * API for resetting. - */ - cp.git_cmd = 1; - strvec_push(&cp.args, "reset"); - - return run_command(&cp); -} - -static void add_diff_to_buf(struct diff_queue_struct *q, - struct diff_options *options, - void *data) -{ - int i; - - for (i = 0; i < q->nr; i++) { - strbuf_addstr(data, q->queue[i]->one->path); - - /* NUL-terminate: will be fed to update-index -z */ - strbuf_addch(data, '\0'); - } -} - -static int get_newly_staged(struct strbuf *out, struct object_id *c_tree) -{ - struct child_process cp = CHILD_PROCESS_INIT; - const char *c_tree_hex = oid_to_hex(c_tree); - - /* - * diff-index is very similar to diff-tree above, and should be - * converted together with update_index. - */ - cp.git_cmd = 1; - strvec_pushl(&cp.args, "diff-index", "--cached", "--name-only", - "--diff-filter=A", NULL); - strvec_push(&cp.args, c_tree_hex); - return pipe_command(&cp, NULL, 0, out, 0, NULL, 0); -} - -static int update_index(struct strbuf *out) -{ - struct child_process cp = CHILD_PROCESS_INIT; - - /* - * Update-index is very complicated and may need to have a public - * function exposed in order to remove this forking. - */ - cp.git_cmd = 1; - strvec_pushl(&cp.args, "update-index", "--add", "--stdin", NULL); - return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0); -} - -static int restore_untracked(struct object_id *u_tree) -{ - int res; - struct child_process cp = CHILD_PROCESS_INIT; - - /* - * We need to run restore files from a given index, but without - * affecting the current index, so we use GIT_INDEX_FILE with - * run_command to fork processes that will not interfere. - */ - cp.git_cmd = 1; - strvec_push(&cp.args, "read-tree"); - strvec_push(&cp.args, oid_to_hex(u_tree)); - strvec_pushf(&cp.env_array, "GIT_INDEX_FILE=%s", - stash_index_path.buf); - if (run_command(&cp)) { - remove_path(stash_index_path.buf); - return -1; - } - - child_process_init(&cp); - cp.git_cmd = 1; - strvec_pushl(&cp.args, "checkout-index", "--all", NULL); - strvec_pushf(&cp.env_array, "GIT_INDEX_FILE=%s", - stash_index_path.buf); - - res = run_command(&cp); - remove_path(stash_index_path.buf); - return res; -} - -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_and_write_cache(REFRESH_QUIET, 0, 0)) - 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(); - discard_cache(); - read_cache(); - } - } - - 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; - - /* read back the result of update_index() back from the disk */ - discard_cache(); - read_cache(); - } - - if (!quiet) { - 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; - strvec_pushf(&cp.env_array, GIT_WORK_TREE_ENVIRONMENT"=%s", - absolute_path(get_git_work_tree())); - strvec_pushf(&cp.env_array, GIT_DIR_ENVIRONMENT"=%s", - absolute_path(get_git_dir())); - strvec_push(&cp.args, "status"); - run_command(&cp); - } - - return 0; -} - -static int apply_stash(int argc, const char **argv, const char *prefix) -{ - int ret; - int quiet = 0; - int index = 0; - struct stash_info info; - struct option options[] = { - OPT__QUIET(&quiet, N_("be quiet, only report errors")), - OPT_BOOL(0, "index", &index, - N_("attempt to recreate the index")), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, - git_stash_apply_usage, 0); - - if (get_stash_info(&info, argc, argv)) - return -1; - - ret = do_apply_stash(prefix, &info, index, quiet); - free_stash_info(&info); - return ret; -} - -static int do_drop_stash(struct stash_info *info, int quiet) -{ - int ret; - struct child_process cp_reflog = CHILD_PROCESS_INIT; - struct child_process cp = CHILD_PROCESS_INIT; - - /* - * reflog does not provide a simple function for deleting refs. One will - * need to be added to avoid implementing too much reflog code here - */ - - cp_reflog.git_cmd = 1; - strvec_pushl(&cp_reflog.args, "reflog", "delete", "--updateref", - "--rewrite", NULL); - strvec_push(&cp_reflog.args, info->revision.buf); - ret = run_command(&cp_reflog); - if (!ret) { - if (!quiet) - printf_ln(_("Dropped %s (%s)"), info->revision.buf, - oid_to_hex(&info->w_commit)); - } else { - return error(_("%s: Could not drop stash entry"), - info->revision.buf); - } - - /* - * This could easily be replaced by get_oid, but currently it will throw - * a fatal error when a reflog is empty, which we can not recover from. - */ - cp.git_cmd = 1; - /* Even though --quiet is specified, rev-parse still outputs the hash */ - cp.no_stdout = 1; - strvec_pushl(&cp.args, "rev-parse", "--verify", "--quiet", NULL); - strvec_pushf(&cp.args, "%s@{0}", ref_stash); - ret = run_command(&cp); - - /* do_clear_stash if we just dropped the last stash entry */ - if (ret) - do_clear_stash(); - - return 0; -} - -static void assert_stash_ref(struct stash_info *info) -{ - if (!info->is_stash_ref) { - error(_("'%s' is not a stash reference"), info->revision.buf); - free_stash_info(info); - exit(1); - } -} - -static int drop_stash(int argc, const char **argv, const char *prefix) -{ - int ret; - int quiet = 0; - struct stash_info info; - struct option options[] = { - OPT__QUIET(&quiet, N_("be quiet, only report errors")), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, - git_stash_drop_usage, 0); - - if (get_stash_info(&info, argc, argv)) - return -1; - - assert_stash_ref(&info); - - ret = do_drop_stash(&info, quiet); - free_stash_info(&info); - return ret; -} - -static int pop_stash(int argc, const char **argv, const char *prefix) -{ - int ret; - int index = 0; - int quiet = 0; - struct stash_info info; - struct option options[] = { - OPT__QUIET(&quiet, N_("be quiet, only report errors")), - OPT_BOOL(0, "index", &index, - N_("attempt to recreate the index")), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, - git_stash_pop_usage, 0); - - if (get_stash_info(&info, argc, argv)) - return -1; - - assert_stash_ref(&info); - if ((ret = do_apply_stash(prefix, &info, index, quiet))) - printf_ln(_("The stash entry is kept in case " - "you need it again.")); - else - ret = do_drop_stash(&info, quiet); - - free_stash_info(&info); - return ret; -} - -static int branch_stash(int argc, const char **argv, const char *prefix) -{ - int ret; - const char *branch = NULL; - struct stash_info info; - struct child_process cp = CHILD_PROCESS_INIT; - struct option options[] = { - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, - git_stash_branch_usage, 0); - - if (!argc) { - fprintf_ln(stderr, _("No branch name specified")); - return -1; - } - - branch = argv[0]; - - if (get_stash_info(&info, argc - 1, argv + 1)) - return -1; - - cp.git_cmd = 1; - strvec_pushl(&cp.args, "checkout", "-b", NULL); - strvec_push(&cp.args, branch); - strvec_push(&cp.args, oid_to_hex(&info.b_commit)); - ret = run_command(&cp); - if (!ret) - ret = do_apply_stash(prefix, &info, 1, 0); - if (!ret && info.is_stash_ref) - ret = do_drop_stash(&info, 0); - - free_stash_info(&info); - - return ret; -} - -static int list_stash(int argc, const char **argv, const char *prefix) -{ - struct child_process cp = CHILD_PROCESS_INIT; - struct option options[] = { - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, - git_stash_list_usage, - PARSE_OPT_KEEP_UNKNOWN); - - if (!ref_exists(ref_stash)) - return 0; - - cp.git_cmd = 1; - strvec_pushl(&cp.args, "log", "--format=%gd: %gs", "-g", - "--first-parent", "-m", NULL); - strvec_pushv(&cp.args, argv); - strvec_push(&cp.args, ref_stash); - strvec_push(&cp.args, "--"); - return run_command(&cp); -} - -static int show_stat = 1; -static int show_patch; -static int use_legacy_stash; - -static int git_stash_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "stash.showstat")) { - show_stat = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "stash.showpatch")) { - show_patch = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "stash.usebuiltin")) { - use_legacy_stash = !git_config_bool(var, value); - return 0; - } - return git_diff_basic_config(var, value, cb); -} - -static int show_stash(int argc, const char **argv, const char *prefix) -{ - int i; - int ret = 0; - struct stash_info info; - struct rev_info rev; - struct strvec stash_args = STRVEC_INIT; - struct strvec revision_args = STRVEC_INIT; - struct option options[] = { - OPT_END() - }; - - init_diff_ui_defaults(); - git_config(git_diff_ui_config, NULL); - init_revisions(&rev, prefix); - - strvec_push(&revision_args, argv[0]); - for (i = 1; i < argc; i++) { - if (argv[i][0] != '-') - strvec_push(&stash_args, argv[i]); - else - strvec_push(&revision_args, argv[i]); - } - - ret = get_stash_info(&info, stash_args.nr, stash_args.v); - strvec_clear(&stash_args); - if (ret) - return -1; - - /* - * The config settings are applied only if there are not passed - * any options. - */ - if (revision_args.nr == 1) { - if (show_stat) - rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT; - - if (show_patch) - rev.diffopt.output_format |= DIFF_FORMAT_PATCH; - - if (!show_stat && !show_patch) { - free_stash_info(&info); - return 0; - } - } - - argc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL); - if (argc > 1) { - free_stash_info(&info); - usage_with_options(git_stash_show_usage, options); - } - if (!rev.diffopt.output_format) { - rev.diffopt.output_format = DIFF_FORMAT_PATCH; - diff_setup_done(&rev.diffopt); - } - - rev.diffopt.flags.recursive = 1; - setup_diff_pager(&rev.diffopt); - diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt); - log_tree_diff_flush(&rev); - - free_stash_info(&info); - return diff_result_code(&rev.diffopt, 0); -} - -static int do_store_stash(const struct object_id *w_commit, const char *stash_msg, - int quiet) -{ - if (!stash_msg) - stash_msg = "Created via \"git stash store\"."; - - if (update_ref(stash_msg, ref_stash, w_commit, NULL, - REF_FORCE_CREATE_REFLOG, - quiet ? UPDATE_REFS_QUIET_ON_ERR : - UPDATE_REFS_MSG_ON_ERR)) { - if (!quiet) { - fprintf_ln(stderr, _("Cannot update %s with %s"), - ref_stash, oid_to_hex(w_commit)); - } - return -1; - } - - return 0; -} - -static int store_stash(int argc, const char **argv, const char *prefix) -{ - int quiet = 0; - const char *stash_msg = NULL; - struct object_id obj; - struct object_context dummy; - struct option options[] = { - OPT__QUIET(&quiet, N_("be quiet")), - OPT_STRING('m', "message", &stash_msg, "message", - N_("stash message")), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, - git_stash_store_usage, - PARSE_OPT_KEEP_UNKNOWN); - - if (argc != 1) { - if (!quiet) - fprintf_ln(stderr, _("\"git stash store\" requires one " - "<commit> argument")); - return -1; - } - - if (get_oid_with_context(the_repository, - argv[0], quiet ? GET_OID_QUIETLY : 0, &obj, - &dummy)) { - if (!quiet) - fprintf_ln(stderr, _("Cannot update %s with %s"), - ref_stash, argv[0]); - return -1; - } - - return do_store_stash(&obj, stash_msg, quiet); -} - -static void add_pathspecs(struct strvec *args, - const struct pathspec *ps) { - int i; - - for (i = 0; i < ps->nr; i++) - strvec_push(args, ps->items[i].original); -} - -/* - * `untracked_files` will be filled with the names of untracked files. - * The return value is: - * - * = 0 if there are not any untracked files - * > 0 if there are untracked files - */ -static int get_untracked_files(const struct pathspec *ps, int include_untracked, - struct strbuf *untracked_files) -{ - int i; - int found = 0; - struct dir_struct dir; - - dir_init(&dir); - if (include_untracked != INCLUDE_ALL_FILES) - setup_standard_excludes(&dir); - - fill_directory(&dir, the_repository->index, ps); - for (i = 0; i < dir.nr; i++) { - struct dir_entry *ent = dir.entries[i]; - found++; - strbuf_addstr(untracked_files, ent->name); - /* NUL-terminate: will be fed to update-index -z */ - strbuf_addch(untracked_files, '\0'); - } - - dir_clear(&dir); - return found; -} - -/* - * The return value of `check_changes_tracked_files()` can be: - * - * < 0 if there was an error - * = 0 if there are no changes. - * > 0 if there are changes. - */ -static int check_changes_tracked_files(const struct pathspec *ps) -{ - int result; - struct rev_info rev; - struct object_id dummy; - int ret = 0; - - /* No initial commit. */ - if (get_oid("HEAD", &dummy)) - return -1; - - if (read_cache() < 0) - return -1; - - init_revisions(&rev, NULL); - copy_pathspec(&rev.prune_data, ps); - - rev.diffopt.flags.quick = 1; - rev.diffopt.flags.ignore_submodules = 1; - rev.abbrev = 0; - - add_head_to_pending(&rev); - diff_setup_done(&rev.diffopt); - - result = run_diff_index(&rev, 1); - if (diff_result_code(&rev.diffopt, result)) { - ret = 1; - goto done; - } - - object_array_clear(&rev.pending); - result = run_diff_files(&rev, 0); - if (diff_result_code(&rev.diffopt, result)) { - ret = 1; - goto done; - } - -done: - clear_pathspec(&rev.prune_data); - return ret; -} - -/* - * The function will fill `untracked_files` with the names of untracked files - * It will return 1 if there were any changes and 0 if there were not. - */ -static int check_changes(const struct pathspec *ps, int include_untracked, - struct strbuf *untracked_files) -{ - int ret = 0; - if (check_changes_tracked_files(ps)) - ret = 1; - - if (include_untracked && get_untracked_files(ps, include_untracked, - untracked_files)) - ret = 1; - - return ret; -} - -static int save_untracked_files(struct stash_info *info, struct strbuf *msg, - struct strbuf files) -{ - int ret = 0; - struct strbuf untracked_msg = STRBUF_INIT; - struct child_process cp_upd_index = CHILD_PROCESS_INIT; - struct index_state istate = { NULL }; - - cp_upd_index.git_cmd = 1; - strvec_pushl(&cp_upd_index.args, "update-index", "-z", "--add", - "--remove", "--stdin", NULL); - strvec_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s", - stash_index_path.buf); - - strbuf_addf(&untracked_msg, "untracked files on %s\n", msg->buf); - if (pipe_command(&cp_upd_index, files.buf, files.len, NULL, 0, - NULL, 0)) { - ret = -1; - goto done; - } - - if (write_index_as_tree(&info->u_tree, &istate, stash_index_path.buf, 0, - NULL)) { - ret = -1; - goto done; - } - - if (commit_tree(untracked_msg.buf, untracked_msg.len, - &info->u_tree, NULL, &info->u_commit, NULL, NULL)) { - ret = -1; - goto done; - } - -done: - discard_index(&istate); - strbuf_release(&untracked_msg); - remove_path(stash_index_path.buf); - return ret; -} - -static int stash_patch(struct stash_info *info, const struct pathspec *ps, - struct strbuf *out_patch, int quiet) -{ - int ret = 0; - struct child_process cp_read_tree = CHILD_PROCESS_INIT; - struct child_process cp_diff_tree = CHILD_PROCESS_INIT; - struct index_state istate = { NULL }; - char *old_index_env = NULL, *old_repo_index_file; - - remove_path(stash_index_path.buf); - - cp_read_tree.git_cmd = 1; - strvec_pushl(&cp_read_tree.args, "read-tree", "HEAD", NULL); - strvec_pushf(&cp_read_tree.env_array, "GIT_INDEX_FILE=%s", - stash_index_path.buf); - if (run_command(&cp_read_tree)) { - ret = -1; - goto done; - } - - /* Find out what the user wants. */ - old_repo_index_file = the_repository->index_file; - the_repository->index_file = stash_index_path.buf; - old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); - setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - - ret = run_add_interactive(NULL, "--patch=stash", ps); - - the_repository->index_file = old_repo_index_file; - if (old_index_env && *old_index_env) - setenv(INDEX_ENVIRONMENT, old_index_env, 1); - else - unsetenv(INDEX_ENVIRONMENT); - FREE_AND_NULL(old_index_env); - - /* State of the working tree. */ - if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0, - NULL)) { - ret = -1; - goto done; - } - - cp_diff_tree.git_cmd = 1; - strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD", - oid_to_hex(&info->w_tree), "--", NULL); - if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) { - ret = -1; - goto done; - } - - if (!out_patch->len) { - if (!quiet) - fprintf_ln(stderr, _("No changes selected")); - ret = 1; - } - -done: - discard_index(&istate); - remove_path(stash_index_path.buf); - return ret; -} - -static int stash_working_tree(struct stash_info *info, const 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); - copy_pathspec(&rev.prune_data, ps); - - 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.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; - strvec_pushl(&cp_upd_index.args, "update-index", - "--ignore-skip-worktree-entries", - "-z", "--add", "--remove", "--stdin", NULL); - strvec_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); - clear_pathspec(&rev.prune_data); - strbuf_release(&diff_output); - remove_path(stash_index_path.buf); - return ret; -} - -static int do_create_stash(const 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); - if (refresh_and_write_cache(REFRESH_QUIET, 0, 0) < 0) { - ret = -1; - goto done; - } - - 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 create_stash(int argc, const char **argv, const char *prefix) -{ - int ret = 0; - struct strbuf stash_msg_buf = STRBUF_INIT; - struct stash_info info; - struct pathspec ps; - - /* Starting with argv[1], since argv[0] is "create" */ - strbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' '); - - memset(&ps, 0, sizeof(ps)); - if (!check_changes_tracked_files(&ps)) - return 0; - - ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, &info, - NULL, 0); - if (!ret) - printf_ln("%s", oid_to_hex(&info.w_commit)); - - strbuf_release(&stash_msg_buf); - return ret; -} - -static int do_push_stash(const 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)) { - fprintf_ln(stderr, _("Did you forget to 'git add'?")); - ret = -1; - free(ps_matched); - goto done; - } - free(ps_matched); - } - - if (refresh_and_write_cache(REFRESH_QUIET, 0, 0)) { - 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; - strvec_pushl(&cp.args, "clean", "--force", - "--quiet", "-d", NULL); - if (include_untracked == INCLUDE_ALL_FILES) - strvec_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; - strvec_push(&cp_add.args, "add"); - if (!include_untracked) - strvec_push(&cp_add.args, "-u"); - if (include_untracked == INCLUDE_ALL_FILES) - strvec_push(&cp_add.args, "--force"); - strvec_push(&cp_add.args, "--"); - add_pathspecs(&cp_add.args, ps); - if (run_command(&cp_add)) { - ret = -1; - goto done; - } - - cp_diff.git_cmd = 1; - strvec_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; - strvec_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; - strvec_pushl(&cp.args, "reset", "--hard", "-q", - "--no-recurse-submodules", NULL); - if (run_command(&cp)) { - ret = -1; - goto done; - } - } - - if (keep_index == 1 && !is_null_oid(&info.i_tree)) { - struct child_process cp = CHILD_PROCESS_INIT; - - cp.git_cmd = 1; - strvec_pushl(&cp.args, "checkout", "--no-overlay", - oid_to_hex(&info.i_tree), "--", NULL); - if (!ps->nr) - strvec_push(&cp.args, ":/"); - else - add_pathspecs(&cp.args, ps); - if (run_command(&cp)) { - ret = -1; - goto done; - } - } - goto done; - } else { - struct child_process cp = CHILD_PROCESS_INIT; - - cp.git_cmd = 1; - strvec_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; - - cp.git_cmd = 1; - strvec_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 push_stash(int argc, const char **argv, const char *prefix, - int push_assumed) -{ - int force_assume = 0; - int keep_index = -1; - int patch_mode = 0; - int include_untracked = 0; - int quiet = 0; - int pathspec_file_nul = 0; - const char *stash_msg = NULL; - const char *pathspec_from_file = NULL; - struct pathspec ps; - struct option options[] = { - OPT_BOOL('k', "keep-index", &keep_index, - N_("keep index")), - OPT_BOOL('p', "patch", &patch_mode, - N_("stash in patch mode")), - OPT__QUIET(&quiet, N_("quiet mode")), - OPT_BOOL('u', "include-untracked", &include_untracked, - N_("include untracked files in stash")), - OPT_SET_INT('a', "all", &include_untracked, - N_("include ignore files"), 2), - OPT_STRING('m', "message", &stash_msg, N_("message"), - N_("stash message")), - OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), - OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), - OPT_END() - }; - - if (argc) { - force_assume = !strcmp(argv[0], "-p"); - argc = parse_options(argc, argv, prefix, options, - git_stash_push_usage, - PARSE_OPT_KEEP_DASHDASH); - } - - if (argc) { - if (!strcmp(argv[0], "--")) { - argc--; - argv++; - } else if (push_assumed && !force_assume) { - die("subcommand wasn't specified; 'push' can't be assumed due to unexpected token '%s'", - argv[0]); - } - } - - parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN, - prefix, argv); - - if (pathspec_from_file) { - if (patch_mode) - die(_("--pathspec-from-file is incompatible with --patch")); - - if (ps.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); - - parse_pathspec_file(&ps, 0, - PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN, - prefix, pathspec_from_file, pathspec_file_nul); - } else if (pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); - } - - return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, - include_untracked); -} - -static int save_stash(int argc, const char **argv, const char *prefix) -{ - int keep_index = -1; - int patch_mode = 0; - int include_untracked = 0; - int quiet = 0; - int ret = 0; - const char *stash_msg = NULL; - struct pathspec ps; - struct strbuf stash_msg_buf = STRBUF_INIT; - struct option options[] = { - OPT_BOOL('k', "keep-index", &keep_index, - N_("keep index")), - OPT_BOOL('p', "patch", &patch_mode, - N_("stash in patch mode")), - OPT__QUIET(&quiet, N_("quiet mode")), - OPT_BOOL('u', "include-untracked", &include_untracked, - N_("include untracked files in stash")), - OPT_SET_INT('a', "all", &include_untracked, - N_("include ignore files"), 2), - OPT_STRING('m', "message", &stash_msg, "message", - N_("stash message")), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, - git_stash_save_usage, - PARSE_OPT_KEEP_DASHDASH); - - if (argc) - stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' '); - - memset(&ps, 0, sizeof(ps)); - ret = do_push_stash(&ps, stash_msg, quiet, keep_index, - patch_mode, include_untracked); - - strbuf_release(&stash_msg_buf); - return ret; -} - -int cmd_stash(int argc, const char **argv, const char *prefix) -{ - pid_t pid = getpid(); - const char *index_file; - struct strvec args = STRVEC_INIT; - - struct option options[] = { - OPT_END() - }; - - git_config(git_stash_config, NULL); - - if (use_legacy_stash || - !git_env_bool("GIT_TEST_STASH_USE_BUILTIN", -1)) - warning(_("the stash.useBuiltin support has been removed!\n" - "See its entry in 'git help config' for details.")); - - argc = parse_options(argc, argv, prefix, options, git_stash_usage, - PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); - - index_file = get_index_file(); - strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file, - (uintmax_t)pid); - - if (!argc) - return !!push_stash(0, NULL, prefix, 0); - else if (!strcmp(argv[0], "apply")) - return !!apply_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "clear")) - return !!clear_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "drop")) - return !!drop_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "pop")) - return !!pop_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "branch")) - return !!branch_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "list")) - return !!list_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "show")) - return !!show_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "store")) - return !!store_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "create")) - return !!create_stash(argc, argv, prefix); - else if (!strcmp(argv[0], "push")) - return !!push_stash(argc, argv, prefix, 0); - else if (!strcmp(argv[0], "save")) - return !!save_stash(argc, argv, prefix); - else if (*argv[0] != '-') - usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]), - git_stash_usage, options); - - /* Assume 'stash push' */ - strvec_push(&args, "push"); - strvec_pushv(&args, argv); - return !!push_stash(args.nr, args.v, prefix, 1); -} diff --git a/third_party/git/builtin/stripspace.c b/third_party/git/builtin/stripspace.c deleted file mode 100644 index be33eb83c1b7..000000000000 --- a/third_party/git/builtin/stripspace.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "parse-options.h" -#include "strbuf.h" - -static void comment_lines(struct strbuf *buf) -{ - char *msg; - size_t len; - - msg = strbuf_detach(buf, &len); - strbuf_add_commented_lines(buf, msg, len); - free(msg); -} - -static const char * const stripspace_usage[] = { - N_("git stripspace [-s | --strip-comments]"), - N_("git stripspace [-c | --comment-lines]"), - NULL -}; - -enum stripspace_mode { - STRIP_DEFAULT = 0, - STRIP_COMMENTS, - COMMENT_LINES -}; - -int cmd_stripspace(int argc, const char **argv, const char *prefix) -{ - struct strbuf buf = STRBUF_INIT; - enum stripspace_mode mode = STRIP_DEFAULT; - int nongit; - - const struct option options[] = { - OPT_CMDMODE('s', "strip-comments", &mode, - N_("skip and remove all lines starting with comment character"), - STRIP_COMMENTS), - OPT_CMDMODE('c', "comment-lines", &mode, - N_("prepend comment character and space to each line"), - COMMENT_LINES), - OPT_END() - }; - - argc = parse_options(argc, argv, prefix, options, stripspace_usage, 0); - if (argc) - usage_with_options(stripspace_usage, options); - - if (mode == STRIP_COMMENTS || mode == COMMENT_LINES) { - setup_git_directory_gently(&nongit); - git_config(git_default_config, NULL); - } - - if (strbuf_read(&buf, 0, 1024) < 0) - die_errno("could not read the input"); - - if (mode == STRIP_DEFAULT || mode == STRIP_COMMENTS) - strbuf_stripspace(&buf, mode == STRIP_COMMENTS); - else - comment_lines(&buf); - - write_or_die(1, buf.buf, buf.len); - strbuf_release(&buf); - return 0; -} diff --git a/third_party/git/builtin/submodule--helper.c b/third_party/git/builtin/submodule--helper.c deleted file mode 100644 index c30896c89788..000000000000 --- a/third_party/git/builtin/submodule--helper.c +++ /dev/null @@ -1,2800 +0,0 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "repository.h" -#include "cache.h" -#include "config.h" -#include "parse-options.h" -#include "quote.h" -#include "pathspec.h" -#include "dir.h" -#include "submodule.h" -#include "submodule-config.h" -#include "string-list.h" -#include "run-command.h" -#include "remote.h" -#include "refs.h" -#include "refspec.h" -#include "connect.h" -#include "revision.h" -#include "diffcore.h" -#include "diff.h" -#include "object-store.h" -#include "dir.h" -#include "advice.h" - -#define OPT_QUIET (1 << 0) -#define OPT_CACHED (1 << 1) -#define OPT_RECURSIVE (1 << 2) -#define OPT_FORCE (1 << 3) - -typedef void (*each_submodule_fn)(const struct cache_entry *list_item, - void *cb_data); - -static char *get_default_remote(void) -{ - char *dest = NULL, *ret; - struct strbuf sb = STRBUF_INIT; - const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL); - - if (!refname) - die(_("No such ref: %s"), "HEAD"); - - /* detached HEAD */ - if (!strcmp(refname, "HEAD")) - return xstrdup("origin"); - - if (!skip_prefix(refname, "refs/heads/", &refname)) - die(_("Expecting a full ref name, got %s"), refname); - - strbuf_addf(&sb, "branch.%s.remote", refname); - if (git_config_get_string(sb.buf, &dest)) - ret = xstrdup("origin"); - else - ret = dest; - - strbuf_release(&sb); - return ret; -} - -static int print_default_remote(int argc, const char **argv, const char *prefix) -{ - char *remote; - - if (argc != 1) - die(_("submodule--helper print-default-remote takes no arguments")); - - remote = get_default_remote(); - if (remote) - printf("%s\n", remote); - - free(remote); - return 0; -} - -static int starts_with_dot_slash(const char *str) -{ - return str[0] == '.' && is_dir_sep(str[1]); -} - -static int starts_with_dot_dot_slash(const char *str) -{ - return str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]); -} - -/* - * Returns 1 if it was the last chop before ':'. - */ -static int chop_last_dir(char **remoteurl, int is_relative) -{ - char *rfind = find_last_dir_sep(*remoteurl); - if (rfind) { - *rfind = '\0'; - return 0; - } - - rfind = strrchr(*remoteurl, ':'); - if (rfind) { - *rfind = '\0'; - return 1; - } - - if (is_relative || !strcmp(".", *remoteurl)) - die(_("cannot strip one component off url '%s'"), - *remoteurl); - - free(*remoteurl); - *remoteurl = xstrdup("."); - return 0; -} - -/* - * The `url` argument is the URL that navigates to the submodule origin - * repo. When relative, this URL is relative to the superproject origin - * URL repo. The `up_path` argument, if specified, is the relative - * path that navigates from the submodule working tree to the superproject - * working tree. Returns the origin URL of the submodule. - * - * Return either an absolute URL or filesystem path (if the superproject - * origin URL is an absolute URL or filesystem path, respectively) or a - * relative file system path (if the superproject origin URL is a relative - * file system path). - * - * When the output is a relative file system path, the path is either - * relative to the submodule working tree, if up_path is specified, or to - * the superproject working tree otherwise. - * - * NEEDSWORK: This works incorrectly on the domain and protocol part. - * remote_url url outcome expectation - * http://a.com/b ../c http://a.com/c as is - * http://a.com/b/ ../c http://a.com/c same as previous line, but - * ignore trailing slash in url - * http://a.com/b ../../c http://c error out - * http://a.com/b ../../../c http:/c error out - * http://a.com/b ../../../../c http:c error out - * http://a.com/b ../../../../../c .:c error out - * NEEDSWORK: Given how chop_last_dir() works, this function is broken - * when a local part has a colon in its path component, too. - */ -static char *relative_url(const char *remote_url, - const char *url, - const char *up_path) -{ - int is_relative = 0; - int colonsep = 0; - char *out; - char *remoteurl = xstrdup(remote_url); - struct strbuf sb = STRBUF_INIT; - size_t len = strlen(remoteurl); - - if (is_dir_sep(remoteurl[len-1])) - remoteurl[len-1] = '\0'; - - if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl)) - is_relative = 0; - else { - is_relative = 1; - /* - * Prepend a './' to ensure all relative - * remoteurls start with './' or '../' - */ - if (!starts_with_dot_slash(remoteurl) && - !starts_with_dot_dot_slash(remoteurl)) { - strbuf_reset(&sb); - strbuf_addf(&sb, "./%s", remoteurl); - free(remoteurl); - remoteurl = strbuf_detach(&sb, NULL); - } - } - /* - * When the url starts with '../', remove that and the - * last directory in remoteurl. - */ - while (url) { - if (starts_with_dot_dot_slash(url)) { - url += 3; - colonsep |= chop_last_dir(&remoteurl, is_relative); - } else if (starts_with_dot_slash(url)) - url += 2; - else - break; - } - strbuf_reset(&sb); - strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url); - if (ends_with(url, "/")) - strbuf_setlen(&sb, sb.len - 1); - free(remoteurl); - - if (starts_with_dot_slash(sb.buf)) - out = xstrdup(sb.buf + 2); - else - out = xstrdup(sb.buf); - strbuf_reset(&sb); - - if (!up_path || !is_relative) - return out; - - strbuf_addf(&sb, "%s%s", up_path, out); - free(out); - return strbuf_detach(&sb, NULL); -} - -static int resolve_relative_url(int argc, const char **argv, const char *prefix) -{ - char *remoteurl = NULL; - char *remote = get_default_remote(); - const char *up_path = NULL; - char *res; - const char *url; - struct strbuf sb = STRBUF_INIT; - - if (argc != 2 && argc != 3) - die("resolve-relative-url only accepts one or two arguments"); - - url = argv[1]; - strbuf_addf(&sb, "remote.%s.url", remote); - free(remote); - - if (git_config_get_string(sb.buf, &remoteurl)) - /* the repository is its own authoritative upstream */ - remoteurl = xgetcwd(); - - if (argc == 3) - up_path = argv[2]; - - res = relative_url(remoteurl, url, up_path); - puts(res); - free(res); - free(remoteurl); - return 0; -} - -static int resolve_relative_url_test(int argc, const char **argv, const char *prefix) -{ - char *remoteurl, *res; - const char *up_path, *url; - - if (argc != 4) - die("resolve-relative-url-test only accepts three arguments: <up_path> <remoteurl> <url>"); - - up_path = argv[1]; - remoteurl = xstrdup(argv[2]); - url = argv[3]; - - if (!strcmp(up_path, "(null)")) - up_path = NULL; - - res = relative_url(remoteurl, url, up_path); - puts(res); - free(res); - free(remoteurl); - return 0; -} - -/* the result should be freed by the caller. */ -static char *get_submodule_displaypath(const char *path, const char *prefix) -{ - const char *super_prefix = get_super_prefix(); - - if (prefix && super_prefix) { - BUG("cannot have prefix '%s' and superprefix '%s'", - prefix, super_prefix); - } else if (prefix) { - struct strbuf sb = STRBUF_INIT; - char *displaypath = xstrdup(relative_path(path, prefix, &sb)); - strbuf_release(&sb); - return displaypath; - } else if (super_prefix) { - return xstrfmt("%s%s", super_prefix, path); - } else { - return xstrdup(path); - } -} - -static char *compute_rev_name(const char *sub_path, const char* object_id) -{ - struct strbuf sb = STRBUF_INIT; - const char ***d; - - static const char *describe_bare[] = { NULL }; - - static const char *describe_tags[] = { "--tags", NULL }; - - static const char *describe_contains[] = { "--contains", NULL }; - - static const char *describe_all_always[] = { "--all", "--always", NULL }; - - static const char **describe_argv[] = { describe_bare, describe_tags, - describe_contains, - describe_all_always, NULL }; - - for (d = describe_argv; *d; d++) { - struct child_process cp = CHILD_PROCESS_INIT; - prepare_submodule_repo_env(&cp.env_array); - cp.dir = sub_path; - cp.git_cmd = 1; - cp.no_stderr = 1; - - strvec_push(&cp.args, "describe"); - strvec_pushv(&cp.args, *d); - strvec_push(&cp.args, object_id); - - if (!capture_command(&cp, &sb, 0)) { - strbuf_strip_suffix(&sb, "\n"); - return strbuf_detach(&sb, NULL); - } - } - - strbuf_release(&sb); - return NULL; -} - -struct module_list { - const struct cache_entry **entries; - int alloc, nr; -}; -#define MODULE_LIST_INIT { NULL, 0, 0 } - -static int module_list_compute(int argc, const char **argv, - const char *prefix, - struct pathspec *pathspec, - struct module_list *list) -{ - int i, result = 0; - char *ps_matched = NULL; - parse_pathspec(pathspec, 0, - PATHSPEC_PREFER_FULL, - prefix, argv); - - if (pathspec->nr) - ps_matched = xcalloc(pathspec->nr, 1); - - if (read_cache() < 0) - die(_("index file corrupt")); - - for (i = 0; i < active_nr; i++) { - const struct cache_entry *ce = active_cache[i]; - - if (!match_pathspec(&the_index, pathspec, ce->name, ce_namelen(ce), - 0, ps_matched, 1) || - !S_ISGITLINK(ce->ce_mode)) - continue; - - ALLOC_GROW(list->entries, list->nr + 1, list->alloc); - list->entries[list->nr++] = ce; - while (i + 1 < active_nr && - !strcmp(ce->name, active_cache[i + 1]->name)) - /* - * Skip entries with the same name in different stages - * to make sure an entry is returned only once. - */ - i++; - } - - if (ps_matched && report_path_error(ps_matched, pathspec)) - result = -1; - - free(ps_matched); - - return result; -} - -static void module_list_active(struct module_list *list) -{ - int i; - struct module_list active_modules = MODULE_LIST_INIT; - - for (i = 0; i < list->nr; i++) { - const struct cache_entry *ce = list->entries[i]; - - if (!is_submodule_active(the_repository, ce->name)) - continue; - - ALLOC_GROW(active_modules.entries, - active_modules.nr + 1, - active_modules.alloc); - active_modules.entries[active_modules.nr++] = ce; - } - - free(list->entries); - *list = active_modules; -} - -static char *get_up_path(const char *path) -{ - int i; - struct strbuf sb = STRBUF_INIT; - - for (i = count_slashes(path); i; i--) - strbuf_addstr(&sb, "../"); - - /* - * Check if 'path' ends with slash or not - * for having the same output for dir/sub_dir - * and dir/sub_dir/ - */ - if (!is_dir_sep(path[strlen(path) - 1])) - strbuf_addstr(&sb, "../"); - - return strbuf_detach(&sb, NULL); -} - -static int module_list(int argc, const char **argv, const char *prefix) -{ - int i; - struct pathspec pathspec; - struct module_list list = MODULE_LIST_INIT; - - struct option module_list_options[] = { - OPT_STRING(0, "prefix", &prefix, - N_("path"), - N_("alternative anchor for relative paths")), - OPT_END() - }; - - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper list [--prefix=<path>] [<path>...]"), - NULL - }; - - argc = parse_options(argc, argv, prefix, module_list_options, - git_submodule_helper_usage, 0); - - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; - - for (i = 0; i < list.nr; i++) { - const struct cache_entry *ce = list.entries[i]; - - if (ce_stage(ce)) - printf("%06o %s U\t", ce->ce_mode, oid_to_hex(&null_oid)); - else - printf("%06o %s %d\t", ce->ce_mode, - oid_to_hex(&ce->oid), ce_stage(ce)); - - fprintf(stdout, "%s\n", ce->name); - } - return 0; -} - -static void for_each_listed_submodule(const struct module_list *list, - each_submodule_fn fn, void *cb_data) -{ - int i; - for (i = 0; i < list->nr; i++) - fn(list->entries[i], cb_data); -} - -struct foreach_cb { - int argc; - const char **argv; - const char *prefix; - int quiet; - int recursive; -}; -#define FOREACH_CB_INIT { 0 } - -static void runcommand_in_submodule_cb(const struct cache_entry *list_item, - void *cb_data) -{ - struct foreach_cb *info = cb_data; - const char *path = list_item->name; - const struct object_id *ce_oid = &list_item->oid; - - const struct submodule *sub; - struct child_process cp = CHILD_PROCESS_INIT; - char *displaypath; - - displaypath = get_submodule_displaypath(path, info->prefix); - - sub = submodule_from_path(the_repository, &null_oid, path); - - if (!sub) - die(_("No url found for submodule path '%s' in .gitmodules"), - displaypath); - - if (!is_submodule_populated_gently(path, NULL)) - goto cleanup; - - prepare_submodule_repo_env(&cp.env_array); - - /* - * For the purpose of executing <command> in the submodule, - * separate shell is used for the purpose of running the - * child process. - */ - cp.use_shell = 1; - cp.dir = path; - - /* - * NEEDSWORK: the command currently has access to the variables $name, - * $sm_path, $displaypath, $sha1 and $toplevel only when the command - * contains a single argument. This is done for maintaining a faithful - * translation from shell script. - */ - if (info->argc == 1) { - char *toplevel = xgetcwd(); - struct strbuf sb = STRBUF_INIT; - - strvec_pushf(&cp.env_array, "name=%s", sub->name); - strvec_pushf(&cp.env_array, "sm_path=%s", path); - strvec_pushf(&cp.env_array, "displaypath=%s", displaypath); - strvec_pushf(&cp.env_array, "sha1=%s", - oid_to_hex(ce_oid)); - strvec_pushf(&cp.env_array, "toplevel=%s", toplevel); - - /* - * Since the path variable was accessible from the script - * before porting, it is also made available after porting. - * The environment variable "PATH" has a very special purpose - * on windows. And since environment variables are - * case-insensitive in windows, it interferes with the - * existing PATH variable. Hence, to avoid that, we expose - * path via the args strvec and not via env_array. - */ - sq_quote_buf(&sb, path); - strvec_pushf(&cp.args, "path=%s; %s", - sb.buf, info->argv[0]); - strbuf_release(&sb); - free(toplevel); - } else { - strvec_pushv(&cp.args, info->argv); - } - - if (!info->quiet) - printf(_("Entering '%s'\n"), displaypath); - - if (info->argv[0] && run_command(&cp)) - die(_("run_command returned non-zero status for %s\n."), - displaypath); - - if (info->recursive) { - struct child_process cpr = CHILD_PROCESS_INIT; - - cpr.git_cmd = 1; - cpr.dir = path; - prepare_submodule_repo_env(&cpr.env_array); - - strvec_pushl(&cpr.args, "--super-prefix", NULL); - strvec_pushf(&cpr.args, "%s/", displaypath); - strvec_pushl(&cpr.args, "submodule--helper", "foreach", "--recursive", - NULL); - - if (info->quiet) - strvec_push(&cpr.args, "--quiet"); - - strvec_push(&cpr.args, "--"); - strvec_pushv(&cpr.args, info->argv); - - if (run_command(&cpr)) - die(_("run_command returned non-zero status while " - "recursing in the nested submodules of %s\n."), - displaypath); - } - -cleanup: - free(displaypath); -} - -static int module_foreach(int argc, const char **argv, const char *prefix) -{ - struct foreach_cb info = FOREACH_CB_INIT; - struct pathspec pathspec; - struct module_list list = MODULE_LIST_INIT; - - struct option module_foreach_options[] = { - OPT__QUIET(&info.quiet, N_("Suppress output of entering each submodule command")), - OPT_BOOL(0, "recursive", &info.recursive, - N_("Recurse into nested submodules")), - OPT_END() - }; - - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper foreach [--quiet] [--recursive] [--] <command>"), - NULL - }; - - argc = parse_options(argc, argv, prefix, module_foreach_options, - git_submodule_helper_usage, 0); - - if (module_list_compute(0, NULL, prefix, &pathspec, &list) < 0) - return 1; - - info.argc = argc; - info.argv = argv; - info.prefix = prefix; - - for_each_listed_submodule(&list, runcommand_in_submodule_cb, &info); - - return 0; -} - -static char *compute_submodule_clone_url(const char *rel_url) -{ - char *remoteurl, *relurl; - char *remote = get_default_remote(); - struct strbuf remotesb = STRBUF_INIT; - - strbuf_addf(&remotesb, "remote.%s.url", remote); - if (git_config_get_string(remotesb.buf, &remoteurl)) { - warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf); - remoteurl = xgetcwd(); - } - relurl = relative_url(remoteurl, rel_url, NULL); - - free(remote); - free(remoteurl); - strbuf_release(&remotesb); - - return relurl; -} - -struct init_cb { - const char *prefix; - unsigned int flags; -}; -#define INIT_CB_INIT { NULL, 0 } - -static void init_submodule(const char *path, const char *prefix, - unsigned int flags) -{ - const struct submodule *sub; - struct strbuf sb = STRBUF_INIT; - char *upd = NULL, *url = NULL, *displaypath; - - displaypath = get_submodule_displaypath(path, prefix); - - sub = submodule_from_path(the_repository, &null_oid, path); - - if (!sub) - die(_("No url found for submodule path '%s' in .gitmodules"), - displaypath); - - /* - * NEEDSWORK: In a multi-working-tree world, this needs to be - * set in the per-worktree config. - * - * Set active flag for the submodule being initialized - */ - if (!is_submodule_active(the_repository, path)) { - strbuf_addf(&sb, "submodule.%s.active", sub->name); - git_config_set_gently(sb.buf, "true"); - strbuf_reset(&sb); - } - - /* - * Copy url setting when it is not set yet. - * To look up the url in .git/config, we must not fall back to - * .gitmodules, so look it up directly. - */ - strbuf_addf(&sb, "submodule.%s.url", sub->name); - if (git_config_get_string(sb.buf, &url)) { - if (!sub->url) - die(_("No url found for submodule path '%s' in .gitmodules"), - displaypath); - - url = xstrdup(sub->url); - - /* Possibly a url relative to parent */ - if (starts_with_dot_dot_slash(url) || - starts_with_dot_slash(url)) { - char *oldurl = url; - url = compute_submodule_clone_url(oldurl); - free(oldurl); - } - - if (git_config_set_gently(sb.buf, url)) - die(_("Failed to register url for submodule path '%s'"), - displaypath); - if (!(flags & OPT_QUIET)) - fprintf(stderr, - _("Submodule '%s' (%s) registered for path '%s'\n"), - sub->name, url, displaypath); - } - strbuf_reset(&sb); - - /* Copy "update" setting when it is not set yet */ - strbuf_addf(&sb, "submodule.%s.update", sub->name); - if (git_config_get_string(sb.buf, &upd) && - sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { - if (sub->update_strategy.type == SM_UPDATE_COMMAND) { - fprintf(stderr, _("warning: command update mode suggested for submodule '%s'\n"), - sub->name); - upd = xstrdup("none"); - } else - upd = xstrdup(submodule_strategy_to_string(&sub->update_strategy)); - - if (git_config_set_gently(sb.buf, upd)) - die(_("Failed to register update mode for submodule path '%s'"), displaypath); - } - strbuf_release(&sb); - free(displaypath); - free(url); - free(upd); -} - -static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data) -{ - struct init_cb *info = cb_data; - init_submodule(list_item->name, info->prefix, info->flags); -} - -static int module_init(int argc, const char **argv, const char *prefix) -{ - struct init_cb info = INIT_CB_INIT; - struct pathspec pathspec; - struct module_list list = MODULE_LIST_INIT; - int quiet = 0; - - struct option module_init_options[] = { - OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")), - OPT_END() - }; - - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper init [<options>] [<path>]"), - NULL - }; - - argc = parse_options(argc, argv, prefix, module_init_options, - git_submodule_helper_usage, 0); - - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; - - /* - * If there are no path args and submodule.active is set then, - * by default, only initialize 'active' modules. - */ - if (!argc && git_config_get_value_multi("submodule.active")) - module_list_active(&list); - - info.prefix = prefix; - if (quiet) - info.flags |= OPT_QUIET; - - for_each_listed_submodule(&list, init_submodule_cb, &info); - - return 0; -} - -struct status_cb { - const char *prefix; - unsigned int flags; -}; -#define STATUS_CB_INIT { NULL, 0 } - -static void print_status(unsigned int flags, char state, const char *path, - const struct object_id *oid, const char *displaypath) -{ - if (flags & OPT_QUIET) - return; - - printf("%c%s %s", state, oid_to_hex(oid), displaypath); - - if (state == ' ' || state == '+') { - const char *name = compute_rev_name(path, oid_to_hex(oid)); - - if (name) - printf(" (%s)", name); - } - - printf("\n"); -} - -static int handle_submodule_head_ref(const char *refname, - const struct object_id *oid, int flags, - void *cb_data) -{ - struct object_id *output = cb_data; - if (oid) - oidcpy(output, oid); - - return 0; -} - -static void status_submodule(const char *path, const struct object_id *ce_oid, - unsigned int ce_flags, const char *prefix, - unsigned int flags) -{ - char *displaypath; - struct strvec diff_files_args = STRVEC_INIT; - struct rev_info rev; - int diff_files_result; - struct strbuf buf = STRBUF_INIT; - const char *git_dir; - - if (!submodule_from_path(the_repository, &null_oid, path)) - die(_("no submodule mapping found in .gitmodules for path '%s'"), - path); - - displaypath = get_submodule_displaypath(path, prefix); - - if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) { - print_status(flags, 'U', path, &null_oid, displaypath); - goto cleanup; - } - - strbuf_addf(&buf, "%s/.git", path); - git_dir = read_gitfile(buf.buf); - if (!git_dir) - git_dir = buf.buf; - - if (!is_submodule_active(the_repository, path) || - !is_git_directory(git_dir)) { - print_status(flags, '-', path, ce_oid, displaypath); - strbuf_release(&buf); - goto cleanup; - } - strbuf_release(&buf); - - strvec_pushl(&diff_files_args, "diff-files", - "--ignore-submodules=dirty", "--quiet", "--", - path, NULL); - - git_config(git_diff_basic_config, NULL); - - repo_init_revisions(the_repository, &rev, NULL); - rev.abbrev = 0; - diff_files_args.nr = setup_revisions(diff_files_args.nr, - diff_files_args.v, - &rev, NULL); - diff_files_result = run_diff_files(&rev, 0); - - if (!diff_result_code(&rev.diffopt, diff_files_result)) { - print_status(flags, ' ', path, ce_oid, - displaypath); - } else if (!(flags & OPT_CACHED)) { - struct object_id oid; - struct ref_store *refs = get_submodule_ref_store(path); - - if (!refs) { - print_status(flags, '-', path, ce_oid, displaypath); - goto cleanup; - } - if (refs_head_ref(refs, handle_submodule_head_ref, &oid)) - die(_("could not resolve HEAD ref inside the " - "submodule '%s'"), path); - - print_status(flags, '+', path, &oid, displaypath); - } else { - print_status(flags, '+', path, ce_oid, displaypath); - } - - if (flags & OPT_RECURSIVE) { - struct child_process cpr = CHILD_PROCESS_INIT; - - cpr.git_cmd = 1; - cpr.dir = path; - prepare_submodule_repo_env(&cpr.env_array); - - strvec_push(&cpr.args, "--super-prefix"); - strvec_pushf(&cpr.args, "%s/", displaypath); - strvec_pushl(&cpr.args, "submodule--helper", "status", - "--recursive", NULL); - - if (flags & OPT_CACHED) - strvec_push(&cpr.args, "--cached"); - - if (flags & OPT_QUIET) - strvec_push(&cpr.args, "--quiet"); - - if (run_command(&cpr)) - die(_("failed to recurse into submodule '%s'"), path); - } - -cleanup: - strvec_clear(&diff_files_args); - free(displaypath); -} - -static void status_submodule_cb(const struct cache_entry *list_item, - void *cb_data) -{ - struct status_cb *info = cb_data; - status_submodule(list_item->name, &list_item->oid, list_item->ce_flags, - info->prefix, info->flags); -} - -static int module_status(int argc, const char **argv, const char *prefix) -{ - struct status_cb info = STATUS_CB_INIT; - struct pathspec pathspec; - struct module_list list = MODULE_LIST_INIT; - int quiet = 0; - - struct option module_status_options[] = { - OPT__QUIET(&quiet, N_("Suppress submodule status output")), - OPT_BIT(0, "cached", &info.flags, N_("Use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED), - OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE), - OPT_END() - }; - - const char *const git_submodule_helper_usage[] = { - N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"), - NULL - }; - - argc = parse_options(argc, argv, prefix, module_status_options, - git_submodule_helper_usage, 0); - - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; - - info.prefix = prefix; - if (quiet) - info.flags |= OPT_QUIET; - - for_each_listed_submodule(&list, status_submodule_cb, &info); - - return 0; -} - -static int module_name(int argc, const char **argv, const char *prefix) -{ - const struct submodule *sub; - - if (argc != 2) - usage(_("git submodule--helper name <path>")); - - sub = submodule_from_path(the_repository, &null_oid, argv[1]); - - if (!sub) - die(_("no submodule mapping found in .gitmodules for path '%s'"), - argv[1]); - - printf("%s\n", sub->name); - - return 0; -} - -struct module_cb { - unsigned int mod_src; - unsigned int mod_dst; - struct object_id oid_src; - struct object_id oid_dst; - char status; - const char *sm_path; -}; -#define MODULE_CB_INIT { 0, 0, NULL, NULL, '\0', NULL } - -struct module_cb_list { - struct module_cb **entries; - int alloc, nr; -}; -#define MODULE_CB_LIST_INIT { NULL, 0, 0 } - -struct summary_cb { - int argc; - const char **argv; - const char *prefix; - unsigned int cached: 1; - unsigned int for_status: 1; - unsigned int files: 1; - int summary_limit; -}; -#define SUMMARY_CB_INIT { 0, NULL, NULL, 0, 0, 0, 0 } - -enum diff_cmd { - DIFF_INDEX, - DIFF_FILES -}; - -static char *verify_submodule_committish(const char *sm_path, - const char *committish) -{ - struct child_process cp_rev_parse = CHILD_PROCESS_INIT; - struct strbuf result = STRBUF_INIT; - - cp_rev_parse.git_cmd = 1; - cp_rev_parse.dir = sm_path; - prepare_submodule_repo_env(&cp_rev_parse.env_array); - strvec_pushl(&cp_rev_parse.args, "rev-parse", "-q", "--short", NULL); - strvec_pushf(&cp_rev_parse.args, "%s^0", committish); - strvec_push(&cp_rev_parse.args, "--"); - - if (capture_command(&cp_rev_parse, &result, 0)) - return NULL; - - strbuf_trim_trailing_newline(&result); - return strbuf_detach(&result, NULL); -} - -static void print_submodule_summary(struct summary_cb *info, char *errmsg, - int total_commits, const char *displaypath, - const char *src_abbrev, const char *dst_abbrev, - struct module_cb *p) -{ - if (p->status == 'T') { - if (S_ISGITLINK(p->mod_dst)) - printf(_("* %s %s(blob)->%s(submodule)"), - displaypath, src_abbrev, dst_abbrev); - else - printf(_("* %s %s(submodule)->%s(blob)"), - displaypath, src_abbrev, dst_abbrev); - } else { - printf("* %s %s...%s", - displaypath, src_abbrev, dst_abbrev); - } - - if (total_commits < 0) - printf(":\n"); - else - printf(" (%d):\n", total_commits); - - if (errmsg) { - printf(_("%s"), errmsg); - } else if (total_commits > 0) { - struct child_process cp_log = CHILD_PROCESS_INIT; - - cp_log.git_cmd = 1; - cp_log.dir = p->sm_path; - prepare_submodule_repo_env(&cp_log.env_array); - strvec_pushl(&cp_log.args, "log", NULL); - - if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst)) { - if (info->summary_limit > 0) - strvec_pushf(&cp_log.args, "-%d", - info->summary_limit); - - strvec_pushl(&cp_log.args, "--pretty= %m %s", - "--first-parent", NULL); - strvec_pushf(&cp_log.args, "%s...%s", - src_abbrev, dst_abbrev); - } else if (S_ISGITLINK(p->mod_dst)) { - strvec_pushl(&cp_log.args, "--pretty= > %s", - "-1", dst_abbrev, NULL); - } else { - strvec_pushl(&cp_log.args, "--pretty= < %s", - "-1", src_abbrev, NULL); - } - run_command(&cp_log); - } - printf("\n"); -} - -static void generate_submodule_summary(struct summary_cb *info, - struct module_cb *p) -{ - char *displaypath, *src_abbrev = NULL, *dst_abbrev; - int missing_src = 0, missing_dst = 0; - char *errmsg = NULL; - int total_commits = -1; - - if (!info->cached && oideq(&p->oid_dst, &null_oid)) { - if (S_ISGITLINK(p->mod_dst)) { - struct ref_store *refs = get_submodule_ref_store(p->sm_path); - if (refs) - refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst); - } else if (S_ISLNK(p->mod_dst) || S_ISREG(p->mod_dst)) { - struct stat st; - int fd = open(p->sm_path, O_RDONLY); - - if (fd < 0 || fstat(fd, &st) < 0 || - index_fd(&the_index, &p->oid_dst, fd, &st, OBJ_BLOB, - p->sm_path, 0)) - error(_("couldn't hash object from '%s'"), p->sm_path); - } else { - /* for a submodule removal (mode:0000000), don't warn */ - if (p->mod_dst) - warning(_("unexpected mode %o\n"), p->mod_dst); - } - } - - if (S_ISGITLINK(p->mod_src)) { - if (p->status != 'D') - src_abbrev = verify_submodule_committish(p->sm_path, - oid_to_hex(&p->oid_src)); - if (!src_abbrev) { - missing_src = 1; - /* - * As `rev-parse` failed, we fallback to getting - * the abbreviated hash using oid_src. We do - * this as we might still need the abbreviated - * hash in cases like a submodule type change, etc. - */ - src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7); - } - } else { - /* - * The source does not point to a submodule. - * So, we fallback to getting the abbreviation using - * oid_src as we might still need the abbreviated - * hash in cases like submodule add, etc. - */ - src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7); - } - - if (S_ISGITLINK(p->mod_dst)) { - dst_abbrev = verify_submodule_committish(p->sm_path, - oid_to_hex(&p->oid_dst)); - if (!dst_abbrev) { - missing_dst = 1; - /* - * As `rev-parse` failed, we fallback to getting - * the abbreviated hash using oid_dst. We do - * this as we might still need the abbreviated - * hash in cases like a submodule type change, etc. - */ - dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7); - } - } else { - /* - * The destination does not point to a submodule. - * So, we fallback to getting the abbreviation using - * oid_dst as we might still need the abbreviated - * hash in cases like a submodule removal, etc. - */ - dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7); - } - - displaypath = get_submodule_displaypath(p->sm_path, info->prefix); - - if (!missing_src && !missing_dst) { - struct child_process cp_rev_list = CHILD_PROCESS_INIT; - struct strbuf sb_rev_list = STRBUF_INIT; - - strvec_pushl(&cp_rev_list.args, "rev-list", - "--first-parent", "--count", NULL); - if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst)) - strvec_pushf(&cp_rev_list.args, "%s...%s", - src_abbrev, dst_abbrev); - else - strvec_push(&cp_rev_list.args, S_ISGITLINK(p->mod_src) ? - src_abbrev : dst_abbrev); - strvec_push(&cp_rev_list.args, "--"); - - cp_rev_list.git_cmd = 1; - cp_rev_list.dir = p->sm_path; - prepare_submodule_repo_env(&cp_rev_list.env_array); - - if (!capture_command(&cp_rev_list, &sb_rev_list, 0)) - total_commits = atoi(sb_rev_list.buf); - - strbuf_release(&sb_rev_list); - } else { - /* - * Don't give error msg for modification whose dst is not - * submodule, i.e., deleted or changed to blob - */ - if (S_ISGITLINK(p->mod_dst)) { - struct strbuf errmsg_str = STRBUF_INIT; - if (missing_src && missing_dst) { - strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commits %s and %s\n", - displaypath, oid_to_hex(&p->oid_src), - oid_to_hex(&p->oid_dst)); - } else { - strbuf_addf(&errmsg_str, " Warn: %s doesn't contain commit %s\n", - displaypath, missing_src ? - oid_to_hex(&p->oid_src) : - oid_to_hex(&p->oid_dst)); - } - errmsg = strbuf_detach(&errmsg_str, NULL); - } - } - - print_submodule_summary(info, errmsg, total_commits, - displaypath, src_abbrev, - dst_abbrev, p); - - free(displaypath); - free(src_abbrev); - free(dst_abbrev); -} - -static void prepare_submodule_summary(struct summary_cb *info, - struct module_cb_list *list) -{ - int i; - for (i = 0; i < list->nr; i++) { - const struct submodule *sub; - struct module_cb *p = list->entries[i]; - struct strbuf sm_gitdir = STRBUF_INIT; - - if (p->status == 'D' || p->status == 'T') { - generate_submodule_summary(info, p); - continue; - } - - if (info->for_status && p->status != 'A' && - (sub = submodule_from_path(the_repository, - &null_oid, p->sm_path))) { - char *config_key = NULL; - const char *value; - int ignore_all = 0; - - config_key = xstrfmt("submodule.%s.ignore", - sub->name); - if (!git_config_get_string_tmp(config_key, &value)) - ignore_all = !strcmp(value, "all"); - else if (sub->ignore) - ignore_all = !strcmp(sub->ignore, "all"); - - free(config_key); - if (ignore_all) - continue; - } - - /* Also show added or modified modules which are checked out */ - strbuf_addstr(&sm_gitdir, p->sm_path); - if (is_nonbare_repository_dir(&sm_gitdir)) - generate_submodule_summary(info, p); - strbuf_release(&sm_gitdir); - } -} - -static void submodule_summary_callback(struct diff_queue_struct *q, - struct diff_options *options, - void *data) -{ - int i; - struct module_cb_list *list = data; - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - struct module_cb *temp; - - if (!S_ISGITLINK(p->one->mode) && !S_ISGITLINK(p->two->mode)) - continue; - temp = (struct module_cb*)malloc(sizeof(struct module_cb)); - temp->mod_src = p->one->mode; - temp->mod_dst = p->two->mode; - temp->oid_src = p->one->oid; - temp->oid_dst = p->two->oid; - temp->status = p->status; - temp->sm_path = xstrdup(p->one->path); - - ALLOC_GROW(list->entries, list->nr + 1, list->alloc); - list->entries[list->nr++] = temp; - } -} - -static const char *get_diff_cmd(enum diff_cmd diff_cmd) -{ - switch (diff_cmd) { - case DIFF_INDEX: return "diff-index"; - case DIFF_FILES: return "diff-files"; - default: BUG("bad diff_cmd value %d", diff_cmd); - } -} - -static int compute_summary_module_list(struct object_id *head_oid, - struct summary_cb *info, - enum diff_cmd diff_cmd) -{ - struct strvec diff_args = STRVEC_INIT; - struct rev_info rev; - struct module_cb_list list = MODULE_CB_LIST_INIT; - - strvec_push(&diff_args, get_diff_cmd(diff_cmd)); - if (info->cached) - strvec_push(&diff_args, "--cached"); - strvec_pushl(&diff_args, "--ignore-submodules=dirty", "--raw", NULL); - if (head_oid) - strvec_push(&diff_args, oid_to_hex(head_oid)); - strvec_push(&diff_args, "--"); - if (info->argc) - strvec_pushv(&diff_args, info->argv); - - git_config(git_diff_basic_config, NULL); - init_revisions(&rev, info->prefix); - rev.abbrev = 0; - precompose_argv(diff_args.nr, diff_args.v); - setup_revisions(diff_args.nr, diff_args.v, &rev, NULL); - rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK; - rev.diffopt.format_callback = submodule_summary_callback; - rev.diffopt.format_callback_data = &list; - - if (!info->cached) { - if (diff_cmd == DIFF_INDEX) - 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; - } - - if (diff_cmd == DIFF_INDEX) - run_diff_index(&rev, info->cached); - else - run_diff_files(&rev, 0); - prepare_submodule_summary(info, &list); - strvec_clear(&diff_args); - return 0; -} - -static int module_summary(int argc, const char **argv, const char *prefix) -{ - struct summary_cb info = SUMMARY_CB_INIT; - int cached = 0; - int for_status = 0; - int files = 0; - int summary_limit = -1; - enum diff_cmd diff_cmd = DIFF_INDEX; - struct object_id head_oid; - int ret; - - struct option module_summary_options[] = { - OPT_BOOL(0, "cached", &cached, - N_("use the commit stored in the index instead of the submodule HEAD")), - OPT_BOOL(0, "files", &files, - N_("to compare the commit in the index with that in the submodule HEAD")), - OPT_BOOL(0, "for-status", &for_status, - N_("skip submodules with 'ignore_config' value set to 'all'")), - OPT_INTEGER('n', "summary-limit", &summary_limit, - N_("limit the summary size")), - OPT_END() - }; - - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper summary [<options>] [<commit>] [--] [<path>]"), - NULL - }; - - argc = parse_options(argc, argv, prefix, module_summary_options, - git_submodule_helper_usage, 0); - - if (!summary_limit) - return 0; - - if (!get_oid(argc ? argv[0] : "HEAD", &head_oid)) { - if (argc) { - argv++; - argc--; - } - } else if (!argc || !strcmp(argv[0], "HEAD")) { - /* before the first commit: compare with an empty tree */ - oidcpy(&head_oid, the_hash_algo->empty_tree); - if (argc) { - argv++; - argc--; - } - } else { - if (get_oid("HEAD", &head_oid)) - die(_("could not fetch a revision for HEAD")); - } - - if (files) { - if (cached) - die(_("--cached and --files are mutually exclusive")); - diff_cmd = DIFF_FILES; - } - - info.argc = argc; - info.argv = argv; - info.prefix = prefix; - info.cached = !!cached; - info.files = !!files; - info.for_status = !!for_status; - info.summary_limit = summary_limit; - - ret = compute_summary_module_list((diff_cmd == DIFF_INDEX) ? &head_oid : NULL, - &info, diff_cmd); - return ret; -} - -struct sync_cb { - const char *prefix; - unsigned int flags; -}; -#define SYNC_CB_INIT { NULL, 0 } - -static void sync_submodule(const char *path, const char *prefix, - unsigned int flags) -{ - const struct submodule *sub; - char *remote_key = NULL; - char *sub_origin_url, *super_config_url, *displaypath; - struct strbuf sb = STRBUF_INIT; - struct child_process cp = CHILD_PROCESS_INIT; - char *sub_config_path = NULL; - - if (!is_submodule_active(the_repository, path)) - return; - - sub = submodule_from_path(the_repository, &null_oid, path); - - if (sub && sub->url) { - if (starts_with_dot_dot_slash(sub->url) || - starts_with_dot_slash(sub->url)) { - char *remote_url, *up_path; - char *remote = get_default_remote(); - strbuf_addf(&sb, "remote.%s.url", remote); - - if (git_config_get_string(sb.buf, &remote_url)) - remote_url = xgetcwd(); - - up_path = get_up_path(path); - sub_origin_url = relative_url(remote_url, sub->url, up_path); - super_config_url = relative_url(remote_url, sub->url, NULL); - - free(remote); - free(up_path); - free(remote_url); - } else { - sub_origin_url = xstrdup(sub->url); - super_config_url = xstrdup(sub->url); - } - } else { - sub_origin_url = xstrdup(""); - super_config_url = xstrdup(""); - } - - displaypath = get_submodule_displaypath(path, prefix); - - if (!(flags & OPT_QUIET)) - printf(_("Synchronizing submodule url for '%s'\n"), - displaypath); - - strbuf_reset(&sb); - strbuf_addf(&sb, "submodule.%s.url", sub->name); - if (git_config_set_gently(sb.buf, super_config_url)) - die(_("failed to register url for submodule path '%s'"), - displaypath); - - if (!is_submodule_populated_gently(path, NULL)) - goto cleanup; - - prepare_submodule_repo_env(&cp.env_array); - cp.git_cmd = 1; - cp.dir = path; - strvec_pushl(&cp.args, "submodule--helper", - "print-default-remote", NULL); - - strbuf_reset(&sb); - if (capture_command(&cp, &sb, 0)) - die(_("failed to get the default remote for submodule '%s'"), - path); - - strbuf_strip_suffix(&sb, "\n"); - remote_key = xstrfmt("remote.%s.url", sb.buf); - - strbuf_reset(&sb); - submodule_to_gitdir(&sb, path); - strbuf_addstr(&sb, "/config"); - - if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url)) - die(_("failed to update remote for submodule '%s'"), - path); - - if (flags & OPT_RECURSIVE) { - struct child_process cpr = CHILD_PROCESS_INIT; - - cpr.git_cmd = 1; - cpr.dir = path; - prepare_submodule_repo_env(&cpr.env_array); - - strvec_push(&cpr.args, "--super-prefix"); - strvec_pushf(&cpr.args, "%s/", displaypath); - strvec_pushl(&cpr.args, "submodule--helper", "sync", - "--recursive", NULL); - - if (flags & OPT_QUIET) - strvec_push(&cpr.args, "--quiet"); - - if (run_command(&cpr)) - die(_("failed to recurse into submodule '%s'"), - path); - } - -cleanup: - free(super_config_url); - free(sub_origin_url); - strbuf_release(&sb); - free(remote_key); - free(displaypath); - free(sub_config_path); -} - -static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data) -{ - struct sync_cb *info = cb_data; - sync_submodule(list_item->name, info->prefix, info->flags); -} - -static int module_sync(int argc, const char **argv, const char *prefix) -{ - struct sync_cb info = SYNC_CB_INIT; - struct pathspec pathspec; - struct module_list list = MODULE_LIST_INIT; - int quiet = 0; - int recursive = 0; - - struct option module_sync_options[] = { - OPT__QUIET(&quiet, N_("Suppress output of synchronizing submodule url")), - OPT_BOOL(0, "recursive", &recursive, - N_("Recurse into nested submodules")), - OPT_END() - }; - - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper sync [--quiet] [--recursive] [<path>]"), - NULL - }; - - argc = parse_options(argc, argv, prefix, module_sync_options, - git_submodule_helper_usage, 0); - - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; - - info.prefix = prefix; - if (quiet) - info.flags |= OPT_QUIET; - if (recursive) - info.flags |= OPT_RECURSIVE; - - for_each_listed_submodule(&list, sync_submodule_cb, &info); - - return 0; -} - -struct deinit_cb { - const char *prefix; - unsigned int flags; -}; -#define DEINIT_CB_INIT { NULL, 0 } - -static void deinit_submodule(const char *path, const char *prefix, - unsigned int flags) -{ - const struct submodule *sub; - char *displaypath = NULL; - struct child_process cp_config = CHILD_PROCESS_INIT; - struct strbuf sb_config = STRBUF_INIT; - char *sub_git_dir = xstrfmt("%s/.git", path); - - sub = submodule_from_path(the_repository, &null_oid, path); - - if (!sub || !sub->name) - goto cleanup; - - displaypath = get_submodule_displaypath(path, prefix); - - /* remove the submodule work tree (unless the user already did it) */ - if (is_directory(path)) { - struct strbuf sb_rm = STRBUF_INIT; - const char *format; - - /* - * protect submodules containing a .git directory - * NEEDSWORK: instead of dying, automatically call - * absorbgitdirs and (possibly) warn. - */ - if (is_directory(sub_git_dir)) - die(_("Submodule work tree '%s' contains a .git " - "directory (use 'rm -rf' if you really want " - "to remove it including all of its history)"), - displaypath); - - if (!(flags & OPT_FORCE)) { - struct child_process cp_rm = CHILD_PROCESS_INIT; - cp_rm.git_cmd = 1; - strvec_pushl(&cp_rm.args, "rm", "-qn", - path, NULL); - - if (run_command(&cp_rm)) - die(_("Submodule work tree '%s' contains local " - "modifications; use '-f' to discard them"), - displaypath); - } - - strbuf_addstr(&sb_rm, path); - - if (!remove_dir_recursively(&sb_rm, 0)) - format = _("Cleared directory '%s'\n"); - else - format = _("Could not remove submodule work tree '%s'\n"); - - if (!(flags & OPT_QUIET)) - printf(format, displaypath); - - submodule_unset_core_worktree(sub); - - strbuf_release(&sb_rm); - } - - if (mkdir(path, 0777)) - printf(_("could not create empty submodule directory %s"), - displaypath); - - cp_config.git_cmd = 1; - strvec_pushl(&cp_config.args, "config", "--get-regexp", NULL); - strvec_pushf(&cp_config.args, "submodule.%s\\.", sub->name); - - /* remove the .git/config entries (unless the user already did it) */ - if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) { - char *sub_key = xstrfmt("submodule.%s", sub->name); - /* - * remove the whole section so we have a clean state when - * the user later decides to init this submodule again - */ - git_config_rename_section_in_file(NULL, sub_key, NULL); - if (!(flags & OPT_QUIET)) - printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"), - sub->name, sub->url, displaypath); - free(sub_key); - } - -cleanup: - free(displaypath); - free(sub_git_dir); - strbuf_release(&sb_config); -} - -static void deinit_submodule_cb(const struct cache_entry *list_item, - void *cb_data) -{ - struct deinit_cb *info = cb_data; - deinit_submodule(list_item->name, info->prefix, info->flags); -} - -static int module_deinit(int argc, const char **argv, const char *prefix) -{ - struct deinit_cb info = DEINIT_CB_INIT; - struct pathspec pathspec; - struct module_list list = MODULE_LIST_INIT; - int quiet = 0; - int force = 0; - int all = 0; - - struct option module_deinit_options[] = { - OPT__QUIET(&quiet, N_("Suppress submodule status output")), - OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes"), 0), - OPT_BOOL(0, "all", &all, N_("Unregister all submodules")), - OPT_END() - }; - - const char *const git_submodule_helper_usage[] = { - N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"), - NULL - }; - - argc = parse_options(argc, argv, prefix, module_deinit_options, - git_submodule_helper_usage, 0); - - if (all && argc) { - error("pathspec and --all are incompatible"); - usage_with_options(git_submodule_helper_usage, - module_deinit_options); - } - - if (!argc && !all) - die(_("Use '--all' if you really want to deinitialize all submodules")); - - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; - - info.prefix = prefix; - if (quiet) - info.flags |= OPT_QUIET; - if (force) - info.flags |= OPT_FORCE; - - for_each_listed_submodule(&list, deinit_submodule_cb, &info); - - return 0; -} - -static int clone_submodule(const char *path, const char *gitdir, const char *url, - const char *depth, struct string_list *reference, int dissociate, - int quiet, int progress, int single_branch) -{ - struct child_process cp = CHILD_PROCESS_INIT; - - strvec_push(&cp.args, "clone"); - strvec_push(&cp.args, "--no-checkout"); - if (quiet) - strvec_push(&cp.args, "--quiet"); - if (progress) - strvec_push(&cp.args, "--progress"); - if (depth && *depth) - strvec_pushl(&cp.args, "--depth", depth, NULL); - if (reference->nr) { - struct string_list_item *item; - for_each_string_list_item(item, reference) - strvec_pushl(&cp.args, "--reference", - item->string, NULL); - } - if (dissociate) - strvec_push(&cp.args, "--dissociate"); - if (gitdir && *gitdir) - strvec_pushl(&cp.args, "--separate-git-dir", gitdir, NULL); - if (single_branch >= 0) - strvec_push(&cp.args, single_branch ? - "--single-branch" : - "--no-single-branch"); - - strvec_push(&cp.args, "--"); - strvec_push(&cp.args, url); - strvec_push(&cp.args, path); - - cp.git_cmd = 1; - prepare_submodule_repo_env(&cp.env_array); - cp.no_stdin = 1; - - return run_command(&cp); -} - -struct submodule_alternate_setup { - const char *submodule_name; - enum SUBMODULE_ALTERNATE_ERROR_MODE { - SUBMODULE_ALTERNATE_ERROR_DIE, - SUBMODULE_ALTERNATE_ERROR_INFO, - SUBMODULE_ALTERNATE_ERROR_IGNORE - } error_mode; - struct string_list *reference; -}; -#define SUBMODULE_ALTERNATE_SETUP_INIT { NULL, \ - SUBMODULE_ALTERNATE_ERROR_IGNORE, NULL } - -static const char alternate_error_advice[] = N_( -"An alternate computed from a superproject's alternate is invalid.\n" -"To allow Git to clone without an alternate in such a case, set\n" -"submodule.alternateErrorStrategy to 'info' or, equivalently, clone with\n" -"'--reference-if-able' instead of '--reference'." -); - -static int add_possible_reference_from_superproject( - struct object_directory *odb, void *sas_cb) -{ - struct submodule_alternate_setup *sas = sas_cb; - size_t len; - - /* - * If the alternate object store is another repository, try the - * standard layout with .git/(modules/<name>)+/objects - */ - if (strip_suffix(odb->path, "/objects", &len)) { - char *sm_alternate; - struct strbuf sb = STRBUF_INIT; - struct strbuf err = STRBUF_INIT; - strbuf_add(&sb, odb->path, len); - - /* - * We need to end the new path with '/' to mark it as a dir, - * otherwise a submodule name containing '/' will be broken - * as the last part of a missing submodule reference would - * be taken as a file name. - */ - strbuf_addf(&sb, "/modules/%s/", sas->submodule_name); - - sm_alternate = compute_alternate_path(sb.buf, &err); - if (sm_alternate) { - string_list_append(sas->reference, xstrdup(sb.buf)); - free(sm_alternate); - } else { - switch (sas->error_mode) { - case SUBMODULE_ALTERNATE_ERROR_DIE: - if (advice_submodule_alternate_error_strategy_die) - advise(_(alternate_error_advice)); - die(_("submodule '%s' cannot add alternate: %s"), - sas->submodule_name, err.buf); - case SUBMODULE_ALTERNATE_ERROR_INFO: - fprintf_ln(stderr, _("submodule '%s' cannot add alternate: %s"), - sas->submodule_name, err.buf); - case SUBMODULE_ALTERNATE_ERROR_IGNORE: - ; /* nothing */ - } - } - strbuf_release(&sb); - } - - return 0; -} - -static void prepare_possible_alternates(const char *sm_name, - struct string_list *reference) -{ - char *sm_alternate = NULL, *error_strategy = NULL; - struct submodule_alternate_setup sas = SUBMODULE_ALTERNATE_SETUP_INIT; - - git_config_get_string("submodule.alternateLocation", &sm_alternate); - if (!sm_alternate) - return; - - git_config_get_string("submodule.alternateErrorStrategy", &error_strategy); - - if (!error_strategy) - error_strategy = xstrdup("die"); - - sas.submodule_name = sm_name; - sas.reference = reference; - if (!strcmp(error_strategy, "die")) - sas.error_mode = SUBMODULE_ALTERNATE_ERROR_DIE; - else if (!strcmp(error_strategy, "info")) - sas.error_mode = SUBMODULE_ALTERNATE_ERROR_INFO; - else if (!strcmp(error_strategy, "ignore")) - sas.error_mode = SUBMODULE_ALTERNATE_ERROR_IGNORE; - else - die(_("Value '%s' for submodule.alternateErrorStrategy is not recognized"), error_strategy); - - if (!strcmp(sm_alternate, "superproject")) - foreach_alt_odb(add_possible_reference_from_superproject, &sas); - else if (!strcmp(sm_alternate, "no")) - ; /* do nothing */ - else - die(_("Value '%s' for submodule.alternateLocation is not recognized"), sm_alternate); - - free(sm_alternate); - free(error_strategy); -} - -static int module_clone(int argc, const char **argv, const char *prefix) -{ - const char *name = NULL, *url = NULL, *depth = NULL; - int quiet = 0; - int progress = 0; - char *p, *path = NULL, *sm_gitdir; - struct strbuf sb = STRBUF_INIT; - struct string_list reference = STRING_LIST_INIT_NODUP; - int dissociate = 0, require_init = 0; - char *sm_alternate = NULL, *error_strategy = NULL; - int single_branch = -1; - - struct option module_clone_options[] = { - OPT_STRING(0, "prefix", &prefix, - N_("path"), - N_("alternative anchor for relative paths")), - OPT_STRING(0, "path", &path, - N_("path"), - N_("where the new submodule will be cloned to")), - OPT_STRING(0, "name", &name, - N_("string"), - N_("name of the new submodule")), - OPT_STRING(0, "url", &url, - N_("string"), - N_("url where to clone the submodule from")), - OPT_STRING_LIST(0, "reference", &reference, - N_("repo"), - N_("reference repository")), - OPT_BOOL(0, "dissociate", &dissociate, - N_("use --reference only while cloning")), - OPT_STRING(0, "depth", &depth, - N_("string"), - N_("depth for shallow clones")), - OPT__QUIET(&quiet, "Suppress output for cloning a submodule"), - OPT_BOOL(0, "progress", &progress, - N_("force cloning progress")), - OPT_BOOL(0, "require-init", &require_init, - N_("disallow cloning into non-empty directory")), - OPT_BOOL(0, "single-branch", &single_branch, - N_("clone only one branch, HEAD or --branch")), - OPT_END() - }; - - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper clone [--prefix=<path>] [--quiet] " - "[--reference <repository>] [--name <name>] [--depth <depth>] " - "[--single-branch] " - "--url <url> --path <path>"), - NULL - }; - - argc = parse_options(argc, argv, prefix, module_clone_options, - git_submodule_helper_usage, 0); - - if (argc || !url || !path || !*path) - usage_with_options(git_submodule_helper_usage, - module_clone_options); - - strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name); - sm_gitdir = absolute_pathdup(sb.buf); - strbuf_reset(&sb); - - if (!is_absolute_path(path)) { - strbuf_addf(&sb, "%s/%s", get_git_work_tree(), path); - path = strbuf_detach(&sb, NULL); - } else - path = xstrdup(path); - - if (validate_submodule_git_dir(sm_gitdir, name) < 0) - die(_("refusing to create/use '%s' in another submodule's " - "git dir"), sm_gitdir); - - if (!file_exists(sm_gitdir)) { - if (safe_create_leading_directories_const(sm_gitdir) < 0) - die(_("could not create directory '%s'"), sm_gitdir); - - prepare_possible_alternates(name, &reference); - - if (clone_submodule(path, sm_gitdir, url, depth, &reference, dissociate, - quiet, progress, single_branch)) - die(_("clone of '%s' into submodule path '%s' failed"), - url, path); - } else { - if (require_init && !access(path, X_OK) && !is_empty_dir(path)) - die(_("directory not empty: '%s'"), path); - if (safe_create_leading_directories_const(path) < 0) - die(_("could not create directory '%s'"), path); - strbuf_addf(&sb, "%s/index", sm_gitdir); - unlink_or_warn(sb.buf); - strbuf_reset(&sb); - } - - connect_work_tree_and_git_dir(path, sm_gitdir, 0); - - p = git_pathdup_submodule(path, "config"); - if (!p) - die(_("could not get submodule directory for '%s'"), path); - - /* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */ - git_config_get_string("submodule.alternateLocation", &sm_alternate); - if (sm_alternate) - git_config_set_in_file(p, "submodule.alternateLocation", - sm_alternate); - git_config_get_string("submodule.alternateErrorStrategy", &error_strategy); - if (error_strategy) - git_config_set_in_file(p, "submodule.alternateErrorStrategy", - error_strategy); - - free(sm_alternate); - free(error_strategy); - - strbuf_release(&sb); - free(sm_gitdir); - free(path); - free(p); - return 0; -} - -static void determine_submodule_update_strategy(struct repository *r, - int just_cloned, - const char *path, - const char *update, - struct submodule_update_strategy *out) -{ - const struct submodule *sub = submodule_from_path(r, &null_oid, path); - char *key; - const char *val; - - key = xstrfmt("submodule.%s.update", sub->name); - - if (update) { - if (parse_submodule_update_strategy(update, out) < 0) - die(_("Invalid update mode '%s' for submodule path '%s'"), - update, path); - } else if (!repo_config_get_string_tmp(r, key, &val)) { - if (parse_submodule_update_strategy(val, out) < 0) - die(_("Invalid update mode '%s' configured for submodule path '%s'"), - val, path); - } else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { - if (sub->update_strategy.type == SM_UPDATE_COMMAND) - BUG("how did we read update = !command from .gitmodules?"); - out->type = sub->update_strategy.type; - out->command = sub->update_strategy.command; - } else - out->type = SM_UPDATE_CHECKOUT; - - if (just_cloned && - (out->type == SM_UPDATE_MERGE || - out->type == SM_UPDATE_REBASE || - out->type == SM_UPDATE_NONE)) - out->type = SM_UPDATE_CHECKOUT; - - free(key); -} - -static int module_update_module_mode(int argc, const char **argv, const char *prefix) -{ - const char *path, *update = NULL; - int just_cloned; - struct submodule_update_strategy update_strategy = { .type = SM_UPDATE_CHECKOUT }; - - if (argc < 3 || argc > 4) - die("submodule--helper update-module-clone expects <just-cloned> <path> [<update>]"); - - just_cloned = git_config_int("just_cloned", argv[1]); - path = argv[2]; - - if (argc == 4) - update = argv[3]; - - determine_submodule_update_strategy(the_repository, - just_cloned, path, update, - &update_strategy); - fputs(submodule_strategy_to_string(&update_strategy), stdout); - - return 0; -} - -struct update_clone_data { - const struct submodule *sub; - struct object_id oid; - unsigned just_cloned; -}; - -struct submodule_update_clone { - /* index into 'list', the list of submodules to look into for cloning */ - int current; - struct module_list list; - unsigned warn_if_uninitialized : 1; - - /* update parameter passed via commandline */ - struct submodule_update_strategy update; - - /* configuration parameters which are passed on to the children */ - int progress; - int quiet; - int recommend_shallow; - struct string_list references; - int dissociate; - unsigned require_init; - const char *depth; - const char *recursive_prefix; - const char *prefix; - int single_branch; - - /* to be consumed by git-submodule.sh */ - struct update_clone_data *update_clone; - int update_clone_nr; int update_clone_alloc; - - /* If we want to stop as fast as possible and return an error */ - unsigned quickstop : 1; - - /* failed clones to be retried again */ - const struct cache_entry **failed_clones; - int failed_clones_nr, failed_clones_alloc; - - int max_jobs; -}; -#define SUBMODULE_UPDATE_CLONE_INIT { \ - .list = MODULE_LIST_INIT, \ - .update = SUBMODULE_UPDATE_STRATEGY_INIT, \ - .recommend_shallow = -1, \ - .references = STRING_LIST_INIT_DUP, \ - .single_branch = -1, \ - .max_jobs = 1, \ -} - - -static void next_submodule_warn_missing(struct submodule_update_clone *suc, - struct strbuf *out, const char *displaypath) -{ - /* - * Only mention uninitialized submodules when their - * paths have been specified. - */ - if (suc->warn_if_uninitialized) { - strbuf_addf(out, - _("Submodule path '%s' not initialized"), - displaypath); - strbuf_addch(out, '\n'); - strbuf_addstr(out, - _("Maybe you want to use 'update --init'?")); - strbuf_addch(out, '\n'); - } -} - -/** - * Determine whether 'ce' needs to be cloned. If so, prepare the 'child' to - * run the clone. Returns 1 if 'ce' needs to be cloned, 0 otherwise. - */ -static int prepare_to_clone_next_submodule(const struct cache_entry *ce, - struct child_process *child, - struct submodule_update_clone *suc, - struct strbuf *out) -{ - const struct submodule *sub = NULL; - const char *url = NULL; - const char *update_string; - enum submodule_update_type update_type; - char *key; - struct strbuf displaypath_sb = STRBUF_INIT; - struct strbuf sb = STRBUF_INIT; - const char *displaypath = NULL; - int needs_cloning = 0; - int need_free_url = 0; - - if (ce_stage(ce)) { - if (suc->recursive_prefix) - strbuf_addf(&sb, "%s/%s", suc->recursive_prefix, ce->name); - else - strbuf_addstr(&sb, ce->name); - strbuf_addf(out, _("Skipping unmerged submodule %s"), sb.buf); - strbuf_addch(out, '\n'); - goto cleanup; - } - - sub = submodule_from_path(the_repository, &null_oid, ce->name); - - if (suc->recursive_prefix) - displaypath = relative_path(suc->recursive_prefix, - ce->name, &displaypath_sb); - else - displaypath = ce->name; - - if (!sub) { - next_submodule_warn_missing(suc, out, displaypath); - goto cleanup; - } - - key = xstrfmt("submodule.%s.update", sub->name); - if (!repo_config_get_string_tmp(the_repository, key, &update_string)) { - update_type = parse_submodule_update_type(update_string); - } else { - update_type = sub->update_strategy.type; - } - free(key); - - if (suc->update.type == SM_UPDATE_NONE - || (suc->update.type == SM_UPDATE_UNSPECIFIED - && update_type == SM_UPDATE_NONE)) { - strbuf_addf(out, _("Skipping submodule '%s'"), displaypath); - strbuf_addch(out, '\n'); - goto cleanup; - } - - /* Check if the submodule has been initialized. */ - if (!is_submodule_active(the_repository, ce->name)) { - next_submodule_warn_missing(suc, out, displaypath); - goto cleanup; - } - - strbuf_reset(&sb); - strbuf_addf(&sb, "submodule.%s.url", sub->name); - if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) { - if (starts_with_dot_slash(sub->url) || - starts_with_dot_dot_slash(sub->url)) { - url = compute_submodule_clone_url(sub->url); - need_free_url = 1; - } else - url = sub->url; - } - - strbuf_reset(&sb); - strbuf_addf(&sb, "%s/.git", ce->name); - needs_cloning = !file_exists(sb.buf); - - ALLOC_GROW(suc->update_clone, suc->update_clone_nr + 1, - suc->update_clone_alloc); - oidcpy(&suc->update_clone[suc->update_clone_nr].oid, &ce->oid); - suc->update_clone[suc->update_clone_nr].just_cloned = needs_cloning; - suc->update_clone[suc->update_clone_nr].sub = sub; - suc->update_clone_nr++; - - if (!needs_cloning) - goto cleanup; - - child->git_cmd = 1; - child->no_stdin = 1; - child->stdout_to_stderr = 1; - child->err = -1; - strvec_push(&child->args, "submodule--helper"); - strvec_push(&child->args, "clone"); - if (suc->progress) - strvec_push(&child->args, "--progress"); - if (suc->quiet) - strvec_push(&child->args, "--quiet"); - if (suc->prefix) - strvec_pushl(&child->args, "--prefix", suc->prefix, NULL); - if (suc->recommend_shallow && sub->recommend_shallow == 1) - strvec_push(&child->args, "--depth=1"); - if (suc->require_init) - strvec_push(&child->args, "--require-init"); - strvec_pushl(&child->args, "--path", sub->path, NULL); - strvec_pushl(&child->args, "--name", sub->name, NULL); - strvec_pushl(&child->args, "--url", url, NULL); - if (suc->references.nr) { - struct string_list_item *item; - for_each_string_list_item(item, &suc->references) - strvec_pushl(&child->args, "--reference", item->string, NULL); - } - if (suc->dissociate) - strvec_push(&child->args, "--dissociate"); - if (suc->depth) - strvec_push(&child->args, suc->depth); - if (suc->single_branch >= 0) - strvec_push(&child->args, suc->single_branch ? - "--single-branch" : - "--no-single-branch"); - -cleanup: - strbuf_release(&displaypath_sb); - strbuf_release(&sb); - if (need_free_url) - free((void*)url); - - return needs_cloning; -} - -static int update_clone_get_next_task(struct child_process *child, - struct strbuf *err, - void *suc_cb, - void **idx_task_cb) -{ - struct submodule_update_clone *suc = suc_cb; - const struct cache_entry *ce; - int index; - - for (; suc->current < suc->list.nr; suc->current++) { - ce = suc->list.entries[suc->current]; - if (prepare_to_clone_next_submodule(ce, child, suc, err)) { - int *p = xmalloc(sizeof(*p)); - *p = suc->current; - *idx_task_cb = p; - suc->current++; - return 1; - } - } - - /* - * The loop above tried cloning each submodule once, now try the - * stragglers again, which we can imagine as an extension of the - * entry list. - */ - index = suc->current - suc->list.nr; - if (index < suc->failed_clones_nr) { - int *p; - ce = suc->failed_clones[index]; - if (!prepare_to_clone_next_submodule(ce, child, suc, err)) { - suc->current ++; - strbuf_addstr(err, "BUG: submodule considered for " - "cloning, doesn't need cloning " - "any more?\n"); - return 0; - } - p = xmalloc(sizeof(*p)); - *p = suc->current; - *idx_task_cb = p; - suc->current ++; - return 1; - } - - return 0; -} - -static int update_clone_start_failure(struct strbuf *err, - void *suc_cb, - void *idx_task_cb) -{ - struct submodule_update_clone *suc = suc_cb; - suc->quickstop = 1; - return 1; -} - -static int update_clone_task_finished(int result, - struct strbuf *err, - void *suc_cb, - void *idx_task_cb) -{ - const struct cache_entry *ce; - struct submodule_update_clone *suc = suc_cb; - - int *idxP = idx_task_cb; - int idx = *idxP; - free(idxP); - - if (!result) - return 0; - - if (idx < suc->list.nr) { - ce = suc->list.entries[idx]; - strbuf_addf(err, _("Failed to clone '%s'. Retry scheduled"), - ce->name); - strbuf_addch(err, '\n'); - ALLOC_GROW(suc->failed_clones, - suc->failed_clones_nr + 1, - suc->failed_clones_alloc); - suc->failed_clones[suc->failed_clones_nr++] = ce; - return 0; - } else { - idx -= suc->list.nr; - ce = suc->failed_clones[idx]; - strbuf_addf(err, _("Failed to clone '%s' a second time, aborting"), - ce->name); - strbuf_addch(err, '\n'); - suc->quickstop = 1; - return 1; - } - - return 0; -} - -static int git_update_clone_config(const char *var, const char *value, - void *cb) -{ - int *max_jobs = cb; - if (!strcmp(var, "submodule.fetchjobs")) - *max_jobs = parse_submodule_fetchjobs(var, value); - return 0; -} - -static void update_submodule(struct update_clone_data *ucd) -{ - fprintf(stdout, "dummy %s %d\t%s\n", - oid_to_hex(&ucd->oid), - ucd->just_cloned, - ucd->sub->path); -} - -static int update_submodules(struct submodule_update_clone *suc) -{ - int i; - - run_processes_parallel_tr2(suc->max_jobs, update_clone_get_next_task, - update_clone_start_failure, - update_clone_task_finished, suc, "submodule", - "parallel/update"); - - /* - * We saved the output and put it out all at once now. - * That means: - * - the listener does not have to interleave their (checkout) - * work with our fetching. The writes involved in a - * checkout involve more straightforward sequential I/O. - * - the listener can avoid doing any work if fetching failed. - */ - if (suc->quickstop) - return 1; - - for (i = 0; i < suc->update_clone_nr; i++) - update_submodule(&suc->update_clone[i]); - - return 0; -} - -static int update_clone(int argc, const char **argv, const char *prefix) -{ - const char *update = NULL; - struct pathspec pathspec; - struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT; - - struct option module_update_clone_options[] = { - OPT_STRING(0, "prefix", &prefix, - N_("path"), - N_("path into the working tree")), - OPT_STRING(0, "recursive-prefix", &suc.recursive_prefix, - N_("path"), - N_("path into the working tree, across nested " - "submodule boundaries")), - OPT_STRING(0, "update", &update, - N_("string"), - N_("rebase, merge, checkout or none")), - OPT_STRING_LIST(0, "reference", &suc.references, N_("repo"), - N_("reference repository")), - OPT_BOOL(0, "dissociate", &suc.dissociate, - N_("use --reference only while cloning")), - OPT_STRING(0, "depth", &suc.depth, "<depth>", - N_("Create a shallow clone truncated to the " - "specified number of revisions")), - OPT_INTEGER('j', "jobs", &suc.max_jobs, - N_("parallel jobs")), - OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow, - N_("whether the initial clone should follow the shallow recommendation")), - OPT__QUIET(&suc.quiet, N_("don't print cloning progress")), - OPT_BOOL(0, "progress", &suc.progress, - N_("force cloning progress")), - OPT_BOOL(0, "require-init", &suc.require_init, - N_("disallow cloning into non-empty directory")), - OPT_BOOL(0, "single-branch", &suc.single_branch, - N_("clone only one branch, HEAD or --branch")), - OPT_END() - }; - - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper update-clone [--prefix=<path>] [<path>...]"), - NULL - }; - suc.prefix = prefix; - - update_clone_config_from_gitmodules(&suc.max_jobs); - git_config(git_update_clone_config, &suc.max_jobs); - - argc = parse_options(argc, argv, prefix, module_update_clone_options, - git_submodule_helper_usage, 0); - - if (update) - if (parse_submodule_update_strategy(update, &suc.update) < 0) - die(_("bad value for update parameter")); - - if (module_list_compute(argc, argv, prefix, &pathspec, &suc.list) < 0) - return 1; - - if (pathspec.nr) - suc.warn_if_uninitialized = 1; - - return update_submodules(&suc); -} - -static int resolve_relative_path(int argc, const char **argv, const char *prefix) -{ - struct strbuf sb = STRBUF_INIT; - if (argc != 3) - die("submodule--helper relative-path takes exactly 2 arguments, got %d", argc); - - printf("%s", relative_path(argv[1], argv[2], &sb)); - strbuf_release(&sb); - return 0; -} - -static const char *remote_submodule_branch(const char *path) -{ - const struct submodule *sub; - const char *branch = NULL; - char *key; - - sub = submodule_from_path(the_repository, &null_oid, path); - if (!sub) - return NULL; - - key = xstrfmt("submodule.%s.branch", sub->name); - if (repo_config_get_string_tmp(the_repository, key, &branch)) - branch = sub->branch; - free(key); - - if (!branch) - return "HEAD"; - - if (!strcmp(branch, ".")) { - const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL); - - if (!refname) - die(_("No such ref: %s"), "HEAD"); - - /* detached HEAD */ - if (!strcmp(refname, "HEAD")) - die(_("Submodule (%s) branch configured to inherit " - "branch from superproject, but the superproject " - "is not on any branch"), sub->name); - - if (!skip_prefix(refname, "refs/heads/", &refname)) - die(_("Expecting a full ref name, got %s"), refname); - return refname; - } - - return branch; -} - -static int resolve_remote_submodule_branch(int argc, const char **argv, - const char *prefix) -{ - const char *ret; - struct strbuf sb = STRBUF_INIT; - if (argc != 2) - die("submodule--helper remote-branch takes exactly one arguments, got %d", argc); - - ret = remote_submodule_branch(argv[1]); - if (!ret) - die("submodule %s doesn't exist", argv[1]); - - printf("%s", ret); - strbuf_release(&sb); - return 0; -} - -static int push_check(int argc, const char **argv, const char *prefix) -{ - struct remote *remote; - const char *superproject_head; - char *head; - int detached_head = 0; - struct object_id head_oid; - - if (argc < 3) - die("submodule--helper push-check requires at least 2 arguments"); - - /* - * superproject's resolved head ref. - * if HEAD then the superproject is in a detached head state, otherwise - * it will be the resolved head ref. - */ - superproject_head = argv[1]; - argv++; - argc--; - /* Get the submodule's head ref and determine if it is detached */ - head = resolve_refdup("HEAD", 0, &head_oid, NULL); - if (!head) - die(_("Failed to resolve HEAD as a valid ref.")); - if (!strcmp(head, "HEAD")) - detached_head = 1; - - /* - * The remote must be configured. - * This is to avoid pushing to the exact same URL as the parent. - */ - remote = pushremote_get(argv[1]); - if (!remote || remote->origin == REMOTE_UNCONFIGURED) - die("remote '%s' not configured", argv[1]); - - /* Check the refspec */ - if (argc > 2) { - int i; - struct ref *local_refs = get_local_heads(); - struct refspec refspec = REFSPEC_INIT_PUSH; - - refspec_appendn(&refspec, argv + 2, argc - 2); - - for (i = 0; i < refspec.nr; i++) { - const struct refspec_item *rs = &refspec.items[i]; - - if (rs->pattern || rs->matching) - continue; - - /* LHS must match a single ref */ - switch (count_refspec_match(rs->src, local_refs, NULL)) { - case 1: - break; - case 0: - /* - * If LHS matches 'HEAD' then we need to ensure - * that it matches the same named branch - * checked out in the superproject. - */ - if (!strcmp(rs->src, "HEAD")) { - if (!detached_head && - !strcmp(head, superproject_head)) - break; - die("HEAD does not match the named branch in the superproject"); - } - /* fallthrough */ - default: - die("src refspec '%s' must name a ref", - rs->src); - } - } - refspec_clear(&refspec); - } - free(head); - - return 0; -} - -static int ensure_core_worktree(int argc, const char **argv, const char *prefix) -{ - const struct submodule *sub; - const char *path; - const char *cw; - struct repository subrepo; - - if (argc != 2) - BUG("submodule--helper ensure-core-worktree <path>"); - - path = argv[1]; - - sub = submodule_from_path(the_repository, &null_oid, path); - if (!sub) - BUG("We could get the submodule handle before?"); - - if (repo_submodule_init(&subrepo, the_repository, sub)) - die(_("could not get a repository handle for submodule '%s'"), path); - - if (!repo_config_get_string_tmp(&subrepo, "core.worktree", &cw)) { - char *cfg_file, *abs_path; - const char *rel_path; - struct strbuf sb = STRBUF_INIT; - - cfg_file = repo_git_path(&subrepo, "config"); - - abs_path = absolute_pathdup(path); - rel_path = relative_path(abs_path, subrepo.gitdir, &sb); - - git_config_set_in_file(cfg_file, "core.worktree", rel_path); - - free(cfg_file); - free(abs_path); - strbuf_release(&sb); - } - - return 0; -} - -static int absorb_git_dirs(int argc, const char **argv, const char *prefix) -{ - int i; - struct pathspec pathspec; - struct module_list list = MODULE_LIST_INIT; - unsigned flags = ABSORB_GITDIR_RECURSE_SUBMODULES; - - struct option embed_gitdir_options[] = { - OPT_STRING(0, "prefix", &prefix, - N_("path"), - N_("path into the working tree")), - OPT_BIT(0, "--recursive", &flags, N_("recurse into submodules"), - ABSORB_GITDIR_RECURSE_SUBMODULES), - OPT_END() - }; - - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper absorb-git-dirs [<options>] [<path>...]"), - NULL - }; - - argc = parse_options(argc, argv, prefix, embed_gitdir_options, - git_submodule_helper_usage, 0); - - if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) - return 1; - - for (i = 0; i < list.nr; i++) - absorb_git_dir_into_superproject(list.entries[i]->name, flags); - - return 0; -} - -static int is_active(int argc, const char **argv, const char *prefix) -{ - if (argc != 2) - die("submodule--helper is-active takes exactly 1 argument"); - - return !is_submodule_active(the_repository, argv[1]); -} - -/* - * Exit non-zero if any of the submodule names given on the command line is - * invalid. If no names are given, filter stdin to print only valid names - * (which is primarily intended for testing). - */ -static int check_name(int argc, const char **argv, const char *prefix) -{ - if (argc > 1) { - while (*++argv) { - if (check_submodule_name(*argv) < 0) - return 1; - } - } else { - struct strbuf buf = STRBUF_INIT; - while (strbuf_getline(&buf, stdin) != EOF) { - if (!check_submodule_name(buf.buf)) - printf("%s\n", buf.buf); - } - strbuf_release(&buf); - } - return 0; -} - -static int module_config(int argc, const char **argv, const char *prefix) -{ - enum { - CHECK_WRITEABLE = 1, - DO_UNSET = 2 - } command = 0; - - struct option module_config_options[] = { - OPT_CMDMODE(0, "check-writeable", &command, - N_("check if it is safe to write to the .gitmodules file"), - CHECK_WRITEABLE), - OPT_CMDMODE(0, "unset", &command, - N_("unset the config in the .gitmodules file"), - DO_UNSET), - OPT_END() - }; - const char *const git_submodule_helper_usage[] = { - N_("git submodule--helper config <name> [<value>]"), - N_("git submodule--helper config --unset <name>"), - N_("git submodule--helper config --check-writeable"), - NULL - }; - - argc = parse_options(argc, argv, prefix, module_config_options, - git_submodule_helper_usage, PARSE_OPT_KEEP_ARGV0); - - if (argc == 1 && command == CHECK_WRITEABLE) - return is_writing_gitmodules_ok() ? 0 : -1; - - /* Equivalent to ACTION_GET in builtin/config.c */ - if (argc == 2 && command != DO_UNSET) - return print_config_from_gitmodules(the_repository, argv[1]); - - /* Equivalent to ACTION_SET in builtin/config.c */ - if (argc == 3 || (argc == 2 && command == DO_UNSET)) { - const char *value = (argc == 3) ? argv[2] : NULL; - - if (!is_writing_gitmodules_ok()) - die(_("please make sure that the .gitmodules file is in the working tree")); - - return config_set_in_gitmodules_file_gently(argv[1], value); - } - - usage_with_options(git_submodule_helper_usage, module_config_options); -} - -static int module_set_url(int argc, const char **argv, const char *prefix) -{ - int quiet = 0; - const char *newurl; - const char *path; - char *config_name; - - struct option options[] = { - OPT__QUIET(&quiet, N_("Suppress output for setting url of a submodule")), - OPT_END() - }; - const char *const usage[] = { - N_("git submodule--helper set-url [--quiet] <path> <newurl>"), - NULL - }; - - argc = parse_options(argc, argv, prefix, options, usage, 0); - - if (argc != 2 || !(path = argv[0]) || !(newurl = argv[1])) - usage_with_options(usage, options); - - config_name = xstrfmt("submodule.%s.url", path); - - config_set_in_gitmodules_file_gently(config_name, newurl); - sync_submodule(path, prefix, quiet ? OPT_QUIET : 0); - - free(config_name); - - return 0; -} - -static int module_set_branch(int argc, const char **argv, const char *prefix) -{ - int opt_default = 0, ret; - const char *opt_branch = NULL; - const char *path; - char *config_name; - - /* - * We accept the `quiet` option for uniformity across subcommands, - * though there is nothing to make less verbose in this subcommand. - */ - struct option options[] = { - OPT_NOOP_NOARG('q', "quiet"), - OPT_BOOL('d', "default", &opt_default, - N_("set the default tracking branch to master")), - OPT_STRING('b', "branch", &opt_branch, N_("branch"), - N_("set the default tracking branch")), - OPT_END() - }; - const char *const usage[] = { - N_("git submodule--helper set-branch [-q|--quiet] (-d|--default) <path>"), - N_("git submodule--helper set-branch [-q|--quiet] (-b|--branch) <branch> <path>"), - NULL - }; - - argc = parse_options(argc, argv, prefix, options, usage, 0); - - if (!opt_branch && !opt_default) - die(_("--branch or --default required")); - - if (opt_branch && opt_default) - die(_("--branch and --default are mutually exclusive")); - - if (argc != 1 || !(path = argv[0])) - usage_with_options(usage, options); - - config_name = xstrfmt("submodule.%s.branch", path); - ret = config_set_in_gitmodules_file_gently(config_name, opt_branch); - - free(config_name); - return !!ret; -} - -#define SUPPORT_SUPER_PREFIX (1<<0) - -struct cmd_struct { - const char *cmd; - int (*fn)(int, const char **, const char *); - unsigned option; -}; - -static struct cmd_struct commands[] = { - {"list", module_list, 0}, - {"name", module_name, 0}, - {"clone", module_clone, 0}, - {"update-module-mode", module_update_module_mode, 0}, - {"update-clone", update_clone, 0}, - {"ensure-core-worktree", ensure_core_worktree, 0}, - {"relative-path", resolve_relative_path, 0}, - {"resolve-relative-url", resolve_relative_url, 0}, - {"resolve-relative-url-test", resolve_relative_url_test, 0}, - {"foreach", module_foreach, SUPPORT_SUPER_PREFIX}, - {"init", module_init, SUPPORT_SUPER_PREFIX}, - {"status", module_status, SUPPORT_SUPER_PREFIX}, - {"print-default-remote", print_default_remote, 0}, - {"sync", module_sync, SUPPORT_SUPER_PREFIX}, - {"deinit", module_deinit, 0}, - {"summary", module_summary, SUPPORT_SUPER_PREFIX}, - {"remote-branch", resolve_remote_submodule_branch, 0}, - {"push-check", push_check, 0}, - {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX}, - {"is-active", is_active, 0}, - {"check-name", check_name, 0}, - {"config", module_config, 0}, - {"set-url", module_set_url, 0}, - {"set-branch", module_set_branch, 0}, -}; - -int cmd_submodule__helper(int argc, const char **argv, const char *prefix) -{ - int i; - if (argc < 2 || !strcmp(argv[1], "-h")) - usage("git submodule--helper <command>"); - - for (i = 0; i < ARRAY_SIZE(commands); i++) { - if (!strcmp(argv[1], commands[i].cmd)) { - if (get_super_prefix() && - !(commands[i].option & SUPPORT_SUPER_PREFIX)) - die(_("%s doesn't support --super-prefix"), - commands[i].cmd); - return commands[i].fn(argc - 1, argv + 1, prefix); - } - } - - die(_("'%s' is not a valid submodule--helper " - "subcommand"), argv[1]); -} diff --git a/third_party/git/builtin/symbolic-ref.c b/third_party/git/builtin/symbolic-ref.c deleted file mode 100644 index 80237f0df10f..000000000000 --- a/third_party/git/builtin/symbolic-ref.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "builtin.h" -#include "config.h" -#include "cache.h" -#include "refs.h" -#include "parse-options.h" - -static const char * const git_symbolic_ref_usage[] = { - N_("git symbolic-ref [<options>] <name> [<ref>]"), - N_("git symbolic-ref -d [-q] <name>"), - NULL -}; - -static int check_symref(const char *HEAD, int quiet, int shorten, int print) -{ - int flag; - const char *refname = resolve_ref_unsafe(HEAD, 0, NULL, &flag); - - if (!refname) - die("No such ref: %s", HEAD); - else if (!(flag & REF_ISSYMREF)) { - if (!quiet) - die("ref %s is not a symbolic ref", HEAD); - else - return 1; - } - if (print) { - if (shorten) - refname = shorten_unambiguous_ref(refname, 0); - puts(refname); - } - return 0; -} - -int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) -{ - int quiet = 0, delete = 0, shorten = 0, ret = 0; - const char *msg = NULL; - struct option options[] = { - OPT__QUIET(&quiet, - N_("suppress error message for non-symbolic (detached) refs")), - OPT_BOOL('d', "delete", &delete, N_("delete symbolic ref")), - OPT_BOOL(0, "short", &shorten, N_("shorten ref output")), - OPT_STRING('m', NULL, &msg, N_("reason"), N_("reason of the update")), - OPT_END(), - }; - - git_config(git_default_config, NULL); - argc = parse_options(argc, argv, prefix, options, - git_symbolic_ref_usage, 0); - if (msg && !*msg) - die("Refusing to perform update with empty message"); - - if (delete) { - if (argc != 1) - usage_with_options(git_symbolic_ref_usage, options); - ret = check_symref(argv[0], 1, 0, 0); - if (ret) - die("Cannot delete %s, not a symbolic ref", argv[0]); - if (!strcmp(argv[0], "HEAD")) - die("deleting '%s' is not allowed", argv[0]); - return delete_ref(NULL, argv[0], NULL, REF_NO_DEREF); - } - - switch (argc) { - case 1: - ret = check_symref(argv[0], quiet, shorten, 1); - break; - case 2: - if (!strcmp(argv[0], "HEAD") && - !starts_with(argv[1], "refs/")) - die("Refusing to point HEAD outside of refs/"); - ret = !!create_symref(argv[0], argv[1], msg); - break; - default: - usage_with_options(git_symbolic_ref_usage, options); - } - return ret; -} diff --git a/third_party/git/builtin/tag.c b/third_party/git/builtin/tag.c deleted file mode 100644 index ecf011776dc0..000000000000 --- a/third_party/git/builtin/tag.c +++ /dev/null @@ -1,594 +0,0 @@ -/* - * Builtin "git tag" - * - * Copyright (c) 2007 Kristian Hรธgsberg <krh@redhat.com>, - * Carlos Rica <jasampler@gmail.com> - * Based on git-tag.sh and mktag.c by Linus Torvalds. - */ - -#include "cache.h" -#include "config.h" -#include "builtin.h" -#include "refs.h" -#include "object-store.h" -#include "tag.h" -#include "run-command.h" -#include "parse-options.h" -#include "diff.h" -#include "revision.h" -#include "gpg-interface.h" -#include "oid-array.h" -#include "column.h" -#include "ref-filter.h" - -static const char * const git_tag_usage[] = { - N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n" - "\t\t<tagname> [<head>]"), - N_("git tag -d <tagname>..."), - N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]\n" - "\t\t[--format=<format>] [--merged <commit>] [--no-merged <commit>] [<pattern>...]"), - N_("git tag -v [--format=<format>] <tagname>..."), - NULL -}; - -static unsigned int colopts; -static int force_sign_annotate; -static int config_sign_tag = -1; /* unspecified */ - -static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, - struct ref_format *format) -{ - struct ref_array array; - char *to_free = NULL; - int i; - - memset(&array, 0, sizeof(array)); - - if (filter->lines == -1) - filter->lines = 0; - - if (!format->format) { - if (filter->lines) { - to_free = xstrfmt("%s %%(contents:lines=%d)", - "%(align:15)%(refname:lstrip=2)%(end)", - filter->lines); - format->format = to_free; - } else - format->format = "%(refname:lstrip=2)"; - } - - if (verify_ref_format(format)) - die(_("unable to parse format string")); - filter->with_commit_tag_algo = 1; - filter_refs(&array, filter, FILTER_REFS_TAGS); - ref_array_sort(sorting, &array); - - for (i = 0; i < array.nr; i++) - show_ref_array_item(array.items[i], format); - ref_array_clear(&array); - free(to_free); - - return 0; -} - -typedef int (*each_tag_name_fn)(const char *name, const char *ref, - const struct object_id *oid, const void *cb_data); - -static int for_each_tag_name(const char **argv, each_tag_name_fn fn, - const void *cb_data) -{ - const char **p; - struct strbuf ref = STRBUF_INIT; - int had_error = 0; - struct object_id oid; - - for (p = argv; *p; p++) { - strbuf_reset(&ref); - strbuf_addf(&ref, "refs/tags/%s", *p); - if (read_ref(ref.buf, &oid)) { - error(_("tag '%s' not found."), *p); - had_error = 1; - continue; - } - if (fn(*p, ref.buf, &oid, cb_data)) - had_error = 1; - } - strbuf_release(&ref); - return had_error; -} - -static int delete_tag(const char *name, const char *ref, - const struct object_id *oid, const void *cb_data) -{ - if (delete_ref(NULL, ref, oid, 0)) - return 1; - printf(_("Deleted tag '%s' (was %s)\n"), name, - find_unique_abbrev(oid, DEFAULT_ABBREV)); - return 0; -} - -static int verify_tag(const char *name, const char *ref, - const struct object_id *oid, const void *cb_data) -{ - int flags; - const struct ref_format *format = cb_data; - flags = GPG_VERIFY_VERBOSE; - - if (format->format) - flags = GPG_VERIFY_OMIT_STATUS; - - if (gpg_verify_tag(oid, name, flags)) - return -1; - - if (format->format) - pretty_print_ref(name, oid, format); - - return 0; -} - -static int do_sign(struct strbuf *buffer) -{ - return sign_buffer(buffer, buffer, get_signing_key()); -} - -static const char tag_template[] = - N_("\nWrite a message for tag:\n %s\n" - "Lines starting with '%c' will be ignored.\n"); - -static const char tag_template_nocleanup[] = - N_("\nWrite a message for tag:\n %s\n" - "Lines starting with '%c' will be kept; you may remove them" - " yourself if you want to.\n"); - -static int git_tag_config(const char *var, const char *value, void *cb) -{ - int status; - struct ref_sorting **sorting_tail = (struct ref_sorting **)cb; - - if (!strcmp(var, "tag.gpgsign")) { - config_sign_tag = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "tag.sort")) { - if (!value) - return config_error_nonbool(var); - parse_ref_sorting(sorting_tail, value); - return 0; - } - - status = git_gpg_config(var, value, cb); - if (status) - return status; - if (!strcmp(var, "tag.forcesignannotated")) { - force_sign_annotate = git_config_bool(var, value); - return 0; - } - - if (starts_with(var, "column.")) - return git_column_config(var, value, "tag", &colopts); - return git_color_default_config(var, value, cb); -} - -static void write_tag_body(int fd, const struct object_id *oid) -{ - unsigned long size; - enum object_type type; - char *buf, *sp; - - buf = read_object_file(oid, &type, &size); - if (!buf) - return; - /* skip header */ - sp = strstr(buf, "\n\n"); - - if (!sp || !size || type != OBJ_TAG) { - free(buf); - return; - } - sp += 2; /* skip the 2 LFs */ - write_or_die(fd, sp, parse_signature(sp, buf + size - sp)); - - free(buf); -} - -static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result) -{ - if (sign && do_sign(buf) < 0) - return error(_("unable to sign the tag")); - if (write_object_file(buf->buf, buf->len, tag_type, result) < 0) - return error(_("unable to write tag file")); - return 0; -} - -struct create_tag_options { - unsigned int message_given:1; - unsigned int use_editor:1; - unsigned int sign; - enum { - CLEANUP_NONE, - CLEANUP_SPACE, - CLEANUP_ALL - } cleanup_mode; -}; - -static const char message_advice_nested_tag[] = - N_("You have created a nested tag. The object referred to by your new tag is\n" - "already a tag. If you meant to tag the object that it points to, use:\n" - "\n" - "\tgit tag -f %s %s^{}"); - -static void create_tag(const struct object_id *object, const char *object_ref, - const char *tag, - struct strbuf *buf, struct create_tag_options *opt, - struct object_id *prev, struct object_id *result) -{ - enum object_type type; - struct strbuf header = STRBUF_INIT; - char *path = NULL; - - type = oid_object_info(the_repository, object, NULL); - if (type <= OBJ_NONE) - die(_("bad object type.")); - - if (type == OBJ_TAG) - advise_if_enabled(ADVICE_NESTED_TAG, _(message_advice_nested_tag), - tag, object_ref); - - strbuf_addf(&header, - "object %s\n" - "type %s\n" - "tag %s\n" - "tagger %s\n\n", - oid_to_hex(object), - type_name(type), - tag, - git_committer_info(IDENT_STRICT)); - - if (!opt->message_given || opt->use_editor) { - int fd; - - /* write the template message before editing: */ - path = git_pathdup("TAG_EDITMSG"); - fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); - if (fd < 0) - die_errno(_("could not create file '%s'"), path); - - if (opt->message_given) { - write_or_die(fd, buf->buf, buf->len); - strbuf_reset(buf); - } else if (!is_null_oid(prev)) { - write_tag_body(fd, prev); - } else { - struct strbuf buf = STRBUF_INIT; - strbuf_addch(&buf, '\n'); - if (opt->cleanup_mode == CLEANUP_ALL) - strbuf_commented_addf(&buf, _(tag_template), tag, comment_line_char); - else - strbuf_commented_addf(&buf, _(tag_template_nocleanup), tag, comment_line_char); - write_or_die(fd, buf.buf, buf.len); - strbuf_release(&buf); - } - close(fd); - - if (launch_editor(path, buf, NULL)) { - fprintf(stderr, - _("Please supply the message using either -m or -F option.\n")); - exit(1); - } - } - - if (opt->cleanup_mode != CLEANUP_NONE) - strbuf_stripspace(buf, opt->cleanup_mode == CLEANUP_ALL); - - if (!opt->message_given && !buf->len) - die(_("no tag message?")); - - strbuf_insert(buf, 0, header.buf, header.len); - strbuf_release(&header); - - if (build_tag_object(buf, opt->sign, result) < 0) { - if (path) - fprintf(stderr, _("The tag message has been left in %s\n"), - path); - exit(128); - } - if (path) { - unlink_or_warn(path); - free(path); - } -} - -static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb) -{ - enum object_type type; - struct commit *c; - char *buf; - unsigned long size; - int subject_len = 0; - const char *subject_start; - - char *rla = getenv("GIT_REFLOG_ACTION"); - if (rla) { - strbuf_addstr(sb, rla); - } else { - strbuf_addstr(sb, "tag: tagging "); - strbuf_add_unique_abbrev(sb, oid, DEFAULT_ABBREV); - } - - strbuf_addstr(sb, " ("); - type = oid_object_info(the_repository, oid, NULL); - switch (type) { - default: - strbuf_addstr(sb, "object of unknown type"); - break; - case OBJ_COMMIT: - if ((buf = read_object_file(oid, &type, &size)) != NULL) { - subject_len = find_commit_subject(buf, &subject_start); - strbuf_insert(sb, sb->len, subject_start, subject_len); - } else { - strbuf_addstr(sb, "commit object"); - } - free(buf); - - if ((c = lookup_commit_reference(the_repository, oid)) != NULL) - strbuf_addf(sb, ", %s", show_date(c->date, 0, DATE_MODE(SHORT))); - break; - case OBJ_TREE: - strbuf_addstr(sb, "tree object"); - break; - case OBJ_BLOB: - strbuf_addstr(sb, "blob object"); - break; - case OBJ_TAG: - strbuf_addstr(sb, "other tag object"); - break; - } - strbuf_addch(sb, ')'); -} - -struct msg_arg { - int given; - struct strbuf buf; -}; - -static int parse_msg_arg(const struct option *opt, const char *arg, int unset) -{ - struct msg_arg *msg = opt->value; - - BUG_ON_OPT_NEG(unset); - - if (!arg) - return -1; - if (msg->buf.len) - strbuf_addstr(&(msg->buf), "\n\n"); - strbuf_addstr(&(msg->buf), arg); - msg->given = 1; - return 0; -} - -static int strbuf_check_tag_ref(struct strbuf *sb, const char *name) -{ - if (name[0] == '-') - return -1; - - strbuf_reset(sb); - strbuf_addf(sb, "refs/tags/%s", name); - - return check_refname_format(sb->buf, 0); -} - -int cmd_tag(int argc, const char **argv, const char *prefix) -{ - struct strbuf buf = STRBUF_INIT; - struct strbuf ref = STRBUF_INIT; - struct strbuf reflog_msg = STRBUF_INIT; - struct object_id object, prev; - const char *object_ref, *tag; - struct create_tag_options opt; - char *cleanup_arg = NULL; - int create_reflog = 0; - int annotate = 0, force = 0; - int cmdmode = 0, create_tag_object = 0; - const char *msgfile = NULL, *keyid = NULL; - struct msg_arg msg = { 0, STRBUF_INIT }; - struct ref_transaction *transaction; - struct strbuf err = STRBUF_INIT; - struct ref_filter filter; - static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; - struct ref_format format = REF_FORMAT_INIT; - int icase = 0; - int edit_flag = 0; - struct option options[] = { - OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'), - { OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"), - N_("print <n> lines of each tag message"), - PARSE_OPT_OPTARG, NULL, 1 }, - OPT_CMDMODE('d', "delete", &cmdmode, N_("delete tags"), 'd'), - OPT_CMDMODE('v', "verify", &cmdmode, N_("verify tags"), 'v'), - - OPT_GROUP(N_("Tag creation options")), - OPT_BOOL('a', "annotate", &annotate, - N_("annotated tag, needs a message")), - OPT_CALLBACK_F('m', "message", &msg, N_("message"), - N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg), - OPT_FILENAME('F', "file", &msgfile, N_("read message from file")), - OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")), - OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")), - OPT_CLEANUP(&cleanup_arg), - OPT_STRING('u', "local-user", &keyid, N_("key-id"), - N_("use another key to sign the tag")), - OPT__FORCE(&force, N_("replace the tag if exists"), 0), - OPT_BOOL(0, "create-reflog", &create_reflog, N_("create a reflog")), - - OPT_GROUP(N_("Tag listing options")), - OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")), - OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")), - OPT_NO_CONTAINS(&filter.no_commit, N_("print only tags that don't contain the commit")), - OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")), - OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")), - OPT_MERGED(&filter, N_("print only tags that are merged")), - OPT_NO_MERGED(&filter, N_("print only tags that are not merged")), - OPT_REF_SORT(sorting_tail), - { - OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"), - N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT, - parse_opt_object_name, (intptr_t) "HEAD" - }, - OPT_STRING( 0 , "format", &format.format, N_("format"), - N_("format to use for the output")), - OPT__COLOR(&format.use_color, N_("respect format colors")), - OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")), - OPT_END() - }; - - setup_ref_filter_porcelain_msg(); - - git_config(git_tag_config, sorting_tail); - - memset(&opt, 0, sizeof(opt)); - memset(&filter, 0, sizeof(filter)); - filter.lines = -1; - opt.sign = -1; - - argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0); - - if (!cmdmode) { - if (argc == 0) - cmdmode = 'l'; - else if (filter.with_commit || filter.no_commit || - filter.reachable_from || filter.unreachable_from || - filter.points_at.nr || filter.lines != -1) - cmdmode = 'l'; - } - - if (cmdmode == 'l') - setup_auto_pager("tag", 1); - - if (opt.sign == -1) - opt.sign = cmdmode ? 0 : config_sign_tag > 0; - - if (keyid) { - opt.sign = 1; - set_signing_key(keyid); - } - create_tag_object = (opt.sign || annotate || msg.given || msgfile); - - if ((create_tag_object || force) && (cmdmode != 0)) - usage_with_options(git_tag_usage, options); - - finalize_colopts(&colopts, -1); - if (cmdmode == 'l' && filter.lines != -1) { - if (explicitly_enable_column(colopts)) - die(_("--column and -n are incompatible")); - colopts = 0; - } - if (!sorting) - sorting = ref_default_sorting(); - ref_sorting_icase_all(sorting, icase); - filter.ignore_case = icase; - if (cmdmode == 'l') { - int ret; - if (column_active(colopts)) { - struct column_options copts; - memset(&copts, 0, sizeof(copts)); - copts.padding = 2; - run_column_filter(colopts, &copts); - } - filter.name_patterns = argv; - ret = list_tags(&filter, sorting, &format); - if (column_active(colopts)) - stop_column_filter(); - return ret; - } - if (filter.lines != -1) - die(_("-n option is only allowed in list mode")); - if (filter.with_commit) - die(_("--contains option is only allowed in list mode")); - if (filter.no_commit) - die(_("--no-contains option is only allowed in list mode")); - if (filter.points_at.nr) - die(_("--points-at option is only allowed in list mode")); - if (filter.reachable_from || filter.unreachable_from) - die(_("--merged and --no-merged options are only allowed in list mode")); - if (cmdmode == 'd') - return for_each_tag_name(argv, delete_tag, NULL); - if (cmdmode == 'v') { - if (format.format && verify_ref_format(&format)) - usage_with_options(git_tag_usage, options); - return for_each_tag_name(argv, verify_tag, &format); - } - - if (msg.given || msgfile) { - if (msg.given && msgfile) - die(_("only one -F or -m option is allowed.")); - if (msg.given) - strbuf_addbuf(&buf, &(msg.buf)); - else { - if (!strcmp(msgfile, "-")) { - if (strbuf_read(&buf, 0, 1024) < 0) - die_errno(_("cannot read '%s'"), msgfile); - } else { - if (strbuf_read_file(&buf, msgfile, 1024) < 0) - die_errno(_("could not open or read '%s'"), - msgfile); - } - } - } - - tag = argv[0]; - - object_ref = argc == 2 ? argv[1] : "HEAD"; - if (argc > 2) - die(_("too many params")); - - if (get_oid(object_ref, &object)) - die(_("Failed to resolve '%s' as a valid ref."), object_ref); - - if (strbuf_check_tag_ref(&ref, tag)) - die(_("'%s' is not a valid tag name."), tag); - - if (read_ref(ref.buf, &prev)) - oidclr(&prev); - else if (!force) - die(_("tag '%s' already exists"), tag); - - opt.message_given = msg.given || msgfile; - opt.use_editor = edit_flag; - - if (!cleanup_arg || !strcmp(cleanup_arg, "strip")) - opt.cleanup_mode = CLEANUP_ALL; - else if (!strcmp(cleanup_arg, "verbatim")) - opt.cleanup_mode = CLEANUP_NONE; - else if (!strcmp(cleanup_arg, "whitespace")) - opt.cleanup_mode = CLEANUP_SPACE; - else - die(_("Invalid cleanup mode %s"), cleanup_arg); - - create_reflog_msg(&object, &reflog_msg); - - if (create_tag_object) { - if (force_sign_annotate && !annotate) - opt.sign = 1; - create_tag(&object, object_ref, tag, &buf, &opt, &prev, &object); - } - - transaction = ref_transaction_begin(&err); - if (!transaction || - ref_transaction_update(transaction, ref.buf, &object, &prev, - create_reflog ? REF_FORCE_CREATE_REFLOG : 0, - reflog_msg.buf, &err) || - ref_transaction_commit(transaction, &err)) - die("%s", err.buf); - ref_transaction_free(transaction); - if (force && !is_null_oid(&prev) && !oideq(&prev, &object)) - printf(_("Updated tag '%s' (was %s)\n"), tag, - find_unique_abbrev(&prev, DEFAULT_ABBREV)); - - UNLEAK(buf); - UNLEAK(ref); - UNLEAK(reflog_msg); - UNLEAK(msg); - UNLEAK(err); - return 0; -} diff --git a/third_party/git/builtin/unpack-file.c b/third_party/git/builtin/unpack-file.c deleted file mode 100644 index 58652229f273..000000000000 --- a/third_party/git/builtin/unpack-file.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "builtin.h" -#include "config.h" -#include "object-store.h" - -static char *create_temp_file(struct object_id *oid) -{ - static char path[50]; - void *buf; - enum object_type type; - unsigned long size; - int fd; - - buf = read_object_file(oid, &type, &size); - if (!buf || type != OBJ_BLOB) - die("unable to read blob object %s", oid_to_hex(oid)); - - xsnprintf(path, sizeof(path), ".merge_file_XXXXXX"); - fd = xmkstemp(path); - if (write_in_full(fd, buf, size) < 0) - die_errno("unable to write temp-file"); - close(fd); - return path; -} - -int cmd_unpack_file(int argc, const char **argv, const char *prefix) -{ - struct object_id oid; - - if (argc != 2 || !strcmp(argv[1], "-h")) - usage("git unpack-file <sha1>"); - if (get_oid(argv[1], &oid)) - die("Not a valid object name %s", argv[1]); - - git_config(git_default_config, NULL); - - puts(create_temp_file(&oid)); - return 0; -} diff --git a/third_party/git/builtin/unpack-objects.c b/third_party/git/builtin/unpack-objects.c deleted file mode 100644 index dd4a75e030d2..000000000000 --- a/third_party/git/builtin/unpack-objects.c +++ /dev/null @@ -1,599 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "object-store.h" -#include "object.h" -#include "delta.h" -#include "pack.h" -#include "blob.h" -#include "commit.h" -#include "tag.h" -#include "tree.h" -#include "tree-walk.h" -#include "progress.h" -#include "decorate.h" -#include "fsck.h" - -static int dry_run, quiet, recover, has_errors, strict; -static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict]"; - -/* We always read in 4kB chunks. */ -static unsigned char buffer[4096]; -static unsigned int offset, len; -static off_t consumed_bytes; -static off_t max_input_size; -static git_hash_ctx ctx; -static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT; -static struct progress *progress; - -/* - * When running under --strict mode, objects whose reachability are - * suspect are kept in core without getting written in the object - * store. - */ -struct obj_buffer { - char *buffer; - unsigned long size; -}; - -static struct decoration obj_decorate; - -static struct obj_buffer *lookup_object_buffer(struct object *base) -{ - return lookup_decoration(&obj_decorate, base); -} - -static void add_object_buffer(struct object *object, char *buffer, unsigned long size) -{ - struct obj_buffer *obj; - obj = xcalloc(1, sizeof(struct obj_buffer)); - obj->buffer = buffer; - obj->size = size; - if (add_decoration(&obj_decorate, object, obj)) - die("object %s tried to add buffer twice!", oid_to_hex(&object->oid)); -} - -/* - * Make sure at least "min" bytes are available in the buffer, and - * return the pointer to the buffer. - */ -static void *fill(int min) -{ - if (min <= len) - return buffer + offset; - if (min > sizeof(buffer)) - die("cannot fill %d bytes", min); - if (offset) { - the_hash_algo->update_fn(&ctx, buffer, offset); - memmove(buffer, buffer + offset, len); - offset = 0; - } - do { - ssize_t ret = xread(0, buffer + len, sizeof(buffer) - len); - if (ret <= 0) { - if (!ret) - die("early EOF"); - die_errno("read error on input"); - } - len += ret; - } while (len < min); - return buffer; -} - -static void use(int bytes) -{ - if (bytes > len) - die("used more bytes than were available"); - len -= bytes; - offset += bytes; - - /* make sure off_t is sufficiently large not to wrap */ - if (signed_add_overflows(consumed_bytes, bytes)) - die("pack too large for current definition of off_t"); - consumed_bytes += bytes; - if (max_input_size && consumed_bytes > max_input_size) - die(_("pack exceeds maximum allowed size")); - display_throughput(progress, consumed_bytes); -} - -static void *get_data(unsigned long size) -{ - git_zstream stream; - void *buf = xmallocz(size); - - memset(&stream, 0, sizeof(stream)); - - stream.next_out = buf; - stream.avail_out = size; - stream.next_in = fill(1); - stream.avail_in = len; - git_inflate_init(&stream); - - for (;;) { - int ret = git_inflate(&stream, 0); - use(len - stream.avail_in); - if (stream.total_out == size && ret == Z_STREAM_END) - break; - if (ret != Z_OK) { - error("inflate returned %d", ret); - FREE_AND_NULL(buf); - if (!recover) - exit(1); - has_errors = 1; - break; - } - stream.next_in = fill(1); - stream.avail_in = len; - } - git_inflate_end(&stream); - return buf; -} - -struct delta_info { - struct object_id base_oid; - unsigned nr; - off_t base_offset; - unsigned long size; - void *delta; - struct delta_info *next; -}; - -static struct delta_info *delta_list; - -static void add_delta_to_list(unsigned nr, const struct object_id *base_oid, - off_t base_offset, - void *delta, unsigned long size) -{ - struct delta_info *info = xmalloc(sizeof(*info)); - - oidcpy(&info->base_oid, base_oid); - info->base_offset = base_offset; - info->size = size; - info->delta = delta; - info->nr = nr; - info->next = delta_list; - delta_list = info; -} - -struct obj_info { - off_t offset; - struct object_id oid; - struct object *obj; -}; - -/* Remember to update object flag allocation in object.h */ -#define FLAG_OPEN (1u<<20) -#define FLAG_WRITTEN (1u<<21) - -static struct obj_info *obj_list; -static unsigned nr_objects; - -/* - * Called only from check_object() after it verified this object - * is Ok. - */ -static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf) -{ - struct object_id oid; - - if (write_object_file(obj_buf->buffer, obj_buf->size, - type_name(obj->type), &oid) < 0) - die("failed to write object %s", oid_to_hex(&obj->oid)); - obj->flags |= FLAG_WRITTEN; -} - -/* - * At the very end of the processing, write_rest() scans the objects - * that have reachability requirements and calls this function. - * Verify its reachability and validity recursively and write it out. - */ -static int check_object(struct object *obj, int type, void *data, struct fsck_options *options) -{ - struct obj_buffer *obj_buf; - - if (!obj) - return 1; - - if (obj->flags & FLAG_WRITTEN) - return 0; - - if (type != OBJ_ANY && obj->type != type) - die("object type mismatch"); - - if (!(obj->flags & FLAG_OPEN)) { - unsigned long size; - int type = oid_object_info(the_repository, &obj->oid, &size); - if (type != obj->type || type <= 0) - die("object of unexpected type"); - obj->flags |= FLAG_WRITTEN; - return 0; - } - - obj_buf = lookup_object_buffer(obj); - if (!obj_buf) - die("Whoops! Cannot find object '%s'", oid_to_hex(&obj->oid)); - if (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options)) - die("fsck error in packed object"); - fsck_options.walk = check_object; - if (fsck_walk(obj, NULL, &fsck_options)) - die("Error on reachable objects of %s", oid_to_hex(&obj->oid)); - write_cached_object(obj, obj_buf); - return 0; -} - -static void write_rest(void) -{ - unsigned i; - for (i = 0; i < nr_objects; i++) { - if (obj_list[i].obj) - check_object(obj_list[i].obj, OBJ_ANY, NULL, NULL); - } -} - -static void added_object(unsigned nr, enum object_type type, - void *data, unsigned long size); - -/* - * Write out nr-th object from the list, now we know the contents - * of it. Under --strict, this buffers structured objects in-core, - * to be checked at the end. - */ -static void write_object(unsigned nr, enum object_type type, - void *buf, unsigned long size) -{ - if (!strict) { - if (write_object_file(buf, size, type_name(type), - &obj_list[nr].oid) < 0) - die("failed to write object"); - added_object(nr, type, buf, size); - free(buf); - obj_list[nr].obj = NULL; - } else if (type == OBJ_BLOB) { - struct blob *blob; - if (write_object_file(buf, size, type_name(type), - &obj_list[nr].oid) < 0) - die("failed to write object"); - added_object(nr, type, buf, size); - free(buf); - - blob = lookup_blob(the_repository, &obj_list[nr].oid); - if (blob) - blob->object.flags |= FLAG_WRITTEN; - else - die("invalid blob object"); - obj_list[nr].obj = NULL; - } else { - struct object *obj; - int eaten; - hash_object_file(the_hash_algo, buf, size, type_name(type), - &obj_list[nr].oid); - added_object(nr, type, buf, size); - obj = parse_object_buffer(the_repository, &obj_list[nr].oid, - type, size, buf, - &eaten); - if (!obj) - die("invalid %s", type_name(type)); - add_object_buffer(obj, buf, size); - obj->flags |= FLAG_OPEN; - obj_list[nr].obj = obj; - } -} - -static void resolve_delta(unsigned nr, enum object_type type, - void *base, unsigned long base_size, - void *delta, unsigned long delta_size) -{ - void *result; - unsigned long result_size; - - result = patch_delta(base, base_size, - delta, delta_size, - &result_size); - if (!result) - die("failed to apply delta"); - free(delta); - write_object(nr, type, result, result_size); -} - -/* - * We now know the contents of an object (which is nr-th in the pack); - * resolve all the deltified objects that are based on it. - */ -static void added_object(unsigned nr, enum object_type type, - void *data, unsigned long size) -{ - struct delta_info **p = &delta_list; - struct delta_info *info; - - while ((info = *p) != NULL) { - if (oideq(&info->base_oid, &obj_list[nr].oid) || - info->base_offset == obj_list[nr].offset) { - *p = info->next; - p = &delta_list; - resolve_delta(info->nr, type, data, size, - info->delta, info->size); - free(info); - continue; - } - p = &info->next; - } -} - -static void unpack_non_delta_entry(enum object_type type, unsigned long size, - unsigned nr) -{ - void *buf = get_data(size); - - if (!dry_run && buf) - write_object(nr, type, buf, size); - else - free(buf); -} - -static int resolve_against_held(unsigned nr, const struct object_id *base, - void *delta_data, unsigned long delta_size) -{ - struct object *obj; - struct obj_buffer *obj_buffer; - obj = lookup_object(the_repository, base); - if (!obj) - return 0; - obj_buffer = lookup_object_buffer(obj); - if (!obj_buffer) - return 0; - resolve_delta(nr, obj->type, obj_buffer->buffer, - obj_buffer->size, delta_data, delta_size); - return 1; -} - -static void unpack_delta_entry(enum object_type type, unsigned long delta_size, - unsigned nr) -{ - void *delta_data, *base; - unsigned long base_size; - struct object_id base_oid; - - if (type == OBJ_REF_DELTA) { - hashcpy(base_oid.hash, fill(the_hash_algo->rawsz)); - use(the_hash_algo->rawsz); - delta_data = get_data(delta_size); - if (dry_run || !delta_data) { - free(delta_data); - return; - } - if (has_object_file(&base_oid)) - ; /* Ok we have this one */ - else if (resolve_against_held(nr, &base_oid, - delta_data, delta_size)) - return; /* we are done */ - else { - /* cannot resolve yet --- queue it */ - oidclr(&obj_list[nr].oid); - add_delta_to_list(nr, &base_oid, 0, delta_data, delta_size); - return; - } - } else { - unsigned base_found = 0; - unsigned char *pack, c; - off_t base_offset; - unsigned lo, mid, hi; - - pack = fill(1); - c = *pack; - use(1); - base_offset = c & 127; - while (c & 128) { - base_offset += 1; - if (!base_offset || MSB(base_offset, 7)) - die("offset value overflow for delta base object"); - pack = fill(1); - c = *pack; - use(1); - base_offset = (base_offset << 7) + (c & 127); - } - base_offset = obj_list[nr].offset - base_offset; - if (base_offset <= 0 || base_offset >= obj_list[nr].offset) - die("offset value out of bound for delta base object"); - - delta_data = get_data(delta_size); - if (dry_run || !delta_data) { - free(delta_data); - return; - } - lo = 0; - hi = nr; - while (lo < hi) { - mid = lo + (hi - lo) / 2; - if (base_offset < obj_list[mid].offset) { - hi = mid; - } else if (base_offset > obj_list[mid].offset) { - lo = mid + 1; - } else { - oidcpy(&base_oid, &obj_list[mid].oid); - base_found = !is_null_oid(&base_oid); - break; - } - } - if (!base_found) { - /* - * The delta base object is itself a delta that - * has not been resolved yet. - */ - oidclr(&obj_list[nr].oid); - add_delta_to_list(nr, &null_oid, base_offset, delta_data, delta_size); - return; - } - } - - if (resolve_against_held(nr, &base_oid, delta_data, delta_size)) - return; - - base = read_object_file(&base_oid, &type, &base_size); - if (!base) { - error("failed to read delta-pack base object %s", - oid_to_hex(&base_oid)); - if (!recover) - exit(1); - has_errors = 1; - return; - } - resolve_delta(nr, type, base, base_size, delta_data, delta_size); - free(base); -} - -static void unpack_one(unsigned nr) -{ - unsigned shift; - unsigned char *pack; - unsigned long size, c; - enum object_type type; - - obj_list[nr].offset = consumed_bytes; - - pack = fill(1); - c = *pack; - use(1); - type = (c >> 4) & 7; - size = (c & 15); - shift = 4; - while (c & 0x80) { - pack = fill(1); - c = *pack; - use(1); - size += (c & 0x7f) << shift; - shift += 7; - } - - switch (type) { - case OBJ_COMMIT: - case OBJ_TREE: - case OBJ_BLOB: - case OBJ_TAG: - unpack_non_delta_entry(type, size, nr); - return; - case OBJ_REF_DELTA: - case OBJ_OFS_DELTA: - unpack_delta_entry(type, size, nr); - return; - default: - error("bad object type %d", type); - has_errors = 1; - if (recover) - return; - exit(1); - } -} - -static void unpack_all(void) -{ - int i; - struct pack_header *hdr = fill(sizeof(struct pack_header)); - - nr_objects = ntohl(hdr->hdr_entries); - - if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE) - die("bad pack file"); - if (!pack_version_ok(hdr->hdr_version)) - die("unknown pack file version %"PRIu32, - ntohl(hdr->hdr_version)); - use(sizeof(struct pack_header)); - - if (!quiet) - progress = start_progress(_("Unpacking objects"), nr_objects); - obj_list = xcalloc(nr_objects, sizeof(*obj_list)); - for (i = 0; i < nr_objects; i++) { - unpack_one(i); - display_progress(progress, i + 1); - } - stop_progress(&progress); - - if (delta_list) - die("unresolved deltas left after unpacking"); -} - -int cmd_unpack_objects(int argc, const char **argv, const char *prefix) -{ - int i; - struct object_id oid; - - read_replace_refs = 0; - - git_config(git_default_config, NULL); - - quiet = !isatty(2); - - for (i = 1 ; i < argc; i++) { - const char *arg = argv[i]; - - if (*arg == '-') { - if (!strcmp(arg, "-n")) { - dry_run = 1; - continue; - } - if (!strcmp(arg, "-q")) { - quiet = 1; - continue; - } - if (!strcmp(arg, "-r")) { - recover = 1; - continue; - } - if (!strcmp(arg, "--strict")) { - strict = 1; - continue; - } - if (skip_prefix(arg, "--strict=", &arg)) { - strict = 1; - fsck_set_msg_types(&fsck_options, arg); - continue; - } - if (starts_with(arg, "--pack_header=")) { - struct pack_header *hdr; - char *c; - - hdr = (struct pack_header *)buffer; - hdr->hdr_signature = htonl(PACK_SIGNATURE); - hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10)); - if (*c != ',') - die("bad %s", arg); - hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10)); - if (*c) - die("bad %s", arg); - len = sizeof(*hdr); - continue; - } - if (skip_prefix(arg, "--max-input-size=", &arg)) { - max_input_size = strtoumax(arg, NULL, 10); - continue; - } - usage(unpack_usage); - } - - /* We don't take any non-flag arguments now.. Maybe some day */ - usage(unpack_usage); - } - the_hash_algo->init_fn(&ctx); - unpack_all(); - the_hash_algo->update_fn(&ctx, buffer, offset); - the_hash_algo->final_fn(oid.hash, &ctx); - if (strict) { - write_rest(); - if (fsck_finish(&fsck_options)) - die(_("fsck error in pack objects")); - } - if (!hasheq(fill(the_hash_algo->rawsz), oid.hash)) - die("final sha1 did not match"); - use(the_hash_algo->rawsz); - - /* Write the last part of the buffer to stdout */ - while (len) { - int ret = xwrite(1, buffer + offset, len); - if (ret <= 0) - break; - len -= ret; - offset += ret; - } - - /* All done */ - return has_errors; -} diff --git a/third_party/git/builtin/update-index.c b/third_party/git/builtin/update-index.c deleted file mode 100644 index 79087bccea4b..000000000000 --- a/third_party/git/builtin/update-index.c +++ /dev/null @@ -1,1245 +0,0 @@ -/* - * GIT - The information manager from hell - * - * Copyright (C) Linus Torvalds, 2005 - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "cache.h" -#include "config.h" -#include "lockfile.h" -#include "quote.h" -#include "cache-tree.h" -#include "tree-walk.h" -#include "builtin.h" -#include "refs.h" -#include "resolve-undo.h" -#include "parse-options.h" -#include "pathspec.h" -#include "dir.h" -#include "split-index.h" -#include "fsmonitor.h" - -/* - * Default to not allowing changes to the list of files. The - * tool doesn't actually care, but this makes it harder to add - * files to the revision control by mistake by doing something - * like "git update-index *" and suddenly having all the object - * files be revision controlled. - */ -static int allow_add; -static int allow_remove; -static int allow_replace; -static int info_only; -static int force_remove; -static int verbose; -static int mark_valid_only; -static int mark_skip_worktree_only; -static int mark_fsmonitor_only; -static int ignore_skip_worktree_entries; -#define MARK_FLAG 1 -#define UNMARK_FLAG 2 -static struct strbuf mtime_dir = STRBUF_INIT; - -/* Untracked cache mode */ -enum uc_mode { - UC_UNSPECIFIED = -1, - UC_DISABLE = 0, - UC_ENABLE, - UC_TEST, - UC_FORCE -}; - -__attribute__((format (printf, 1, 2))) -static void report(const char *fmt, ...) -{ - va_list vp; - - if (!verbose) - return; - - va_start(vp, fmt); - vprintf(fmt, vp); - putchar('\n'); - va_end(vp); -} - -static void remove_test_directory(void) -{ - if (mtime_dir.len) - remove_dir_recursively(&mtime_dir, 0); -} - -static const char *get_mtime_path(const char *path) -{ - static struct strbuf sb = STRBUF_INIT; - strbuf_reset(&sb); - strbuf_addf(&sb, "%s/%s", mtime_dir.buf, path); - return sb.buf; -} - -static void xmkdir(const char *path) -{ - path = get_mtime_path(path); - if (mkdir(path, 0700)) - die_errno(_("failed to create directory %s"), path); -} - -static int xstat_mtime_dir(struct stat *st) -{ - if (stat(mtime_dir.buf, st)) - die_errno(_("failed to stat %s"), mtime_dir.buf); - return 0; -} - -static int create_file(const char *path) -{ - int fd; - path = get_mtime_path(path); - fd = open(path, O_CREAT | O_RDWR, 0644); - if (fd < 0) - die_errno(_("failed to create file %s"), path); - return fd; -} - -static void xunlink(const char *path) -{ - path = get_mtime_path(path); - if (unlink(path)) - die_errno(_("failed to delete file %s"), path); -} - -static void xrmdir(const char *path) -{ - path = get_mtime_path(path); - if (rmdir(path)) - die_errno(_("failed to delete directory %s"), path); -} - -static void avoid_racy(void) -{ - /* - * not use if we could usleep(10) if USE_NSEC is defined. The - * field nsec could be there, but the OS could choose to - * ignore it? - */ - sleep(1); -} - -static int test_if_untracked_cache_is_supported(void) -{ - struct stat st; - struct stat_data base; - int fd, ret = 0; - char *cwd; - - strbuf_addstr(&mtime_dir, "mtime-test-XXXXXX"); - if (!mkdtemp(mtime_dir.buf)) - die_errno("Could not make temporary directory"); - - cwd = xgetcwd(); - fprintf(stderr, _("Testing mtime in '%s' "), cwd); - free(cwd); - - atexit(remove_test_directory); - xstat_mtime_dir(&st); - fill_stat_data(&base, &st); - fputc('.', stderr); - - avoid_racy(); - fd = create_file("newfile"); - xstat_mtime_dir(&st); - if (!match_stat_data(&base, &st)) { - close(fd); - fputc('\n', stderr); - fprintf_ln(stderr,_("directory stat info does not " - "change after adding a new file")); - goto done; - } - fill_stat_data(&base, &st); - fputc('.', stderr); - - avoid_racy(); - xmkdir("new-dir"); - xstat_mtime_dir(&st); - if (!match_stat_data(&base, &st)) { - close(fd); - fputc('\n', stderr); - fprintf_ln(stderr, _("directory stat info does not change " - "after adding a new directory")); - goto done; - } - fill_stat_data(&base, &st); - fputc('.', stderr); - - avoid_racy(); - write_or_die(fd, "data", 4); - close(fd); - xstat_mtime_dir(&st); - if (match_stat_data(&base, &st)) { - fputc('\n', stderr); - fprintf_ln(stderr, _("directory stat info changes " - "after updating a file")); - goto done; - } - fputc('.', stderr); - - avoid_racy(); - close(create_file("new-dir/new")); - xstat_mtime_dir(&st); - if (match_stat_data(&base, &st)) { - fputc('\n', stderr); - fprintf_ln(stderr, _("directory stat info changes after " - "adding a file inside subdirectory")); - goto done; - } - fputc('.', stderr); - - avoid_racy(); - xunlink("newfile"); - xstat_mtime_dir(&st); - if (!match_stat_data(&base, &st)) { - fputc('\n', stderr); - fprintf_ln(stderr, _("directory stat info does not " - "change after deleting a file")); - goto done; - } - fill_stat_data(&base, &st); - fputc('.', stderr); - - avoid_racy(); - xunlink("new-dir/new"); - xrmdir("new-dir"); - xstat_mtime_dir(&st); - if (!match_stat_data(&base, &st)) { - fputc('\n', stderr); - fprintf_ln(stderr, _("directory stat info does not " - "change after deleting a directory")); - goto done; - } - - if (rmdir(mtime_dir.buf)) - die_errno(_("failed to delete directory %s"), mtime_dir.buf); - fprintf_ln(stderr, _(" OK")); - ret = 1; - -done: - strbuf_release(&mtime_dir); - return ret; -} - -static int mark_ce_flags(const char *path, int flag, int mark) -{ - int namelen = strlen(path); - int pos = cache_name_pos(path, namelen); - if (0 <= pos) { - mark_fsmonitor_invalid(&the_index, active_cache[pos]); - if (mark) - active_cache[pos]->ce_flags |= flag; - else - active_cache[pos]->ce_flags &= ~flag; - active_cache[pos]->ce_flags |= CE_UPDATE_IN_BASE; - cache_tree_invalidate_path(&the_index, path); - active_cache_changed |= CE_ENTRY_CHANGED; - return 0; - } - return -1; -} - -static int remove_one_path(const char *path) -{ - if (!allow_remove) - return error("%s: does not exist and --remove not passed", path); - if (remove_file_from_cache(path)) - return error("%s: cannot remove from the index", path); - return 0; -} - -/* - * Handle a path that couldn't be lstat'ed. It's either: - * - missing file (ENOENT or ENOTDIR). That's ok if we're - * supposed to be removing it and the removal actually - * succeeds. - * - permission error. That's never ok. - */ -static int process_lstat_error(const char *path, int err) -{ - if (is_missing_file_error(err)) - return remove_one_path(path); - return error("lstat(\"%s\"): %s", path, strerror(err)); -} - -static int add_one_path(const struct cache_entry *old, const char *path, int len, struct stat *st) -{ - int option; - struct cache_entry *ce; - - /* Was the old index entry already up-to-date? */ - if (old && !ce_stage(old) && !ce_match_stat(old, st, 0)) - return 0; - - ce = make_empty_cache_entry(&the_index, len); - memcpy(ce->name, path, len); - ce->ce_flags = create_ce_flags(0); - ce->ce_namelen = len; - fill_stat_cache_info(&the_index, ce, st); - ce->ce_mode = ce_mode_from_stat(old, st->st_mode); - - if (index_path(&the_index, &ce->oid, path, st, - info_only ? 0 : HASH_WRITE_OBJECT)) { - discard_cache_entry(ce); - return -1; - } - option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; - option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; - if (add_cache_entry(ce, option)) { - discard_cache_entry(ce); - return error("%s: cannot add to the index - missing --add option?", path); - } - return 0; -} - -/* - * Handle a path that was a directory. Four cases: - * - * - it's already a gitlink in the index, and we keep it that - * way, and update it if we can (if we cannot find the HEAD, - * we're going to keep it unchanged in the index!) - * - * - it's a *file* in the index, in which case it should be - * removed as a file if removal is allowed, since it doesn't - * exist as such any more. If removal isn't allowed, it's - * an error. - * - * (NOTE! This is old and arguably fairly strange behaviour. - * We might want to make this an error unconditionally, and - * use "--force-remove" if you actually want to force removal). - * - * - it used to exist as a subdirectory (ie multiple files with - * this particular prefix) in the index, in which case it's wrong - * to try to update it as a directory. - * - * - it doesn't exist at all in the index, but it is a valid - * git directory, and it should be *added* as a gitlink. - */ -static int process_directory(const char *path, int len, struct stat *st) -{ - struct object_id oid; - int pos = cache_name_pos(path, len); - - /* Exact match: file or existing gitlink */ - if (pos >= 0) { - const struct cache_entry *ce = active_cache[pos]; - if (S_ISGITLINK(ce->ce_mode)) { - - /* Do nothing to the index if there is no HEAD! */ - if (resolve_gitlink_ref(path, "HEAD", &oid) < 0) - return 0; - - return add_one_path(ce, path, len, st); - } - /* Should this be an unconditional error? */ - return remove_one_path(path); - } - - /* Inexact match: is there perhaps a subdirectory match? */ - pos = -pos-1; - while (pos < active_nr) { - const struct cache_entry *ce = active_cache[pos++]; - - if (strncmp(ce->name, path, len)) - break; - if (ce->name[len] > '/') - break; - if (ce->name[len] < '/') - continue; - - /* Subdirectory match - error out */ - return error("%s: is a directory - add individual files instead", path); - } - - /* No match - should we add it as a gitlink? */ - if (!resolve_gitlink_ref(path, "HEAD", &oid)) - return add_one_path(NULL, path, len, st); - - /* Error out. */ - return error("%s: is a directory - add files inside instead", path); -} - -static int process_path(const char *path, struct stat *st, int stat_errno) -{ - int pos, len; - const struct cache_entry *ce; - - len = strlen(path); - if (has_symlink_leading_path(path, len)) - return error("'%s' is beyond a symbolic link", path); - - pos = cache_name_pos(path, len); - ce = pos < 0 ? NULL : active_cache[pos]; - if (ce && ce_skip_worktree(ce)) { - /* - * working directory version is assumed "good" - * so updating it does not make sense. - * On the other hand, removing it from index should work - */ - if (!ignore_skip_worktree_entries && allow_remove && - remove_file_from_cache(path)) - return error("%s: cannot remove from the index", path); - return 0; - } - - /* - * First things first: get the stat information, to decide - * what to do about the pathname! - */ - if (stat_errno) - return process_lstat_error(path, stat_errno); - - if (S_ISDIR(st->st_mode)) - return process_directory(path, len, st); - - return add_one_path(ce, path, len, st); -} - -static int add_cacheinfo(unsigned int mode, const struct object_id *oid, - const char *path, int stage) -{ - int len, option; - struct cache_entry *ce; - - if (!verify_path(path, mode)) - return error("Invalid path '%s'", path); - - len = strlen(path); - ce = make_empty_cache_entry(&the_index, len); - - oidcpy(&ce->oid, oid); - memcpy(ce->name, path, len); - ce->ce_flags = create_ce_flags(stage); - ce->ce_namelen = len; - ce->ce_mode = create_ce_mode(mode); - if (assume_unchanged) - ce->ce_flags |= CE_VALID; - option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; - option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; - if (add_cache_entry(ce, option)) - return error("%s: cannot add to the index - missing --add option?", - path); - report("add '%s'", path); - return 0; -} - -static void chmod_path(char flip, const char *path) -{ - int pos; - struct cache_entry *ce; - - pos = cache_name_pos(path, strlen(path)); - if (pos < 0) - goto fail; - ce = active_cache[pos]; - if (chmod_cache_entry(ce, flip) < 0) - goto fail; - - report("chmod %cx '%s'", flip, path); - return; - fail: - die("git update-index: cannot chmod %cx '%s'", flip, path); -} - -static void update_one(const char *path) -{ - int stat_errno = 0; - struct stat st; - - if (mark_valid_only || mark_skip_worktree_only || force_remove || - mark_fsmonitor_only) - st.st_mode = 0; - else if (lstat(path, &st) < 0) { - st.st_mode = 0; - stat_errno = errno; - } /* else stat is valid */ - - if (!verify_path(path, st.st_mode)) { - fprintf(stderr, "Ignoring path %s\n", path); - return; - } - if (mark_valid_only) { - if (mark_ce_flags(path, CE_VALID, mark_valid_only == MARK_FLAG)) - die("Unable to mark file %s", path); - return; - } - if (mark_skip_worktree_only) { - if (mark_ce_flags(path, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG)) - die("Unable to mark file %s", path); - return; - } - if (mark_fsmonitor_only) { - if (mark_ce_flags(path, CE_FSMONITOR_VALID, mark_fsmonitor_only == MARK_FLAG)) - die("Unable to mark file %s", path); - return; - } - - if (force_remove) { - if (remove_file_from_cache(path)) - die("git update-index: unable to remove %s", path); - report("remove '%s'", path); - return; - } - if (process_path(path, &st, stat_errno)) - die("Unable to process path %s", path); - report("add '%s'", path); -} - -static void read_index_info(int nul_term_line) -{ - const int hexsz = the_hash_algo->hexsz; - struct strbuf buf = STRBUF_INIT; - struct strbuf uq = STRBUF_INIT; - strbuf_getline_fn getline_fn; - - getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; - while (getline_fn(&buf, stdin) != EOF) { - char *ptr, *tab; - char *path_name; - struct object_id oid; - unsigned int mode; - unsigned long ul; - int stage; - - /* This reads lines formatted in one of three formats: - * - * (1) mode SP sha1 TAB path - * The first format is what "git apply --index-info" - * reports, and used to reconstruct a partial tree - * that is used for phony merge base tree when falling - * back on 3-way merge. - * - * (2) mode SP type SP sha1 TAB path - * The second format is to stuff "git ls-tree" output - * into the index file. - * - * (3) mode SP sha1 SP stage TAB path - * This format is to put higher order stages into the - * index file and matches "git ls-files --stage" output. - */ - errno = 0; - ul = strtoul(buf.buf, &ptr, 8); - if (ptr == buf.buf || *ptr != ' ' - || errno || (unsigned int) ul != ul) - goto bad_line; - mode = ul; - - tab = strchr(ptr, '\t'); - if (!tab || tab - ptr < hexsz + 1) - goto bad_line; - - if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') { - stage = tab[-1] - '0'; - ptr = tab + 1; /* point at the head of path */ - tab = tab - 2; /* point at tail of sha1 */ - } - else { - stage = 0; - ptr = tab + 1; /* point at the head of path */ - } - - if (get_oid_hex(tab - hexsz, &oid) || - tab[-(hexsz + 1)] != ' ') - goto bad_line; - - path_name = ptr; - if (!nul_term_line && path_name[0] == '"') { - strbuf_reset(&uq); - if (unquote_c_style(&uq, path_name, NULL)) { - die("git update-index: bad quoting of path name"); - } - path_name = uq.buf; - } - - if (!verify_path(path_name, mode)) { - fprintf(stderr, "Ignoring path %s\n", path_name); - continue; - } - - if (!mode) { - /* mode == 0 means there is no such path -- remove */ - if (remove_file_from_cache(path_name)) - die("git update-index: unable to remove %s", - ptr); - } - else { - /* mode ' ' sha1 '\t' name - * ptr[-1] points at tab, - * ptr[-41] is at the beginning of sha1 - */ - ptr[-(hexsz + 2)] = ptr[-1] = 0; - if (add_cacheinfo(mode, &oid, path_name, stage)) - die("git update-index: unable to update %s", - path_name); - } - continue; - - bad_line: - die("malformed index info %s", buf.buf); - } - strbuf_release(&buf); - strbuf_release(&uq); -} - -static const char * const update_index_usage[] = { - N_("git update-index [<options>] [--] [<file>...]"), - NULL -}; - -static struct object_id head_oid; -static struct object_id merge_head_oid; - -static struct cache_entry *read_one_ent(const char *which, - struct object_id *ent, const char *path, - int namelen, int stage) -{ - unsigned short mode; - struct object_id oid; - struct cache_entry *ce; - - if (get_tree_entry(the_repository, ent, path, &oid, &mode)) { - if (which) - error("%s: not in %s branch.", path, which); - return NULL; - } - if (mode == S_IFDIR) { - if (which) - error("%s: not a blob in %s branch.", path, which); - return NULL; - } - ce = make_empty_cache_entry(&the_index, namelen); - - oidcpy(&ce->oid, &oid); - memcpy(ce->name, path, namelen); - ce->ce_flags = create_ce_flags(stage); - ce->ce_namelen = namelen; - ce->ce_mode = create_ce_mode(mode); - return ce; -} - -static int unresolve_one(const char *path) -{ - int namelen = strlen(path); - int pos; - int ret = 0; - struct cache_entry *ce_2 = NULL, *ce_3 = NULL; - - /* See if there is such entry in the index. */ - pos = cache_name_pos(path, namelen); - if (0 <= pos) { - /* already merged */ - pos = unmerge_cache_entry_at(pos); - if (pos < active_nr) { - const struct cache_entry *ce = active_cache[pos]; - if (ce_stage(ce) && - ce_namelen(ce) == namelen && - !memcmp(ce->name, path, namelen)) - return 0; - } - /* no resolve-undo information; fall back */ - } else { - /* If there isn't, either it is unmerged, or - * resolved as "removed" by mistake. We do not - * want to do anything in the former case. - */ - pos = -pos-1; - if (pos < active_nr) { - const struct cache_entry *ce = active_cache[pos]; - if (ce_namelen(ce) == namelen && - !memcmp(ce->name, path, namelen)) { - fprintf(stderr, - "%s: skipping still unmerged path.\n", - path); - goto free_return; - } - } - } - - /* Grab blobs from given path from HEAD and MERGE_HEAD, - * stuff HEAD version in stage #2, - * stuff MERGE_HEAD version in stage #3. - */ - ce_2 = read_one_ent("our", &head_oid, path, namelen, 2); - ce_3 = read_one_ent("their", &merge_head_oid, path, namelen, 3); - - if (!ce_2 || !ce_3) { - ret = -1; - goto free_return; - } - if (oideq(&ce_2->oid, &ce_3->oid) && - ce_2->ce_mode == ce_3->ce_mode) { - fprintf(stderr, "%s: identical in both, skipping.\n", - path); - goto free_return; - } - - remove_file_from_cache(path); - if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) { - error("%s: cannot add our version to the index.", path); - ret = -1; - goto free_return; - } - if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD)) - return 0; - error("%s: cannot add their version to the index.", path); - ret = -1; - free_return: - discard_cache_entry(ce_2); - discard_cache_entry(ce_3); - return ret; -} - -static void read_head_pointers(void) -{ - if (read_ref("HEAD", &head_oid)) - die("No HEAD -- no initial commit yet?"); - if (read_ref("MERGE_HEAD", &merge_head_oid)) { - fprintf(stderr, "Not in the middle of a merge.\n"); - exit(0); - } -} - -static int do_unresolve(int ac, const char **av, - const char *prefix, int prefix_length) -{ - int i; - int err = 0; - - /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we - * are not doing a merge, so exit with success status. - */ - read_head_pointers(); - - for (i = 1; i < ac; i++) { - const char *arg = av[i]; - char *p = prefix_path(prefix, prefix_length, arg); - err |= unresolve_one(p); - free(p); - } - return err; -} - -static int do_reupdate(int ac, const char **av, - const char *prefix) -{ - /* Read HEAD and run update-index on paths that are - * merged and already different between index and HEAD. - */ - int pos; - int has_head = 1; - struct pathspec pathspec; - - parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_CWD, - prefix, av + 1); - - if (read_ref("HEAD", &head_oid)) - /* If there is no HEAD, that means it is an initial - * commit. Update everything in the index. - */ - has_head = 0; - redo: - for (pos = 0; pos < active_nr; pos++) { - const struct cache_entry *ce = active_cache[pos]; - struct cache_entry *old = NULL; - int save_nr; - char *path; - - if (ce_stage(ce) || !ce_path_match(&the_index, ce, &pathspec, NULL)) - continue; - if (has_head) - old = read_one_ent(NULL, &head_oid, - ce->name, ce_namelen(ce), 0); - if (old && ce->ce_mode == old->ce_mode && - oideq(&ce->oid, &old->oid)) { - discard_cache_entry(old); - continue; /* unchanged */ - } - /* Be careful. The working tree may not have the - * path anymore, in which case, under 'allow_remove', - * or worse yet 'allow_replace', active_nr may decrease. - */ - save_nr = active_nr; - path = xstrdup(ce->name); - update_one(path); - free(path); - discard_cache_entry(old); - if (save_nr != active_nr) - goto redo; - } - clear_pathspec(&pathspec); - return 0; -} - -struct refresh_params { - unsigned int flags; - int *has_errors; -}; - -static int refresh(struct refresh_params *o, unsigned int flag) -{ - setup_work_tree(); - read_cache(); - *o->has_errors |= refresh_cache(o->flags | flag); - return 0; -} - -static int refresh_callback(const struct option *opt, - const char *arg, int unset) -{ - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - return refresh(opt->value, 0); -} - -static int really_refresh_callback(const struct option *opt, - const char *arg, int unset) -{ - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - return refresh(opt->value, REFRESH_REALLY); -} - -static int chmod_callback(const struct option *opt, - const char *arg, int unset) -{ - char *flip = opt->value; - BUG_ON_OPT_NEG(unset); - if ((arg[0] != '-' && arg[0] != '+') || arg[1] != 'x' || arg[2]) - return error("option 'chmod' expects \"+x\" or \"-x\""); - *flip = arg[0]; - return 0; -} - -static int resolve_undo_clear_callback(const struct option *opt, - const char *arg, int unset) -{ - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - resolve_undo_clear(); - return 0; -} - -static int parse_new_style_cacheinfo(const char *arg, - unsigned int *mode, - struct object_id *oid, - const char **path) -{ - unsigned long ul; - char *endp; - const char *p; - - if (!arg) - return -1; - - errno = 0; - ul = strtoul(arg, &endp, 8); - if (errno || endp == arg || *endp != ',' || (unsigned int) ul != ul) - return -1; /* not a new-style cacheinfo */ - *mode = ul; - endp++; - if (parse_oid_hex(endp, oid, &p) || *p != ',') - return -1; - *path = p + 1; - return 0; -} - -static enum parse_opt_result cacheinfo_callback( - struct parse_opt_ctx_t *ctx, const struct option *opt, - const char *arg, int unset) -{ - struct object_id oid; - unsigned int mode; - const char *path; - - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - - if (!parse_new_style_cacheinfo(ctx->argv[1], &mode, &oid, &path)) { - if (add_cacheinfo(mode, &oid, path, 0)) - die("git update-index: --cacheinfo cannot add %s", path); - ctx->argv++; - ctx->argc--; - return 0; - } - if (ctx->argc <= 3) - return error("option 'cacheinfo' expects <mode>,<sha1>,<path>"); - if (strtoul_ui(*++ctx->argv, 8, &mode) || - get_oid_hex(*++ctx->argv, &oid) || - add_cacheinfo(mode, &oid, *++ctx->argv, 0)) - die("git update-index: --cacheinfo cannot add %s", *ctx->argv); - ctx->argc -= 3; - return 0; -} - -static enum parse_opt_result stdin_cacheinfo_callback( - struct parse_opt_ctx_t *ctx, const struct option *opt, - const char *arg, int unset) -{ - int *nul_term_line = opt->value; - - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - - if (ctx->argc != 1) - return error("option '%s' must be the last argument", opt->long_name); - allow_add = allow_replace = allow_remove = 1; - read_index_info(*nul_term_line); - return 0; -} - -static enum parse_opt_result stdin_callback( - struct parse_opt_ctx_t *ctx, const struct option *opt, - const char *arg, int unset) -{ - int *read_from_stdin = opt->value; - - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - - if (ctx->argc != 1) - return error("option '%s' must be the last argument", opt->long_name); - *read_from_stdin = 1; - return 0; -} - -static enum parse_opt_result unresolve_callback( - struct parse_opt_ctx_t *ctx, const struct option *opt, - const char *arg, int unset) -{ - int *has_errors = opt->value; - const char *prefix = startup_info->prefix; - - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - - /* consume remaining arguments. */ - *has_errors = do_unresolve(ctx->argc, ctx->argv, - prefix, prefix ? strlen(prefix) : 0); - if (*has_errors) - active_cache_changed = 0; - - ctx->argv += ctx->argc - 1; - ctx->argc = 1; - return 0; -} - -static enum parse_opt_result reupdate_callback( - struct parse_opt_ctx_t *ctx, const struct option *opt, - const char *arg, int unset) -{ - int *has_errors = opt->value; - const char *prefix = startup_info->prefix; - - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - - /* consume remaining arguments. */ - setup_work_tree(); - *has_errors = do_reupdate(ctx->argc, ctx->argv, prefix); - if (*has_errors) - active_cache_changed = 0; - - ctx->argv += ctx->argc - 1; - ctx->argc = 1; - return 0; -} - -int cmd_update_index(int argc, const char **argv, const char *prefix) -{ - int newfd, entries, has_errors = 0, nul_term_line = 0; - enum uc_mode untracked_cache = UC_UNSPECIFIED; - int read_from_stdin = 0; - int prefix_length = prefix ? strlen(prefix) : 0; - int preferred_index_format = 0; - char set_executable_bit = 0; - struct refresh_params refresh_args = {0, &has_errors}; - int lock_error = 0; - int split_index = -1; - int force_write = 0; - int fsmonitor = -1; - struct lock_file lock_file = LOCK_INIT; - struct parse_opt_ctx_t ctx; - strbuf_getline_fn getline_fn; - int parseopt_state = PARSE_OPT_UNKNOWN; - struct repository *r = the_repository; - struct option options[] = { - OPT_BIT('q', NULL, &refresh_args.flags, - N_("continue refresh even when index needs update"), - REFRESH_QUIET), - OPT_BIT(0, "ignore-submodules", &refresh_args.flags, - N_("refresh: ignore submodules"), - REFRESH_IGNORE_SUBMODULES), - OPT_SET_INT(0, "add", &allow_add, - N_("do not ignore new files"), 1), - OPT_SET_INT(0, "replace", &allow_replace, - N_("let files replace directories and vice-versa"), 1), - OPT_SET_INT(0, "remove", &allow_remove, - N_("notice files missing from worktree"), 1), - OPT_BIT(0, "unmerged", &refresh_args.flags, - N_("refresh even if index contains unmerged entries"), - REFRESH_UNMERGED), - OPT_CALLBACK_F(0, "refresh", &refresh_args, NULL, - N_("refresh stat information"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, - refresh_callback), - OPT_CALLBACK_F(0, "really-refresh", &refresh_args, NULL, - N_("like --refresh, but ignore assume-unchanged setting"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, - really_refresh_callback), - {OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL, - N_("<mode>,<object>,<path>"), - N_("add the specified entry to the index"), - PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */ - PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, - NULL, 0, - cacheinfo_callback}, - OPT_CALLBACK_F(0, "chmod", &set_executable_bit, "(+|-)x", - N_("override the executable bit of the listed files"), - PARSE_OPT_NONEG, - chmod_callback), - {OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL, - N_("mark files as \"not changing\""), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, - {OPTION_SET_INT, 0, "no-assume-unchanged", &mark_valid_only, NULL, - N_("clear assumed-unchanged bit"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, - {OPTION_SET_INT, 0, "skip-worktree", &mark_skip_worktree_only, NULL, - N_("mark files as \"index-only\""), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, - {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL, - N_("clear skip-worktree bit"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, - OPT_BOOL(0, "ignore-skip-worktree-entries", &ignore_skip_worktree_entries, - N_("do not touch index-only entries")), - OPT_SET_INT(0, "info-only", &info_only, - N_("add to index only; do not add content to object database"), 1), - OPT_SET_INT(0, "force-remove", &force_remove, - N_("remove named paths even if present in worktree"), 1), - OPT_BOOL('z', NULL, &nul_term_line, - N_("with --stdin: input lines are terminated by null bytes")), - {OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL, - N_("read list of paths to be updated from standard input"), - PARSE_OPT_NONEG | PARSE_OPT_NOARG, - NULL, 0, stdin_callback}, - {OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &nul_term_line, NULL, - N_("add entries from standard input to the index"), - PARSE_OPT_NONEG | PARSE_OPT_NOARG, - NULL, 0, stdin_cacheinfo_callback}, - {OPTION_LOWLEVEL_CALLBACK, 0, "unresolve", &has_errors, NULL, - N_("repopulate stages #2 and #3 for the listed paths"), - PARSE_OPT_NONEG | PARSE_OPT_NOARG, - NULL, 0, unresolve_callback}, - {OPTION_LOWLEVEL_CALLBACK, 'g', "again", &has_errors, NULL, - N_("only update entries that differ from HEAD"), - PARSE_OPT_NONEG | PARSE_OPT_NOARG, - NULL, 0, reupdate_callback}, - OPT_BIT(0, "ignore-missing", &refresh_args.flags, - N_("ignore files missing from worktree"), - REFRESH_IGNORE_MISSING), - OPT_SET_INT(0, "verbose", &verbose, - N_("report actions to standard output"), 1), - OPT_CALLBACK_F(0, "clear-resolve-undo", NULL, NULL, - N_("(for porcelains) forget saved unresolved conflicts"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, - resolve_undo_clear_callback), - OPT_INTEGER(0, "index-version", &preferred_index_format, - N_("write index in this format")), - OPT_BOOL(0, "split-index", &split_index, - N_("enable or disable split index")), - OPT_BOOL(0, "untracked-cache", &untracked_cache, - N_("enable/disable untracked cache")), - OPT_SET_INT(0, "test-untracked-cache", &untracked_cache, - N_("test if the filesystem supports untracked cache"), UC_TEST), - OPT_SET_INT(0, "force-untracked-cache", &untracked_cache, - N_("enable untracked cache without testing the filesystem"), UC_FORCE), - OPT_SET_INT(0, "force-write-index", &force_write, - N_("write out the index even if is not flagged as changed"), 1), - OPT_BOOL(0, "fsmonitor", &fsmonitor, - N_("enable or disable file system monitor")), - {OPTION_SET_INT, 0, "fsmonitor-valid", &mark_fsmonitor_only, NULL, - N_("mark files as fsmonitor valid"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, - {OPTION_SET_INT, 0, "no-fsmonitor-valid", &mark_fsmonitor_only, NULL, - N_("clear fsmonitor valid bit"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, - OPT_END() - }; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage_with_options(update_index_usage, options); - - git_config(git_default_config, NULL); - - /* we will diagnose later if it turns out that we need to update it */ - newfd = hold_locked_index(&lock_file, 0); - if (newfd < 0) - lock_error = errno; - - entries = read_cache(); - if (entries < 0) - die("cache corrupted"); - - the_index.updated_skipworktree = 1; - - /* - * Custom copy of parse_options() because we want to handle - * filename arguments as they come. - */ - parse_options_start(&ctx, argc, argv, prefix, - options, PARSE_OPT_STOP_AT_NON_OPTION); - while (ctx.argc) { - if (parseopt_state != PARSE_OPT_DONE) - parseopt_state = parse_options_step(&ctx, options, - update_index_usage); - if (!ctx.argc) - break; - switch (parseopt_state) { - case PARSE_OPT_HELP: - case PARSE_OPT_ERROR: - exit(129); - case PARSE_OPT_COMPLETE: - exit(0); - case PARSE_OPT_NON_OPTION: - case PARSE_OPT_DONE: - { - const char *path = ctx.argv[0]; - char *p; - - setup_work_tree(); - p = prefix_path(prefix, prefix_length, path); - update_one(p); - if (set_executable_bit) - chmod_path(set_executable_bit, p); - free(p); - ctx.argc--; - ctx.argv++; - break; - } - case PARSE_OPT_UNKNOWN: - if (ctx.argv[0][1] == '-') - error("unknown option '%s'", ctx.argv[0] + 2); - else - error("unknown switch '%c'", *ctx.opt); - usage_with_options(update_index_usage, options); - } - } - argc = parse_options_end(&ctx); - - getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; - if (preferred_index_format) { - if (preferred_index_format < INDEX_FORMAT_LB || - INDEX_FORMAT_UB < preferred_index_format) - die("index-version %d not in range: %d..%d", - preferred_index_format, - INDEX_FORMAT_LB, INDEX_FORMAT_UB); - - if (the_index.version != preferred_index_format) - active_cache_changed |= SOMETHING_CHANGED; - the_index.version = preferred_index_format; - } - - if (read_from_stdin) { - struct strbuf buf = STRBUF_INIT; - struct strbuf unquoted = STRBUF_INIT; - - setup_work_tree(); - while (getline_fn(&buf, stdin) != EOF) { - char *p; - if (!nul_term_line && buf.buf[0] == '"') { - strbuf_reset(&unquoted); - if (unquote_c_style(&unquoted, buf.buf, NULL)) - die("line is badly quoted"); - strbuf_swap(&buf, &unquoted); - } - p = prefix_path(prefix, prefix_length, buf.buf); - update_one(p); - if (set_executable_bit) - chmod_path(set_executable_bit, p); - free(p); - } - strbuf_release(&unquoted); - strbuf_release(&buf); - } - - if (split_index > 0) { - if (git_config_get_split_index() == 0) - warning(_("core.splitIndex is set to false; " - "remove or change it, if you really want to " - "enable split index")); - if (the_index.split_index) - the_index.cache_changed |= SPLIT_INDEX_ORDERED; - else - add_split_index(&the_index); - } else if (!split_index) { - if (git_config_get_split_index() == 1) - warning(_("core.splitIndex is set to true; " - "remove or change it, if you really want to " - "disable split index")); - remove_split_index(&the_index); - } - - prepare_repo_settings(r); - switch (untracked_cache) { - case UC_UNSPECIFIED: - break; - case UC_DISABLE: - if (r->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE) - warning(_("core.untrackedCache is set to true; " - "remove or change it, if you really want to " - "disable the untracked cache")); - remove_untracked_cache(&the_index); - report(_("Untracked cache disabled")); - break; - case UC_TEST: - setup_work_tree(); - return !test_if_untracked_cache_is_supported(); - case UC_ENABLE: - case UC_FORCE: - if (r->settings.core_untracked_cache == UNTRACKED_CACHE_REMOVE) - warning(_("core.untrackedCache is set to false; " - "remove or change it, if you really want to " - "enable the untracked cache")); - add_untracked_cache(&the_index); - report(_("Untracked cache enabled for '%s'"), get_git_work_tree()); - break; - default: - BUG("bad untracked_cache value: %d", untracked_cache); - } - - if (fsmonitor > 0) { - if (git_config_get_fsmonitor() == 0) - warning(_("core.fsmonitor is unset; " - "set it if you really want to " - "enable fsmonitor")); - add_fsmonitor(&the_index); - report(_("fsmonitor enabled")); - } else if (!fsmonitor) { - if (git_config_get_fsmonitor() == 1) - warning(_("core.fsmonitor is set; " - "remove it if you really want to " - "disable fsmonitor")); - remove_fsmonitor(&the_index); - report(_("fsmonitor disabled")); - } - - if (active_cache_changed || force_write) { - if (newfd < 0) { - if (refresh_args.flags & REFRESH_QUIET) - exit(128); - unable_to_lock_die(get_index_file(), lock_error); - } - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) - die("Unable to write new index file"); - } - - rollback_lock_file(&lock_file); - - return has_errors ? 1 : 0; -} diff --git a/third_party/git/builtin/update-ref.c b/third_party/git/builtin/update-ref.c deleted file mode 100644 index 8a2df4459c66..000000000000 --- a/third_party/git/builtin/update-ref.c +++ /dev/null @@ -1,560 +0,0 @@ -#include "cache.h" -#include "config.h" -#include "refs.h" -#include "builtin.h" -#include "parse-options.h" -#include "quote.h" -#include "strvec.h" - -static const char * const git_update_ref_usage[] = { - N_("git update-ref [<options>] -d <refname> [<old-val>]"), - N_("git update-ref [<options>] <refname> <new-val> [<old-val>]"), - N_("git update-ref [<options>] --stdin [-z]"), - NULL -}; - -static char line_termination = '\n'; -static unsigned int update_flags; -static unsigned int default_flags; -static unsigned create_reflog_flag; -static const char *msg; - -/* - * Parse one whitespace- or NUL-terminated, possibly C-quoted argument - * and append the result to arg. Return a pointer to the terminator. - * Die if there is an error in how the argument is C-quoted. This - * function is only used if not -z. - */ -static const char *parse_arg(const char *next, struct strbuf *arg) -{ - if (*next == '"') { - const char *orig = next; - - if (unquote_c_style(arg, next, &next)) - die("badly quoted argument: %s", orig); - if (*next && !isspace(*next)) - die("unexpected character after quoted argument: %s", orig); - } else { - while (*next && !isspace(*next)) - strbuf_addch(arg, *next++); - } - - return next; -} - -/* - * Parse the reference name immediately after "command SP". If not - * -z, then handle C-quoting. Return a pointer to a newly allocated - * string containing the name of the reference, or NULL if there was - * an error. Update *next to point at the character that terminates - * the argument. Die if C-quoting is malformed or the reference name - * is invalid. - */ -static char *parse_refname(const char **next) -{ - struct strbuf ref = STRBUF_INIT; - - if (line_termination) { - /* Without -z, use the next argument */ - *next = parse_arg(*next, &ref); - } else { - /* With -z, use everything up to the next NUL */ - strbuf_addstr(&ref, *next); - *next += ref.len; - } - - if (!ref.len) { - strbuf_release(&ref); - return NULL; - } - - if (check_refname_format(ref.buf, REFNAME_ALLOW_ONELEVEL)) - die("invalid ref format: %s", ref.buf); - - return strbuf_detach(&ref, NULL); -} - -/* - * The value being parsed is <oldvalue> (as opposed to <newvalue>; the - * difference affects which error messages are generated): - */ -#define PARSE_SHA1_OLD 0x01 - -/* - * For backwards compatibility, accept an empty string for update's - * <newvalue> in binary mode to be equivalent to specifying zeros. - */ -#define PARSE_SHA1_ALLOW_EMPTY 0x02 - -/* - * Parse an argument separator followed by the next argument, if any. - * If there is an argument, convert it to a SHA-1, write it to sha1, - * set *next to point at the character terminating the argument, and - * return 0. If there is no argument at all (not even the empty - * string), return 1 and leave *next unchanged. If the value is - * provided but cannot be converted to a SHA-1, die. flags can - * include PARSE_SHA1_OLD and/or PARSE_SHA1_ALLOW_EMPTY. - */ -static int parse_next_oid(const char **next, const char *end, - struct object_id *oid, - const char *command, const char *refname, - int flags) -{ - struct strbuf arg = STRBUF_INIT; - int ret = 0; - - if (*next == end) - goto eof; - - if (line_termination) { - /* Without -z, consume SP and use next argument */ - if (!**next || **next == line_termination) - return 1; - if (**next != ' ') - die("%s %s: expected SP but got: %s", - command, refname, *next); - (*next)++; - *next = parse_arg(*next, &arg); - if (arg.len) { - if (get_oid(arg.buf, oid)) - goto invalid; - } else { - /* Without -z, an empty value means all zeros: */ - oidclr(oid); - } - } else { - /* With -z, read the next NUL-terminated line */ - if (**next) - die("%s %s: expected NUL but got: %s", - command, refname, *next); - (*next)++; - if (*next == end) - goto eof; - strbuf_addstr(&arg, *next); - *next += arg.len; - - if (arg.len) { - if (get_oid(arg.buf, oid)) - goto invalid; - } else if (flags & PARSE_SHA1_ALLOW_EMPTY) { - /* With -z, treat an empty value as all zeros: */ - warning("%s %s: missing <newvalue>, treating as zero", - command, refname); - oidclr(oid); - } else { - /* - * With -z, an empty non-required value means - * unspecified: - */ - ret = 1; - } - } - - strbuf_release(&arg); - - return ret; - - invalid: - die(flags & PARSE_SHA1_OLD ? - "%s %s: invalid <oldvalue>: %s" : - "%s %s: invalid <newvalue>: %s", - command, refname, arg.buf); - - eof: - die(flags & PARSE_SHA1_OLD ? - "%s %s: unexpected end of input when reading <oldvalue>" : - "%s %s: unexpected end of input when reading <newvalue>", - command, refname); -} - - -/* - * The following five parse_cmd_*() functions parse the corresponding - * command. In each case, next points at the character following the - * command name and the following space. They each return a pointer - * to the character terminating the command, and die with an - * explanatory message if there are any parsing problems. All of - * these functions handle either text or binary format input, - * depending on how line_termination is set. - */ - -static void parse_cmd_update(struct ref_transaction *transaction, - const char *next, const char *end) -{ - struct strbuf err = STRBUF_INIT; - char *refname; - struct object_id new_oid, old_oid; - int have_old; - - refname = parse_refname(&next); - if (!refname) - die("update: missing <ref>"); - - if (parse_next_oid(&next, end, &new_oid, "update", refname, - PARSE_SHA1_ALLOW_EMPTY)) - die("update %s: missing <newvalue>", refname); - - have_old = !parse_next_oid(&next, end, &old_oid, "update", refname, - PARSE_SHA1_OLD); - - if (*next != line_termination) - die("update %s: extra input: %s", refname, next); - - if (ref_transaction_update(transaction, refname, - &new_oid, have_old ? &old_oid : NULL, - update_flags | create_reflog_flag, - msg, &err)) - die("%s", err.buf); - - update_flags = default_flags; - free(refname); - strbuf_release(&err); -} - -static void parse_cmd_create(struct ref_transaction *transaction, - const char *next, const char *end) -{ - struct strbuf err = STRBUF_INIT; - char *refname; - struct object_id new_oid; - - refname = parse_refname(&next); - if (!refname) - die("create: missing <ref>"); - - if (parse_next_oid(&next, end, &new_oid, "create", refname, 0)) - die("create %s: missing <newvalue>", refname); - - if (is_null_oid(&new_oid)) - die("create %s: zero <newvalue>", refname); - - if (*next != line_termination) - die("create %s: extra input: %s", refname, next); - - if (ref_transaction_create(transaction, refname, &new_oid, - update_flags | create_reflog_flag, - msg, &err)) - die("%s", err.buf); - - update_flags = default_flags; - free(refname); - strbuf_release(&err); -} - -static void parse_cmd_delete(struct ref_transaction *transaction, - const char *next, const char *end) -{ - struct strbuf err = STRBUF_INIT; - char *refname; - struct object_id old_oid; - int have_old; - - refname = parse_refname(&next); - if (!refname) - die("delete: missing <ref>"); - - if (parse_next_oid(&next, end, &old_oid, "delete", refname, - PARSE_SHA1_OLD)) { - have_old = 0; - } else { - if (is_null_oid(&old_oid)) - die("delete %s: zero <oldvalue>", refname); - have_old = 1; - } - - if (*next != line_termination) - die("delete %s: extra input: %s", refname, next); - - if (ref_transaction_delete(transaction, refname, - have_old ? &old_oid : NULL, - update_flags, msg, &err)) - die("%s", err.buf); - - update_flags = default_flags; - free(refname); - strbuf_release(&err); -} - -static void parse_cmd_verify(struct ref_transaction *transaction, - const char *next, const char *end) -{ - struct strbuf err = STRBUF_INIT; - char *refname; - struct object_id old_oid; - - refname = parse_refname(&next); - if (!refname) - die("verify: missing <ref>"); - - if (parse_next_oid(&next, end, &old_oid, "verify", refname, - PARSE_SHA1_OLD)) - oidclr(&old_oid); - - if (*next != line_termination) - die("verify %s: extra input: %s", refname, next); - - if (ref_transaction_verify(transaction, refname, &old_oid, - update_flags, &err)) - die("%s", err.buf); - - update_flags = default_flags; - free(refname); - strbuf_release(&err); -} - -static void parse_cmd_option(struct ref_transaction *transaction, - const char *next, const char *end) -{ - const char *rest; - if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination) - update_flags |= REF_NO_DEREF; - else - die("option unknown: %s", next); -} - -static void parse_cmd_start(struct ref_transaction *transaction, - const char *next, const char *end) -{ - if (*next != line_termination) - die("start: extra input: %s", next); - puts("start: ok"); -} - -static void parse_cmd_prepare(struct ref_transaction *transaction, - const char *next, const char *end) -{ - struct strbuf error = STRBUF_INIT; - if (*next != line_termination) - die("prepare: extra input: %s", next); - if (ref_transaction_prepare(transaction, &error)) - die("prepare: %s", error.buf); - puts("prepare: ok"); -} - -static void parse_cmd_abort(struct ref_transaction *transaction, - const char *next, const char *end) -{ - struct strbuf error = STRBUF_INIT; - if (*next != line_termination) - die("abort: extra input: %s", next); - if (ref_transaction_abort(transaction, &error)) - die("abort: %s", error.buf); - puts("abort: ok"); -} - -static void parse_cmd_commit(struct ref_transaction *transaction, - const char *next, const char *end) -{ - struct strbuf error = STRBUF_INIT; - if (*next != line_termination) - die("commit: extra input: %s", next); - if (ref_transaction_commit(transaction, &error)) - die("commit: %s", error.buf); - puts("commit: ok"); - ref_transaction_free(transaction); -} - -enum update_refs_state { - /* Non-transactional state open for updates. */ - UPDATE_REFS_OPEN, - /* A transaction has been started. */ - UPDATE_REFS_STARTED, - /* References are locked and ready for commit */ - UPDATE_REFS_PREPARED, - /* Transaction has been committed or closed. */ - UPDATE_REFS_CLOSED, -}; - -static const struct parse_cmd { - const char *prefix; - void (*fn)(struct ref_transaction *, const char *, const char *); - unsigned args; - enum update_refs_state state; -} command[] = { - { "update", parse_cmd_update, 3, UPDATE_REFS_OPEN }, - { "create", parse_cmd_create, 2, UPDATE_REFS_OPEN }, - { "delete", parse_cmd_delete, 2, UPDATE_REFS_OPEN }, - { "verify", parse_cmd_verify, 2, UPDATE_REFS_OPEN }, - { "option", parse_cmd_option, 1, UPDATE_REFS_OPEN }, - { "start", parse_cmd_start, 0, UPDATE_REFS_STARTED }, - { "prepare", parse_cmd_prepare, 0, UPDATE_REFS_PREPARED }, - { "abort", parse_cmd_abort, 0, UPDATE_REFS_CLOSED }, - { "commit", parse_cmd_commit, 0, UPDATE_REFS_CLOSED }, -}; - -static void update_refs_stdin(void) -{ - struct strbuf input = STRBUF_INIT, err = STRBUF_INIT; - enum update_refs_state state = UPDATE_REFS_OPEN; - struct ref_transaction *transaction; - int i, j; - - transaction = ref_transaction_begin(&err); - if (!transaction) - die("%s", err.buf); - - /* Read each line dispatch its command */ - while (!strbuf_getwholeline(&input, stdin, line_termination)) { - const struct parse_cmd *cmd = NULL; - - if (*input.buf == line_termination) - die("empty command in input"); - else if (isspace(*input.buf)) - die("whitespace before command: %s", input.buf); - - for (i = 0; i < ARRAY_SIZE(command); i++) { - const char *prefix = command[i].prefix; - char c; - - if (!starts_with(input.buf, prefix)) - continue; - - /* - * If the command has arguments, verify that it's - * followed by a space. Otherwise, it shall be followed - * by a line terminator. - */ - c = command[i].args ? ' ' : line_termination; - if (input.buf[strlen(prefix)] != c) - continue; - - cmd = &command[i]; - break; - } - if (!cmd) - die("unknown command: %s", input.buf); - - /* - * Read additional arguments if NUL-terminated. Do not raise an - * error in case there is an early EOF to let the command - * handle missing arguments with a proper error message. - */ - for (j = 1; line_termination == '\0' && j < cmd->args; j++) - if (strbuf_appendwholeline(&input, stdin, line_termination)) - break; - - switch (state) { - case UPDATE_REFS_OPEN: - case UPDATE_REFS_STARTED: - /* Do not downgrade a transaction to a non-transaction. */ - if (cmd->state >= state) - state = cmd->state; - break; - case UPDATE_REFS_PREPARED: - if (cmd->state != UPDATE_REFS_CLOSED) - die("prepared transactions can only be closed"); - state = cmd->state; - break; - case UPDATE_REFS_CLOSED: - die("transaction is closed"); - break; - } - - cmd->fn(transaction, input.buf + strlen(cmd->prefix) + !!cmd->args, - input.buf + input.len); - } - - switch (state) { - case UPDATE_REFS_OPEN: - /* Commit by default if no transaction was requested. */ - if (ref_transaction_commit(transaction, &err)) - die("%s", err.buf); - ref_transaction_free(transaction); - break; - case UPDATE_REFS_STARTED: - case UPDATE_REFS_PREPARED: - /* If using a transaction, we want to abort it. */ - if (ref_transaction_abort(transaction, &err)) - die("%s", err.buf); - break; - case UPDATE_REFS_CLOSED: - /* Otherwise no need to do anything, the transaction was closed already. */ - break; - } - - strbuf_release(&err); - strbuf_release(&input); -} - -int cmd_update_ref(int argc, const char **argv, const char *prefix) -{ - const char *refname, *oldval; - struct object_id oid, oldoid; - int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0; - int create_reflog = 0; - struct option options[] = { - OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")), - OPT_BOOL('d', NULL, &delete, N_("delete the reference")), - OPT_BOOL( 0 , "no-deref", &no_deref, - N_("update <refname> not the one it points to")), - OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")), - OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")), - OPT_BOOL( 0 , "create-reflog", &create_reflog, N_("create a reflog")), - OPT_END(), - }; - - git_config(git_default_config, NULL); - argc = parse_options(argc, argv, prefix, options, git_update_ref_usage, - 0); - if (msg && !*msg) - die("Refusing to perform update with empty message."); - - create_reflog_flag = create_reflog ? REF_FORCE_CREATE_REFLOG : 0; - - if (no_deref) { - default_flags = REF_NO_DEREF; - update_flags = default_flags; - } - - if (read_stdin) { - if (delete || argc > 0) - usage_with_options(git_update_ref_usage, options); - if (end_null) - line_termination = '\0'; - update_refs_stdin(); - return 0; - } - - if (end_null) - usage_with_options(git_update_ref_usage, options); - - if (delete) { - if (argc < 1 || argc > 2) - usage_with_options(git_update_ref_usage, options); - refname = argv[0]; - oldval = argv[1]; - } else { - const char *value; - if (argc < 2 || argc > 3) - usage_with_options(git_update_ref_usage, options); - refname = argv[0]; - value = argv[1]; - oldval = argv[2]; - if (get_oid(value, &oid)) - die("%s: not a valid SHA1", value); - } - - if (oldval) { - if (!*oldval) - /* - * The empty string implies that the reference - * must not already exist: - */ - oidclr(&oldoid); - else if (get_oid(oldval, &oldoid)) - die("%s: not a valid old SHA1", oldval); - } - - if (delete) - /* - * For purposes of backwards compatibility, we treat - * NULL_SHA1 as "don't care" here: - */ - return delete_ref(msg, refname, - (oldval && !is_null_oid(&oldoid)) ? &oldoid : NULL, - default_flags); - else - return update_ref(msg, refname, &oid, oldval ? &oldoid : NULL, - default_flags | create_reflog_flag, - UPDATE_REFS_DIE_ON_ERR); -} diff --git a/third_party/git/builtin/update-server-info.c b/third_party/git/builtin/update-server-info.c deleted file mode 100644 index 4321a344567e..000000000000 --- a/third_party/git/builtin/update-server-info.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "cache.h" -#include "config.h" -#include "builtin.h" -#include "parse-options.h" - -static const char * const update_server_info_usage[] = { - N_("git update-server-info [--force]"), - NULL -}; - -int cmd_update_server_info(int argc, const char **argv, const char *prefix) -{ - int force = 0; - struct option options[] = { - OPT__FORCE(&force, N_("update the info files from scratch"), 0), - OPT_END() - }; - - git_config(git_default_config, NULL); - argc = parse_options(argc, argv, prefix, options, - update_server_info_usage, 0); - if (argc > 0) - usage_with_options(update_server_info_usage, options); - - return !!update_server_info(force); -} diff --git a/third_party/git/builtin/upload-archive.c b/third_party/git/builtin/upload-archive.c deleted file mode 100644 index 24654b4c9bf0..000000000000 --- a/third_party/git/builtin/upload-archive.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2006 Franck Bui-Huu - */ -#include "cache.h" -#include "builtin.h" -#include "archive.h" -#include "pkt-line.h" -#include "sideband.h" -#include "run-command.h" -#include "strvec.h" - -static const char upload_archive_usage[] = - "git upload-archive <repo>"; - -static const char deadchild[] = -"git upload-archive: archiver died with error"; - -#define MAX_ARGS (64) - -int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix) -{ - struct strvec sent_argv = STRVEC_INIT; - const char *arg_cmd = "argument "; - - if (argc != 2 || !strcmp(argv[1], "-h")) - usage(upload_archive_usage); - - if (!enter_repo(argv[1], 0)) - die("'%s' does not appear to be a git repository", argv[1]); - - init_archivers(); - - /* put received options in sent_argv[] */ - strvec_push(&sent_argv, "git-upload-archive"); - for (;;) { - char *buf = packet_read_line(0, NULL); - if (!buf) - break; /* got a flush */ - if (sent_argv.nr > MAX_ARGS) - die("Too many options (>%d)", MAX_ARGS - 1); - - if (!starts_with(buf, arg_cmd)) - die("'argument' token or flush expected"); - strvec_push(&sent_argv, buf + strlen(arg_cmd)); - } - - /* parse all options sent by the client */ - return write_archive(sent_argv.nr, sent_argv.v, prefix, - the_repository, NULL, 1); -} - -__attribute__((format (printf, 1, 2))) -static void error_clnt(const char *fmt, ...) -{ - struct strbuf buf = STRBUF_INIT; - va_list params; - - va_start(params, fmt); - strbuf_vaddf(&buf, fmt, params); - va_end(params); - send_sideband(1, 3, buf.buf, buf.len, LARGE_PACKET_MAX); - die("sent error to the client: %s", buf.buf); -} - -static ssize_t process_input(int child_fd, int band) -{ - char buf[16384]; - ssize_t sz = read(child_fd, buf, sizeof(buf)); - if (sz < 0) { - if (errno != EAGAIN && errno != EINTR) - error_clnt("read error: %s\n", strerror(errno)); - return sz; - } - send_sideband(1, band, buf, sz, LARGE_PACKET_MAX); - return sz; -} - -int cmd_upload_archive(int argc, const char **argv, const char *prefix) -{ - struct child_process writer = { argv }; - - if (argc == 2 && !strcmp(argv[1], "-h")) - usage(upload_archive_usage); - - /* - * Set up sideband subprocess. - * - * We (parent) monitor and read from child, sending its fd#1 and fd#2 - * multiplexed out to our fd#1. If the child dies, we tell the other - * end over channel #3. - */ - argv[0] = "upload-archive--writer"; - writer.out = writer.err = -1; - writer.git_cmd = 1; - if (start_command(&writer)) { - int err = errno; - packet_write_fmt(1, "NACK unable to spawn subprocess\n"); - die("upload-archive: %s", strerror(err)); - } - - packet_write_fmt(1, "ACK\n"); - packet_flush(1); - - while (1) { - struct pollfd pfd[2]; - - pfd[0].fd = writer.out; - pfd[0].events = POLLIN; - pfd[1].fd = writer.err; - pfd[1].events = POLLIN; - if (poll(pfd, 2, -1) < 0) { - if (errno != EINTR) { - error_errno("poll failed resuming"); - sleep(1); - } - continue; - } - if (pfd[1].revents & POLLIN) - /* Status stream ready */ - if (process_input(pfd[1].fd, 2)) - continue; - if (pfd[0].revents & POLLIN) - /* Data stream ready */ - if (process_input(pfd[0].fd, 1)) - continue; - - if (finish_command(&writer)) - error_clnt("%s", deadchild); - packet_flush(1); - break; - } - return 0; -} diff --git a/third_party/git/builtin/upload-pack.c b/third_party/git/builtin/upload-pack.c deleted file mode 100644 index 6da8fa2607c6..000000000000 --- a/third_party/git/builtin/upload-pack.c +++ /dev/null @@ -1,74 +0,0 @@ -#include "cache.h" -#include "builtin.h" -#include "exec-cmd.h" -#include "pkt-line.h" -#include "parse-options.h" -#include "protocol.h" -#include "upload-pack.h" -#include "serve.h" - -static const char * const upload_pack_usage[] = { - N_("git upload-pack [<options>] <dir>"), - NULL -}; - -int cmd_upload_pack(int argc, const char **argv, const char *prefix) -{ - const char *dir; - int strict = 0; - struct upload_pack_options opts = { 0 }; - struct serve_options serve_opts = SERVE_OPTIONS_INIT; - struct option options[] = { - OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc, - N_("quit after a single request/response exchange")), - OPT_BOOL(0, "advertise-refs", &opts.advertise_refs, - N_("exit immediately after initial ref advertisement")), - OPT_BOOL(0, "strict", &strict, - N_("do not try <directory>/.git/ if <directory> is no Git directory")), - OPT_INTEGER(0, "timeout", &opts.timeout, - N_("interrupt transfer after <n> seconds of inactivity")), - OPT_END() - }; - - packet_trace_identity("upload-pack"); - read_replace_refs = 0; - - argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0); - - if (argc != 1) - usage_with_options(upload_pack_usage, options); - - if (opts.timeout) - opts.daemon_mode = 1; - - setup_path(); - - dir = argv[0]; - - if (!enter_repo(dir, strict)) - die("'%s' does not appear to be a git repository", dir); - - switch (determine_protocol_version_server()) { - case protocol_v2: - serve_opts.advertise_capabilities = opts.advertise_refs; - serve_opts.stateless_rpc = opts.stateless_rpc; - serve(&serve_opts); - break; - case protocol_v1: - /* - * v1 is just the original protocol with a version string, - * so just fall through after writing the version string. - */ - if (opts.advertise_refs || !opts.stateless_rpc) - packet_write_fmt(1, "version 1\n"); - - /* fallthrough */ - case protocol_v0: - upload_pack(&opts); - break; - case protocol_unknown_version: - BUG("unknown protocol version"); - } - - return 0; -} diff --git a/third_party/git/builtin/var.c b/third_party/git/builtin/var.c deleted file mode 100644 index 6c6f46b4aeaf..000000000000 --- a/third_party/git/builtin/var.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * GIT - The information manager from hell - * - * Copyright (C) Eric Biederman, 2005 - */ -#include "builtin.h" -#include "config.h" - -static const char var_usage[] = "git var (-l | <variable>)"; - -static const char *editor(int flag) -{ - const char *pgm = git_editor(); - - if (!pgm && flag & IDENT_STRICT) - die("Terminal is dumb, but EDITOR unset"); - - return pgm; -} - -static const char *pager(int flag) -{ - const char *pgm = git_pager(1); - - if (!pgm) - pgm = "cat"; - return pgm; -} - -struct git_var { - const char *name; - const char *(*read)(int); -}; -static struct git_var git_vars[] = { - { "GIT_COMMITTER_IDENT", git_committer_info }, - { "GIT_AUTHOR_IDENT", git_author_info }, - { "GIT_EDITOR", editor }, - { "GIT_PAGER", pager }, - { "", NULL }, -}; - -static void list_vars(void) -{ - struct git_var *ptr; - const char *val; - - for (ptr = git_vars; ptr->read; ptr++) - if ((val = ptr->read(0))) - printf("%s=%s\n", ptr->name, val); -} - -static const char *read_var(const char *var) -{ - struct git_var *ptr; - const char *val; - val = NULL; - for (ptr = git_vars; ptr->read; ptr++) { - if (strcmp(var, ptr->name) == 0) { - val = ptr->read(IDENT_STRICT); - break; - } - } - return val; -} - -static int show_config(const char *var, const char *value, void *cb) -{ - if (value) - printf("%s=%s\n", var, value); - else - printf("%s\n", var); - return git_default_config(var, value, cb); -} - -int cmd_var(int argc, const char **argv, const char *prefix) -{ - const char *val = NULL; - if (argc != 2) - usage(var_usage); - - if (strcmp(argv[1], "-l") == 0) { - git_config(show_config, NULL); - list_vars(); - return 0; - } - git_config(git_default_config, NULL); - val = read_var(argv[1]); - if (!val) - usage(var_usage); - - printf("%s\n", val); - - return 0; -} diff --git a/third_party/git/builtin/verify-commit.c b/third_party/git/builtin/verify-commit.c deleted file mode 100644 index 40c69a0bedde..000000000000 --- a/third_party/git/builtin/verify-commit.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Builtin "git commit-commit" - * - * Copyright (c) 2014 Michael J Gruber <git@drmicha.warpmail.net> - * - * Based on git-verify-tag - */ -#include "cache.h" -#include "config.h" -#include "builtin.h" -#include "object-store.h" -#include "repository.h" -#include "commit.h" -#include "run-command.h" -#include "parse-options.h" -#include "gpg-interface.h" - -static const char * const verify_commit_usage[] = { - N_("git verify-commit [-v | --verbose] <commit>..."), - NULL -}; - -static int run_gpg_verify(struct commit *commit, unsigned flags) -{ - struct signature_check signature_check; - int ret; - - memset(&signature_check, 0, sizeof(signature_check)); - - ret = check_commit_signature(commit, &signature_check); - print_signature_buffer(&signature_check, flags); - - signature_check_clear(&signature_check); - return ret; -} - -static int verify_commit(const char *name, unsigned flags) -{ - struct object_id oid; - struct object *obj; - - if (get_oid(name, &oid)) - return error("commit '%s' not found.", name); - - obj = parse_object(the_repository, &oid); - if (!obj) - return error("%s: unable to read file.", name); - if (obj->type != OBJ_COMMIT) - return error("%s: cannot verify a non-commit object of type %s.", - name, type_name(obj->type)); - - return run_gpg_verify((struct commit *)obj, flags); -} - -static int git_verify_commit_config(const char *var, const char *value, void *cb) -{ - int status = git_gpg_config(var, value, cb); - if (status) - return status; - return git_default_config(var, value, cb); -} - -int cmd_verify_commit(int argc, const char **argv, const char *prefix) -{ - int i = 1, verbose = 0, had_error = 0; - unsigned flags = 0; - const struct option verify_commit_options[] = { - OPT__VERBOSE(&verbose, N_("print commit contents")), - OPT_BIT(0, "raw", &flags, N_("print raw gpg status output"), GPG_VERIFY_RAW), - OPT_END() - }; - - git_config(git_verify_commit_config, NULL); - - argc = parse_options(argc, argv, prefix, verify_commit_options, - verify_commit_usage, PARSE_OPT_KEEP_ARGV0); - if (argc <= i) - usage_with_options(verify_commit_usage, verify_commit_options); - - if (verbose) - flags |= GPG_VERIFY_VERBOSE; - - /* sometimes the program was terminated because this signal - * was received in the process of writing the gpg input: */ - signal(SIGPIPE, SIG_IGN); - while (i < argc) - if (verify_commit(argv[i++], flags)) - had_error = 1; - return had_error; -} diff --git a/third_party/git/builtin/verify-pack.c b/third_party/git/builtin/verify-pack.c deleted file mode 100644 index 05c52135946b..000000000000 --- a/third_party/git/builtin/verify-pack.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "run-command.h" -#include "parse-options.h" - -#define VERIFY_PACK_VERBOSE 01 -#define VERIFY_PACK_STAT_ONLY 02 - -static int verify_one_pack(const char *path, unsigned int flags, const char *hash_algo) -{ - struct child_process index_pack = CHILD_PROCESS_INIT; - struct strvec *argv = &index_pack.args; - struct strbuf arg = STRBUF_INIT; - int verbose = flags & VERIFY_PACK_VERBOSE; - int stat_only = flags & VERIFY_PACK_STAT_ONLY; - int err; - - strvec_push(argv, "index-pack"); - - if (stat_only) - strvec_push(argv, "--verify-stat-only"); - else if (verbose) - strvec_push(argv, "--verify-stat"); - else - strvec_push(argv, "--verify"); - - if (hash_algo) - strvec_pushf(argv, "--object-format=%s", hash_algo); - - /* - * In addition to "foo.pack" we accept "foo.idx" and "foo"; - * normalize these forms to "foo.pack" for "index-pack --verify". - */ - strbuf_addstr(&arg, path); - if (strbuf_strip_suffix(&arg, ".idx") || - !ends_with(arg.buf, ".pack")) - strbuf_addstr(&arg, ".pack"); - strvec_push(argv, arg.buf); - - index_pack.git_cmd = 1; - - err = run_command(&index_pack); - - if (verbose || stat_only) { - if (err) - printf("%s: bad\n", arg.buf); - else { - if (!stat_only) - printf("%s: ok\n", arg.buf); - } - } - strbuf_release(&arg); - - return err; -} - -static const char * const verify_pack_usage[] = { - N_("git verify-pack [-v | --verbose] [-s | --stat-only] <pack>..."), - NULL -}; - -int cmd_verify_pack(int argc, const char **argv, const char *prefix) -{ - int err = 0; - unsigned int flags = 0; - const char *object_format = NULL; - int i; - const struct option verify_pack_options[] = { - OPT_BIT('v', "verbose", &flags, N_("verbose"), - VERIFY_PACK_VERBOSE), - OPT_BIT('s', "stat-only", &flags, N_("show statistics only"), - VERIFY_PACK_STAT_ONLY), - OPT_STRING(0, "object-format", &object_format, N_("hash"), - N_("specify the hash algorithm to use")), - OPT_END() - }; - - git_config(git_default_config, NULL); - argc = parse_options(argc, argv, prefix, verify_pack_options, - verify_pack_usage, 0); - if (argc < 1) - usage_with_options(verify_pack_usage, verify_pack_options); - for (i = 0; i < argc; i++) { - if (verify_one_pack(argv[i], flags, object_format)) - err = 1; - } - - return err; -} diff --git a/third_party/git/builtin/verify-tag.c b/third_party/git/builtin/verify-tag.c deleted file mode 100644 index f45136a06ba7..000000000000 --- a/third_party/git/builtin/verify-tag.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Builtin "git verify-tag" - * - * Copyright (c) 2007 Carlos Rica <jasampler@gmail.com> - * - * Based on git-verify-tag.sh - */ -#include "cache.h" -#include "config.h" -#include "builtin.h" -#include "tag.h" -#include "run-command.h" -#include "parse-options.h" -#include "gpg-interface.h" -#include "ref-filter.h" - -static const char * const verify_tag_usage[] = { - N_("git verify-tag [-v | --verbose] [--format=<format>] <tag>..."), - NULL -}; - -static int git_verify_tag_config(const char *var, const char *value, void *cb) -{ - int status = git_gpg_config(var, value, cb); - if (status) - return status; - return git_default_config(var, value, cb); -} - -int cmd_verify_tag(int argc, const char **argv, const char *prefix) -{ - int i = 1, verbose = 0, had_error = 0; - unsigned flags = 0; - struct ref_format format = REF_FORMAT_INIT; - const struct option verify_tag_options[] = { - OPT__VERBOSE(&verbose, N_("print tag contents")), - OPT_BIT(0, "raw", &flags, N_("print raw gpg status output"), GPG_VERIFY_RAW), - OPT_STRING(0, "format", &format.format, N_("format"), N_("format to use for the output")), - OPT_END() - }; - - git_config(git_verify_tag_config, NULL); - - argc = parse_options(argc, argv, prefix, verify_tag_options, - verify_tag_usage, PARSE_OPT_KEEP_ARGV0); - if (argc <= i) - usage_with_options(verify_tag_usage, verify_tag_options); - - if (verbose) - flags |= GPG_VERIFY_VERBOSE; - - if (format.format) { - if (verify_ref_format(&format)) - usage_with_options(verify_tag_usage, - verify_tag_options); - flags |= GPG_VERIFY_OMIT_STATUS; - } - - while (i < argc) { - struct object_id oid; - const char *name = argv[i++]; - - if (get_oid(name, &oid)) { - had_error = !!error("tag '%s' not found.", name); - continue; - } - - if (gpg_verify_tag(&oid, name, flags)) { - had_error = 1; - continue; - } - - if (format.format) - pretty_print_ref(name, &oid, &format); - } - return had_error; -} diff --git a/third_party/git/builtin/worktree.c b/third_party/git/builtin/worktree.c deleted file mode 100644 index 99abaeec6c6e..000000000000 --- a/third_party/git/builtin/worktree.c +++ /dev/null @@ -1,1088 +0,0 @@ -#include "cache.h" -#include "checkout.h" -#include "config.h" -#include "builtin.h" -#include "dir.h" -#include "parse-options.h" -#include "strvec.h" -#include "branch.h" -#include "refs.h" -#include "run-command.h" -#include "sigchain.h" -#include "submodule.h" -#include "utf8.h" -#include "worktree.h" - -static const char * const worktree_usage[] = { - N_("git worktree add [<options>] <path> [<commit-ish>]"), - N_("git worktree list [<options>]"), - N_("git worktree lock [<options>] <path>"), - N_("git worktree move <worktree> <new-path>"), - N_("git worktree prune [<options>]"), - N_("git worktree remove [<options>] <worktree>"), - N_("git worktree unlock <path>"), - NULL -}; - -struct add_opts { - int force; - int detach; - int quiet; - int checkout; - int keep_locked; -}; - -static int show_only; -static int verbose; -static int guess_remote; -static timestamp_t expire; - -static int git_worktree_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "worktree.guessremote")) { - guess_remote = git_config_bool(var, value); - return 0; - } - - return git_default_config(var, value, cb); -} - -static int delete_git_dir(const char *id) -{ - struct strbuf sb = STRBUF_INIT; - int ret; - - strbuf_addstr(&sb, git_common_path("worktrees/%s", id)); - ret = remove_dir_recursively(&sb, 0); - if (ret < 0 && errno == ENOTDIR) - ret = unlink(sb.buf); - if (ret) - error_errno(_("failed to delete '%s'"), sb.buf); - strbuf_release(&sb); - return ret; -} - -static void delete_worktrees_dir_if_empty(void) -{ - rmdir(git_path("worktrees")); /* ignore failed removal */ -} - -/* - * Return true if worktree entry should be pruned, along with the reason for - * pruning. Otherwise, return false and the worktree's path, or NULL if it - * cannot be determined. Caller is responsible for freeing returned path. - */ -static int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath) -{ - struct stat st; - char *path; - int fd; - size_t len; - ssize_t read_result; - - *wtpath = NULL; - if (!is_directory(git_path("worktrees/%s", id))) { - strbuf_addstr(reason, _("not a valid directory")); - return 1; - } - if (file_exists(git_path("worktrees/%s/locked", id))) - return 0; - if (stat(git_path("worktrees/%s/gitdir", id), &st)) { - strbuf_addstr(reason, _("gitdir file does not exist")); - return 1; - } - fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY); - if (fd < 0) { - strbuf_addf(reason, _("unable to read gitdir file (%s)"), - strerror(errno)); - return 1; - } - len = xsize_t(st.st_size); - path = xmallocz(len); - - read_result = read_in_full(fd, path, len); - if (read_result < 0) { - strbuf_addf(reason, _("unable to read gitdir file (%s)"), - strerror(errno)); - close(fd); - free(path); - return 1; - } - close(fd); - - if (read_result != len) { - strbuf_addf(reason, - _("short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"), - (uintmax_t)len, (uintmax_t)read_result); - free(path); - return 1; - } - while (len && (path[len - 1] == '\n' || path[len - 1] == '\r')) - len--; - if (!len) { - strbuf_addstr(reason, _("invalid gitdir file")); - free(path); - return 1; - } - path[len] = '\0'; - if (!file_exists(path)) { - if (stat(git_path("worktrees/%s/index", id), &st) || - st.st_mtime <= expire) { - strbuf_addstr(reason, _("gitdir file points to non-existent location")); - free(path); - return 1; - } else { - *wtpath = path; - return 0; - } - } - *wtpath = path; - return 0; -} - -static void prune_worktree(const char *id, const char *reason) -{ - if (show_only || verbose) - printf_ln(_("Removing %s/%s: %s"), "worktrees", id, reason); - if (!show_only) - delete_git_dir(id); -} - -static int prune_cmp(const void *a, const void *b) -{ - const struct string_list_item *x = a; - const struct string_list_item *y = b; - int c; - - if ((c = fspathcmp(x->string, y->string))) - return c; - /* - * paths same; prune_dupes() removes all but the first worktree entry - * having the same path, so sort main worktree ('util' is NULL) above - * linked worktrees ('util' not NULL) since main worktree can't be - * removed - */ - if (!x->util) - return -1; - if (!y->util) - return 1; - /* paths same; sort by .git/worktrees/<id> */ - return strcmp(x->util, y->util); -} - -static void prune_dups(struct string_list *l) -{ - int i; - - QSORT(l->items, l->nr, prune_cmp); - for (i = 1; i < l->nr; i++) { - if (!fspathcmp(l->items[i].string, l->items[i - 1].string)) - prune_worktree(l->items[i].util, "duplicate entry"); - } -} - -static void prune_worktrees(void) -{ - struct strbuf reason = STRBUF_INIT; - struct strbuf main_path = STRBUF_INIT; - struct string_list kept = STRING_LIST_INIT_NODUP; - DIR *dir = opendir(git_path("worktrees")); - struct dirent *d; - if (!dir) - return; - while ((d = readdir(dir)) != NULL) { - char *path; - if (is_dot_or_dotdot(d->d_name)) - continue; - strbuf_reset(&reason); - if (should_prune_worktree(d->d_name, &reason, &path)) - prune_worktree(d->d_name, reason.buf); - else if (path) - string_list_append(&kept, path)->util = xstrdup(d->d_name); - } - closedir(dir); - - strbuf_add_absolute_path(&main_path, get_git_common_dir()); - /* massage main worktree absolute path to match 'gitdir' content */ - strbuf_strip_suffix(&main_path, "/."); - string_list_append(&kept, strbuf_detach(&main_path, NULL)); - prune_dups(&kept); - string_list_clear(&kept, 1); - - if (!show_only) - delete_worktrees_dir_if_empty(); - strbuf_release(&reason); -} - -static int prune(int ac, const char **av, const char *prefix) -{ - struct option options[] = { - OPT__DRY_RUN(&show_only, N_("do not remove, show only")), - OPT__VERBOSE(&verbose, N_("report pruned working trees")), - OPT_EXPIRY_DATE(0, "expire", &expire, - N_("expire working trees older than <time>")), - OPT_END() - }; - - expire = TIME_MAX; - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); - if (ac) - usage_with_options(worktree_usage, options); - prune_worktrees(); - return 0; -} - -static char *junk_work_tree; -static char *junk_git_dir; -static int is_junk; -static pid_t junk_pid; - -static void remove_junk(void) -{ - struct strbuf sb = STRBUF_INIT; - if (!is_junk || getpid() != junk_pid) - return; - if (junk_git_dir) { - strbuf_addstr(&sb, junk_git_dir); - remove_dir_recursively(&sb, 0); - strbuf_reset(&sb); - } - if (junk_work_tree) { - strbuf_addstr(&sb, junk_work_tree); - remove_dir_recursively(&sb, 0); - } - strbuf_release(&sb); -} - -static void remove_junk_on_signal(int signo) -{ - remove_junk(); - sigchain_pop(signo); - raise(signo); -} - -static const char *worktree_basename(const char *path, int *olen) -{ - const char *name; - int len; - - len = strlen(path); - while (len && is_dir_sep(path[len - 1])) - len--; - - for (name = path + len - 1; name > path; name--) - if (is_dir_sep(*name)) { - name++; - break; - } - - *olen = len; - return name; -} - -/* check that path is viable location for worktree */ -static void check_candidate_path(const char *path, - int force, - struct worktree **worktrees, - const char *cmd) -{ - struct worktree *wt; - int locked; - - if (file_exists(path) && !is_empty_dir(path)) - die(_("'%s' already exists"), path); - - wt = find_worktree_by_path(worktrees, path); - if (!wt) - return; - - locked = !!worktree_lock_reason(wt); - if ((!locked && force) || (locked && force > 1)) { - if (delete_git_dir(wt->id)) - die(_("unusable worktree destination '%s'"), path); - return; - } - - if (locked) - die(_("'%s' is a missing but locked worktree;\nuse '%s -f -f' to override, or 'unlock' and 'prune' or 'remove' to clear"), cmd, path); - else - die(_("'%s' is a missing but already registered worktree;\nuse '%s -f' to override, or 'prune' or 'remove' to clear"), cmd, path); -} - -static int add_worktree(const char *path, const char *refname, - const struct add_opts *opts) -{ - struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT; - struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT; - const char *name; - struct child_process cp = CHILD_PROCESS_INIT; - struct strvec child_env = STRVEC_INIT; - unsigned int counter = 0; - int len, ret; - struct strbuf symref = STRBUF_INIT; - struct commit *commit = NULL; - int is_branch = 0; - struct strbuf sb_name = STRBUF_INIT; - struct worktree **worktrees; - - worktrees = get_worktrees(); - check_candidate_path(path, opts->force, worktrees, "add"); - free_worktrees(worktrees); - worktrees = NULL; - - /* is 'refname' a branch or commit? */ - if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) && - ref_exists(symref.buf)) { - is_branch = 1; - if (!opts->force) - die_if_checked_out(symref.buf, 0); - } - commit = lookup_commit_reference_by_name(refname); - if (!commit) - die(_("invalid reference: %s"), refname); - - name = worktree_basename(path, &len); - strbuf_add(&sb, name, path + len - name); - sanitize_refname_component(sb.buf, &sb_name); - if (!sb_name.len) - BUG("How come '%s' becomes empty after sanitization?", sb.buf); - strbuf_reset(&sb); - name = sb_name.buf; - git_path_buf(&sb_repo, "worktrees/%s", name); - len = sb_repo.len; - if (safe_create_leading_directories_const(sb_repo.buf)) - die_errno(_("could not create leading directories of '%s'"), - sb_repo.buf); - - while (mkdir(sb_repo.buf, 0777)) { - counter++; - if ((errno != EEXIST) || !counter /* overflow */) - die_errno(_("could not create directory of '%s'"), - sb_repo.buf); - strbuf_setlen(&sb_repo, len); - strbuf_addf(&sb_repo, "%d", counter); - } - name = strrchr(sb_repo.buf, '/') + 1; - - junk_pid = getpid(); - atexit(remove_junk); - sigchain_push_common(remove_junk_on_signal); - - junk_git_dir = xstrdup(sb_repo.buf); - is_junk = 1; - - /* - * lock the incomplete repo so prune won't delete it, unlock - * after the preparation is over. - */ - strbuf_addf(&sb, "%s/locked", sb_repo.buf); - if (!opts->keep_locked) - write_file(sb.buf, "initializing"); - else - write_file(sb.buf, "added with --lock"); - - strbuf_addf(&sb_git, "%s/.git", path); - if (safe_create_leading_directories_const(sb_git.buf)) - die_errno(_("could not create leading directories of '%s'"), - sb_git.buf); - junk_work_tree = xstrdup(path); - - strbuf_reset(&sb); - strbuf_addf(&sb, "%s/gitdir", sb_repo.buf); - strbuf_realpath(&realpath, sb_git.buf, 1); - write_file(sb.buf, "%s", realpath.buf); - strbuf_realpath(&realpath, get_git_common_dir(), 1); - write_file(sb_git.buf, "gitdir: %s/worktrees/%s", - realpath.buf, name); - /* - * This is to keep resolve_ref() happy. We need a valid HEAD - * or is_git_directory() will reject the directory. Any value which - * looks like an object ID will do since it will be immediately - * replaced by the symbolic-ref or update-ref invocation in the new - * worktree. - */ - strbuf_reset(&sb); - strbuf_addf(&sb, "%s/HEAD", sb_repo.buf); - write_file(sb.buf, "%s", oid_to_hex(&null_oid)); - strbuf_reset(&sb); - strbuf_addf(&sb, "%s/commondir", sb_repo.buf); - write_file(sb.buf, "../.."); - - strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf); - strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path); - cp.git_cmd = 1; - - if (!is_branch) - strvec_pushl(&cp.args, "update-ref", "HEAD", - oid_to_hex(&commit->object.oid), NULL); - else { - strvec_pushl(&cp.args, "symbolic-ref", "HEAD", - symref.buf, NULL); - if (opts->quiet) - strvec_push(&cp.args, "--quiet"); - } - - cp.env = child_env.v; - ret = run_command(&cp); - if (ret) - goto done; - - if (opts->checkout) { - cp.argv = NULL; - strvec_clear(&cp.args); - strvec_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL); - if (opts->quiet) - strvec_push(&cp.args, "--quiet"); - cp.env = child_env.v; - ret = run_command(&cp); - if (ret) - goto done; - } - - is_junk = 0; - FREE_AND_NULL(junk_work_tree); - FREE_AND_NULL(junk_git_dir); - -done: - if (ret || !opts->keep_locked) { - strbuf_reset(&sb); - strbuf_addf(&sb, "%s/locked", sb_repo.buf); - unlink_or_warn(sb.buf); - } - - /* - * Hook failure does not warrant worktree deletion, so run hook after - * is_junk is cleared, but do return appropriate code when hook fails. - */ - if (!ret && opts->checkout) { - const char *hook = find_hook("post-checkout"); - if (hook) { - const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL }; - cp.git_cmd = 0; - cp.no_stdin = 1; - cp.stdout_to_stderr = 1; - cp.dir = path; - cp.env = env; - cp.argv = NULL; - cp.trace2_hook_name = "post-checkout"; - strvec_pushl(&cp.args, absolute_path(hook), - oid_to_hex(&null_oid), - oid_to_hex(&commit->object.oid), - "1", NULL); - ret = run_command(&cp); - } - } - - strvec_clear(&child_env); - strbuf_release(&sb); - strbuf_release(&symref); - strbuf_release(&sb_repo); - strbuf_release(&sb_git); - strbuf_release(&sb_name); - strbuf_release(&realpath); - return ret; -} - -static void print_preparing_worktree_line(int detach, - const char *branch, - const char *new_branch, - int force_new_branch) -{ - if (force_new_branch) { - struct commit *commit = lookup_commit_reference_by_name(new_branch); - if (!commit) - printf_ln(_("Preparing worktree (new branch '%s')"), new_branch); - else - printf_ln(_("Preparing worktree (resetting branch '%s'; was at %s)"), - new_branch, - find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); - } else if (new_branch) { - printf_ln(_("Preparing worktree (new branch '%s')"), new_branch); - } else { - struct strbuf s = STRBUF_INIT; - if (!detach && !strbuf_check_branch_ref(&s, branch) && - ref_exists(s.buf)) - printf_ln(_("Preparing worktree (checking out '%s')"), - branch); - else { - struct commit *commit = lookup_commit_reference_by_name(branch); - if (!commit) - die(_("invalid reference: %s"), branch); - printf_ln(_("Preparing worktree (detached HEAD %s)"), - find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV)); - } - strbuf_release(&s); - } -} - -static const char *dwim_branch(const char *path, const char **new_branch) -{ - int n; - const char *s = worktree_basename(path, &n); - const char *branchname = xstrndup(s, n); - struct strbuf ref = STRBUF_INIT; - - UNLEAK(branchname); - if (!strbuf_check_branch_ref(&ref, branchname) && - ref_exists(ref.buf)) { - strbuf_release(&ref); - return branchname; - } - - *new_branch = branchname; - if (guess_remote) { - struct object_id oid; - const char *remote = - unique_tracking_name(*new_branch, &oid, NULL); - return remote; - } - return NULL; -} - -static int add(int ac, const char **av, const char *prefix) -{ - struct add_opts opts; - const char *new_branch_force = NULL; - char *path; - const char *branch; - const char *new_branch = NULL; - const char *opt_track = NULL; - struct option options[] = { - OPT__FORCE(&opts.force, - N_("checkout <branch> even if already checked out in other worktree"), - PARSE_OPT_NOCOMPLETE), - OPT_STRING('b', NULL, &new_branch, N_("branch"), - N_("create a new branch")), - OPT_STRING('B', NULL, &new_branch_force, N_("branch"), - N_("create or reset a branch")), - OPT_BOOL('d', "detach", &opts.detach, N_("detach HEAD at named commit")), - OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")), - OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")), - OPT__QUIET(&opts.quiet, N_("suppress progress reporting")), - OPT_PASSTHRU(0, "track", &opt_track, NULL, - N_("set up tracking mode (see git-branch(1))"), - PARSE_OPT_NOARG | PARSE_OPT_OPTARG), - OPT_BOOL(0, "guess-remote", &guess_remote, - N_("try to match the new branch name with a remote-tracking branch")), - OPT_END() - }; - - memset(&opts, 0, sizeof(opts)); - opts.checkout = 1; - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); - if (!!opts.detach + !!new_branch + !!new_branch_force > 1) - die(_("-b, -B, and --detach are mutually exclusive")); - if (ac < 1 || ac > 2) - usage_with_options(worktree_usage, options); - - path = prefix_filename(prefix, av[0]); - branch = ac < 2 ? "HEAD" : av[1]; - - if (!strcmp(branch, "-")) - branch = "@{-1}"; - - if (new_branch_force) { - struct strbuf symref = STRBUF_INIT; - - new_branch = new_branch_force; - - if (!opts.force && - !strbuf_check_branch_ref(&symref, new_branch) && - ref_exists(symref.buf)) - die_if_checked_out(symref.buf, 0); - strbuf_release(&symref); - } - - if (ac < 2 && !new_branch && !opts.detach) { - const char *s = dwim_branch(path, &new_branch); - if (s) - branch = s; - } - - if (ac == 2 && !new_branch && !opts.detach) { - struct object_id oid; - struct commit *commit; - const char *remote; - - commit = lookup_commit_reference_by_name(branch); - if (!commit) { - remote = unique_tracking_name(branch, &oid, NULL); - if (remote) { - new_branch = branch; - branch = remote; - } - } - } - if (!opts.quiet) - print_preparing_worktree_line(opts.detach, branch, new_branch, !!new_branch_force); - - if (new_branch) { - struct child_process cp = CHILD_PROCESS_INIT; - cp.git_cmd = 1; - strvec_push(&cp.args, "branch"); - if (new_branch_force) - strvec_push(&cp.args, "--force"); - if (opts.quiet) - strvec_push(&cp.args, "--quiet"); - strvec_push(&cp.args, new_branch); - strvec_push(&cp.args, branch); - if (opt_track) - strvec_push(&cp.args, opt_track); - if (run_command(&cp)) - return -1; - branch = new_branch; - } else if (opt_track) { - die(_("--[no-]track can only be used if a new branch is created")); - } - - UNLEAK(path); - UNLEAK(opts); - return add_worktree(path, branch, &opts); -} - -static void show_worktree_porcelain(struct worktree *wt) -{ - printf("worktree %s\n", wt->path); - if (wt->is_bare) - printf("bare\n"); - else { - printf("HEAD %s\n", oid_to_hex(&wt->head_oid)); - if (wt->is_detached) - printf("detached\n"); - else if (wt->head_ref) - printf("branch %s\n", wt->head_ref); - } - printf("\n"); -} - -static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len) -{ - struct strbuf sb = STRBUF_INIT; - int cur_path_len = strlen(wt->path); - int path_adj = cur_path_len - utf8_strwidth(wt->path); - - strbuf_addf(&sb, "%-*s ", 1 + path_maxlen + path_adj, wt->path); - if (wt->is_bare) - strbuf_addstr(&sb, "(bare)"); - else { - strbuf_addf(&sb, "%-*s ", abbrev_len, - find_unique_abbrev(&wt->head_oid, DEFAULT_ABBREV)); - if (wt->is_detached) - strbuf_addstr(&sb, "(detached HEAD)"); - else if (wt->head_ref) { - char *ref = shorten_unambiguous_ref(wt->head_ref, 0); - strbuf_addf(&sb, "[%s]", ref); - free(ref); - } else - strbuf_addstr(&sb, "(error)"); - } - printf("%s\n", sb.buf); - - strbuf_release(&sb); -} - -static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen) -{ - int i; - - for (i = 0; wt[i]; i++) { - int sha1_len; - int path_len = strlen(wt[i]->path); - - if (path_len > *maxlen) - *maxlen = path_len; - sha1_len = strlen(find_unique_abbrev(&wt[i]->head_oid, *abbrev)); - if (sha1_len > *abbrev) - *abbrev = sha1_len; - } -} - -static int pathcmp(const void *a_, const void *b_) -{ - const struct worktree *const *a = a_; - const struct worktree *const *b = b_; - return fspathcmp((*a)->path, (*b)->path); -} - -static void pathsort(struct worktree **wt) -{ - int n = 0; - struct worktree **p = wt; - - while (*p++) - n++; - QSORT(wt, n, pathcmp); -} - -static int list(int ac, const char **av, const char *prefix) -{ - int porcelain = 0; - - struct option options[] = { - OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")), - OPT_END() - }; - - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); - if (ac) - usage_with_options(worktree_usage, options); - else { - struct worktree **worktrees = get_worktrees(); - int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i; - - /* sort worktrees by path but keep main worktree at top */ - pathsort(worktrees + 1); - - if (!porcelain) - measure_widths(worktrees, &abbrev, &path_maxlen); - - for (i = 0; worktrees[i]; i++) { - if (porcelain) - show_worktree_porcelain(worktrees[i]); - else - show_worktree(worktrees[i], path_maxlen, abbrev); - } - free_worktrees(worktrees); - } - return 0; -} - -static int lock_worktree(int ac, const char **av, const char *prefix) -{ - const char *reason = "", *old_reason; - struct option options[] = { - OPT_STRING(0, "reason", &reason, N_("string"), - N_("reason for locking")), - OPT_END() - }; - struct worktree **worktrees, *wt; - - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); - if (ac != 1) - usage_with_options(worktree_usage, options); - - worktrees = get_worktrees(); - wt = find_worktree(worktrees, prefix, av[0]); - if (!wt) - die(_("'%s' is not a working tree"), av[0]); - if (is_main_worktree(wt)) - die(_("The main working tree cannot be locked or unlocked")); - - old_reason = worktree_lock_reason(wt); - if (old_reason) { - if (*old_reason) - die(_("'%s' is already locked, reason: %s"), - av[0], old_reason); - die(_("'%s' is already locked"), av[0]); - } - - write_file(git_common_path("worktrees/%s/locked", wt->id), - "%s", reason); - free_worktrees(worktrees); - return 0; -} - -static int unlock_worktree(int ac, const char **av, const char *prefix) -{ - struct option options[] = { - OPT_END() - }; - struct worktree **worktrees, *wt; - int ret; - - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); - if (ac != 1) - usage_with_options(worktree_usage, options); - - worktrees = get_worktrees(); - wt = find_worktree(worktrees, prefix, av[0]); - if (!wt) - die(_("'%s' is not a working tree"), av[0]); - if (is_main_worktree(wt)) - die(_("The main working tree cannot be locked or unlocked")); - if (!worktree_lock_reason(wt)) - die(_("'%s' is not locked"), av[0]); - ret = unlink_or_warn(git_common_path("worktrees/%s/locked", wt->id)); - free_worktrees(worktrees); - return ret; -} - -static void validate_no_submodules(const struct worktree *wt) -{ - struct index_state istate = { NULL }; - struct strbuf path = STRBUF_INIT; - int i, found_submodules = 0; - - if (is_directory(worktree_git_path(wt, "modules"))) { - /* - * There could be false positives, e.g. the "modules" - * directory exists but is empty. But it's a rare case and - * this simpler check is probably good enough for now. - */ - found_submodules = 1; - } else if (read_index_from(&istate, worktree_git_path(wt, "index"), - get_worktree_git_dir(wt)) > 0) { - for (i = 0; i < istate.cache_nr; i++) { - struct cache_entry *ce = istate.cache[i]; - int err; - - if (!S_ISGITLINK(ce->ce_mode)) - continue; - - strbuf_reset(&path); - strbuf_addf(&path, "%s/%s", wt->path, ce->name); - if (!is_submodule_populated_gently(path.buf, &err)) - continue; - - found_submodules = 1; - break; - } - } - discard_index(&istate); - strbuf_release(&path); - - if (found_submodules) - die(_("working trees containing submodules cannot be moved or removed")); -} - -static int move_worktree(int ac, const char **av, const char *prefix) -{ - int force = 0; - struct option options[] = { - OPT__FORCE(&force, - N_("force move even if worktree is dirty or locked"), - PARSE_OPT_NOCOMPLETE), - OPT_END() - }; - struct worktree **worktrees, *wt; - struct strbuf dst = STRBUF_INIT; - struct strbuf errmsg = STRBUF_INIT; - const char *reason = NULL; - char *path; - - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); - if (ac != 2) - usage_with_options(worktree_usage, options); - - path = prefix_filename(prefix, av[1]); - strbuf_addstr(&dst, path); - free(path); - - worktrees = get_worktrees(); - wt = find_worktree(worktrees, prefix, av[0]); - if (!wt) - die(_("'%s' is not a working tree"), av[0]); - if (is_main_worktree(wt)) - die(_("'%s' is a main working tree"), av[0]); - if (is_directory(dst.buf)) { - const char *sep = find_last_dir_sep(wt->path); - - if (!sep) - die(_("could not figure out destination name from '%s'"), - wt->path); - strbuf_trim_trailing_dir_sep(&dst); - strbuf_addstr(&dst, sep); - } - check_candidate_path(dst.buf, force, worktrees, "move"); - - validate_no_submodules(wt); - - if (force < 2) - reason = worktree_lock_reason(wt); - if (reason) { - if (*reason) - die(_("cannot move a locked working tree, lock reason: %s\nuse 'move -f -f' to override or unlock first"), - reason); - die(_("cannot move a locked working tree;\nuse 'move -f -f' to override or unlock first")); - } - if (validate_worktree(wt, &errmsg, 0)) - die(_("validation failed, cannot move working tree: %s"), - errmsg.buf); - strbuf_release(&errmsg); - - if (rename(wt->path, dst.buf) == -1) - die_errno(_("failed to move '%s' to '%s'"), wt->path, dst.buf); - - update_worktree_location(wt, dst.buf); - - strbuf_release(&dst); - free_worktrees(worktrees); - return 0; -} - -/* - * Note, "git status --porcelain" is used to determine if it's safe to - * delete a whole worktree. "git status" does not ignore user - * configuration, so if a normal "git status" shows "clean" for the - * user, then it's ok to remove it. - * - * This assumption may be a bad one. We may want to ignore - * (potentially bad) user settings and only delete a worktree when - * it's absolutely safe to do so from _our_ point of view because we - * know better. - */ -static void check_clean_worktree(struct worktree *wt, - const char *original_path) -{ - struct child_process cp; - char buf[1]; - int ret; - - /* - * Until we sort this out, all submodules are "dirty" and - * will abort this function. - */ - validate_no_submodules(wt); - - child_process_init(&cp); - strvec_pushf(&cp.env_array, "%s=%s/.git", - GIT_DIR_ENVIRONMENT, wt->path); - strvec_pushf(&cp.env_array, "%s=%s", - GIT_WORK_TREE_ENVIRONMENT, wt->path); - strvec_pushl(&cp.args, "status", - "--porcelain", "--ignore-submodules=none", - NULL); - cp.git_cmd = 1; - cp.dir = wt->path; - cp.out = -1; - ret = start_command(&cp); - if (ret) - die_errno(_("failed to run 'git status' on '%s'"), - original_path); - ret = xread(cp.out, buf, sizeof(buf)); - if (ret) - die(_("'%s' contains modified or untracked files, use --force to delete it"), - original_path); - close(cp.out); - ret = finish_command(&cp); - if (ret) - die_errno(_("failed to run 'git status' on '%s', code %d"), - original_path, ret); -} - -static int delete_git_work_tree(struct worktree *wt) -{ - struct strbuf sb = STRBUF_INIT; - int ret = 0; - - strbuf_addstr(&sb, wt->path); - if (remove_dir_recursively(&sb, 0)) { - error_errno(_("failed to delete '%s'"), sb.buf); - ret = -1; - } - strbuf_release(&sb); - return ret; -} - -static int remove_worktree(int ac, const char **av, const char *prefix) -{ - int force = 0; - struct option options[] = { - OPT__FORCE(&force, - N_("force removal even if worktree is dirty or locked"), - PARSE_OPT_NOCOMPLETE), - OPT_END() - }; - struct worktree **worktrees, *wt; - struct strbuf errmsg = STRBUF_INIT; - const char *reason = NULL; - int ret = 0; - - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); - if (ac != 1) - usage_with_options(worktree_usage, options); - - worktrees = get_worktrees(); - wt = find_worktree(worktrees, prefix, av[0]); - if (!wt) - die(_("'%s' is not a working tree"), av[0]); - if (is_main_worktree(wt)) - die(_("'%s' is a main working tree"), av[0]); - if (force < 2) - reason = worktree_lock_reason(wt); - if (reason) { - if (*reason) - die(_("cannot remove a locked working tree, lock reason: %s\nuse 'remove -f -f' to override or unlock first"), - reason); - die(_("cannot remove a locked working tree;\nuse 'remove -f -f' to override or unlock first")); - } - if (validate_worktree(wt, &errmsg, WT_VALIDATE_WORKTREE_MISSING_OK)) - die(_("validation failed, cannot remove working tree: %s"), - errmsg.buf); - strbuf_release(&errmsg); - - if (file_exists(wt->path)) { - if (!force) - check_clean_worktree(wt, av[0]); - - ret |= delete_git_work_tree(wt); - } - /* - * continue on even if ret is non-zero, there's no going back - * from here. - */ - ret |= delete_git_dir(wt->id); - delete_worktrees_dir_if_empty(); - - free_worktrees(worktrees); - return ret; -} - -static void report_repair(int iserr, const char *path, const char *msg, void *cb_data) -{ - if (!iserr) { - printf_ln(_("repair: %s: %s"), msg, path); - } else { - int *exit_status = (int *)cb_data; - fprintf_ln(stderr, _("error: %s: %s"), msg, path); - *exit_status = 1; - } -} - -static int repair(int ac, const char **av, const char *prefix) -{ - const char **p; - const char *self[] = { ".", NULL }; - struct option options[] = { - OPT_END() - }; - int rc = 0; - - ac = parse_options(ac, av, prefix, options, worktree_usage, 0); - repair_worktrees(report_repair, &rc); - p = ac > 0 ? av : self; - for (; *p; p++) - repair_worktree_at_path(*p, report_repair, &rc); - return rc; -} - -int cmd_worktree(int ac, const char **av, const char *prefix) -{ - struct option options[] = { - OPT_END() - }; - - git_config(git_worktree_config, NULL); - - if (ac < 2) - usage_with_options(worktree_usage, options); - if (!prefix) - prefix = ""; - if (!strcmp(av[1], "add")) - return add(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "prune")) - return prune(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "list")) - return list(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "lock")) - return lock_worktree(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "unlock")) - return unlock_worktree(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "move")) - return move_worktree(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "remove")) - return remove_worktree(ac - 1, av + 1, prefix); - if (!strcmp(av[1], "repair")) - return repair(ac - 1, av + 1, prefix); - usage_with_options(worktree_usage, options); -} diff --git a/third_party/git/builtin/write-tree.c b/third_party/git/builtin/write-tree.c deleted file mode 100644 index 45d61707e7d1..000000000000 --- a/third_party/git/builtin/write-tree.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * GIT - The information manager from hell - * - * Copyright (C) Linus Torvalds, 2005 - */ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "builtin.h" -#include "cache.h" -#include "config.h" -#include "tree.h" -#include "cache-tree.h" -#include "parse-options.h" - -static const char * const write_tree_usage[] = { - N_("git write-tree [--missing-ok] [--prefix=<prefix>/]"), - NULL -}; - -int cmd_write_tree(int argc, const char **argv, const char *cmd_prefix) -{ - int flags = 0, ret; - const char *tree_prefix = NULL; - struct object_id oid; - const char *me = "git-write-tree"; - struct option write_tree_options[] = { - OPT_BIT(0, "missing-ok", &flags, N_("allow missing objects"), - WRITE_TREE_MISSING_OK), - OPT_STRING(0, "prefix", &tree_prefix, N_("<prefix>/"), - N_("write tree object for a subdirectory <prefix>")), - { OPTION_BIT, 0, "ignore-cache-tree", &flags, NULL, - N_("only useful for debugging"), - PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, NULL, - WRITE_TREE_IGNORE_CACHE_TREE }, - OPT_END() - }; - - git_config(git_default_config, NULL); - argc = parse_options(argc, argv, cmd_prefix, write_tree_options, - write_tree_usage, 0); - - ret = write_cache_as_tree(&oid, flags, tree_prefix); - switch (ret) { - case 0: - printf("%s\n", oid_to_hex(&oid)); - break; - case WRITE_TREE_UNREADABLE_INDEX: - die("%s: error reading the index", me); - break; - case WRITE_TREE_UNMERGED_INDEX: - die("%s: error building trees", me); - break; - case WRITE_TREE_PREFIX_ERROR: - die("%s: prefix %s not found", me, tree_prefix); - break; - } - return ret; -} |