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 path_exists(struct tree *tree, const char *path) { const char *paths[] = { path, NULL }; struct pathspec pathspec; int ret; parse_pathspec(&pathspec, 0, 0, "", paths); pathspec.recursive = 1; ret = read_tree_recursive(tree, "", 0, 0, &pathspec, reject_entry, &pathspec); free_pathspec(&pathspec); return ret != 0; }
static int do_reupdate(int ac, const char **av, const char *prefix, int prefix_length) { /* 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 && !oidcmp(&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; }
static int path_exists(struct archiver_args *args, const char *path) { const char *paths[] = { path, NULL }; struct path_exists_context ctx; int ret; ctx.args = args; parse_pathspec(&ctx.pathspec, 0, 0, "", paths); ctx.pathspec.recursive = 1; ret = read_tree_recursive(args->tree, "", 0, 0, &ctx.pathspec, reject_entry, &ctx); clear_pathspec(&ctx.pathspec); return ret != 0; }
int interactive_add(int argc, 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); }
int git_checkout_file(const char *ref, const char *path, const char *outputpath) { struct cache_entry *ce; int ret; GIT_HASH sha1; struct tree * root; struct checkout state; struct pathspec pathspec; const char *matchbuf[1]; ret = get_sha1(ref, sha1); if(ret) return ret; reprepare_packed_git(); root = parse_tree_indirect(sha1); if(!root) { free_all_pack(); return -1; } ce = xcalloc(1, cache_entry_size(strlen(path))); matchbuf[0] = NULL; parse_pathspec(&pathspec, PATHSPEC_ALL_MAGIC, PATHSPEC_PREFER_CWD, path, matchbuf); pathspec.items[0].nowildcard_len = pathspec.items[0].len; ret = read_tree_recursive(root, "", 0, 0, &pathspec, update_some, ce); free_pathspec(&pathspec); if(ret) { free_all_pack(); free(ce); return ret; } memset(&state, 0, sizeof(state)); state.force = 1; state.refresh_cache = 0; ret = write_entry(ce, outputpath, &state, 0); free_all_pack(); free(ce); return ret; }
static int phoenixfs_open(const char *path, struct fuse_file_info *fi) { int rev, fd; FILE *infile, *fsfile; char fspath[PATH_MAX]; struct file_record *fr; char sha1_digest[40]; rev = parse_pathspec(xpath, path); build_xpath(fspath, xpath, 0); /* Skip zinflate for entries not in fstree */ if (!(fr = find_fr(xpath, rev))) goto END; /* Build openpath by hand */ print_sha1(sha1_digest, fr->sha1); sprintf(openpath, "%s/.git/loose/%s", ROOTENV->fsback, sha1_digest); if (access(openpath, F_OK) < 0) { /* Try extracting from packfile */ sprintf(xpath, "%s/.git/loose", ROOTENV->fsback); if (unpack_entry(fr->sha1, xpath) < 0) return -ENOENT; else PHOENIXFS_DBG("open:: pack %s", sha1_digest); } else PHOENIXFS_DBG("open:: loose %s", sha1_digest); /* zinflate openpath onto fspath */ PHOENIXFS_DBG("open:: zinflate %s onto %s", sha1_digest, fspath); if (!(infile = fopen(openpath, "rb")) || !(fsfile = fopen(fspath, "wb+"))) return -errno; if (zinflate(infile, fsfile) != Z_OK) PHOENIXFS_DBG("open:: zinflate issue"); fclose(infile); fclose(fsfile); END: if ((fd = open(fspath, fi->flags)) < 0) return -errno; fi->fh = fd; return 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 | PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP, 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 (!S_ISGITLINK(ce->ce_mode) || !match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched, 1)) 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, prefix)) result = -1; free(ps_matched); return result; }
static void parse_pathspec_arg(const char **pathspec, struct archiver_args *ar_args) { /* * must be consistent with parse_pathspec in path_exists() * Also if pathspec patterns are dependent, we're in big * trouble as we test each one separately */ parse_pathspec(&ar_args->pathspec, 0, PATHSPEC_PREFER_FULL, "", pathspec); if (pathspec) { while (*pathspec) { if (**pathspec && !path_exists(ar_args->tree, *pathspec)) die(_("pathspec '%s' did not match any files"), *pathspec); pathspec++; } } }
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; 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_("recursivley search in each submodule")), OPT_STRING(0, "parent-basename", &parent_basename, N_("basename"), N_("prepend parent project's basename to output")), 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")), { 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_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('z', "null", &opt.null_following_name, N_("print NUL after filenames")), 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), { OPTION_CALLBACK, 'e', NULL, &opt, N_("pattern"), N_("match <pattern>"), PARSE_OPT_NONEG, pattern_callback }, { OPTION_CALLBACK, 0, "and", &opt, NULL, N_("combine patterns specified with -e"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback }, OPT_BOOL(0, "or", &dummy, ""), { OPTION_CALLBACK, 0, "not", &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback }, { OPTION_CALLBACK, '(', NULL, &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, open_callback }, { OPTION_CALLBACK, ')', 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")), { OPTION_SET_INT, 0, "debug", &opt.debug, NULL, N_("show parse tree for grep expression"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 }, OPT_GROUP(""), { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager, N_("pager"), N_("show matching files in the pager"), PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager }, OPT_BOOL(0, "ext-grep", &external_grep_allowed__ignored, N_("allow calling of grep(1) (ignored by this build)")), OPT_END() }; init_grep_defaults(); git_config(grep_cmd_config, NULL); grep_init(&opt, prefix); super_prefix = get_super_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(); } /* * 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.")); if (!opt.fixed && opt.ignore_case) opt.regflags |= REG_ICASE; compile_grep_patterns(&opt); /* Check revs and then paths */ for (i = 0; i < argc; i++) { const char *arg = argv[i]; unsigned char sha1[20]; struct object_context oc; /* Is it a rev? */ if (!get_sha1_with_context(arg, 0, sha1, &oc)) { struct object *object = parse_object_or_die(sha1, arg); if (!seen_dashdash) verify_non_filename(prefix, arg); add_object_array_with_path(object, arg, &list, oc.mode, oc.path); continue; } if (!strcmp(arg, "--")) { i++; seen_dashdash = 1; } break; } #ifndef NO_PTHREADS if (list.nr || cached || show_in_pager) num_threads = 0; else if (num_threads == 0) num_threads = GREP_NUM_THREADS_DEFAULT; else if (num_threads < 0) die(_("invalid number of threads specified (%d)"), num_threads); #else num_threads = 0; #endif #ifndef NO_PTHREADS if (num_threads) { 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; start_threads(&opt); } #endif /* The rest are paths */ if (!seen_dashdash) { int j; for (j = i; j < argc; j++) verify_filename(prefix, argv[j], j == i); } 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; if (recurse_submodules) { gitmodules_config(); compile_submodule_options(&opt, &pathspec, cached, untracked, opt_exclude, use_index, pattern_type_arg); } 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, buf.buf); strbuf_detach(&buf, NULL); } } if (recurse_submodules && (!use_index || untracked)) die(_("option not supported with --recurse-submodules.")); 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; if (list.nr) die(_("--no-index or --untracked cannot be used with revs.")); 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) hit |= wait_all(); if (hit && show_in_pager) run_pager(&opt, prefix); free_grep_patterns(&opt); return !hit; }
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, nul_term_line = 0, read_from_stdin = 0, unborn; char **stdin_paths = NULL; int stdin_nr = 0, stdin_alloc = 0; const char *rev; 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), { OPTION_CALLBACK, 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_BOOL('z', NULL, &nul_term_line, N_("EXPERIMENTAL: paths are separated with NUL character")), OPT_BOOL(0, "stdin", &read_from_stdin, N_("EXPERIMENTAL: read paths from <stdin>")), OPT_END() }; git_config(git_reset_config, NULL); argc = parse_options(argc, argv, prefix, options, git_reset_usage, PARSE_OPT_KEEP_DASHDASH); parse_args(&pathspec, argv, prefix, patch_mode, &rev); if (read_from_stdin) { strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; int flags = PATHSPEC_PREFER_FULL; struct strbuf buf = STRBUF_INIT; struct strbuf unquoted = STRBUF_INIT; if (patch_mode) die(_("--stdin is incompatible with --patch")); if (pathspec.nr) die(_("--stdin is incompatible with path arguments")); 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); } ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc); stdin_paths[stdin_nr++] = xstrdup(buf.buf); strbuf_reset(&buf); } strbuf_release(&unquoted); strbuf_release(&buf); ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc); stdin_paths[stdin_nr++] = NULL; flags |= PATHSPEC_LITERAL_PATH; parse_pathspec(&pathspec, 0, flags, prefix, (const char **)stdin_paths); } else if (nul_term_line) die(_("-z requires --stdin")); unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ hashcpy(oid.hash, EMPTY_TREE_SHA1_BIN); } else if (!pathspec.nr) { struct commit *commit; if (get_oid_committish(rev, &oid)) die(_("Failed to resolve '%s' as a valid revision."), rev); commit = lookup_commit_reference(&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}")); 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 (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; if (get_git_work_tree()) refresh_index(&the_index, flags, NULL, NULL, _("Unstaged changes after reset:")); } else { int err = reset_index(&oid, reset_type, quiet); if (reset_type == KEEP && !err) err = reset_index(&oid, MIXED, quiet); if (err) die(_("Could not reset index file to revision '%s'."), rev); } 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(&oid)); } if (!pathspec.nr) remove_branch_state(); if (stdin_paths) { while (stdin_nr) free(stdin_paths[--stdin_nr]); free(stdin_paths); } return update_ref_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) exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive)); if (edit_interactive) return(edit_patch(argc, argv, prefix)); argc--; argv++; if (0 <= addremove_explicit) addremove = addremove_explicit; else if (take_worktree_changes && ADDREMOVE_DEFAULT) addremove = 0; /* "-u" was given but not "-A" */ if (addremove && take_worktree_changes) die(_("-A and -u are mutually incompatible")); if (!take_worktree_changes && addremove_explicit < 0 && argc) /* Turn "git add pathspec..." to "git add -A pathspec..." */ addremove = 1; if (!show_only && ignore_missing) die(_("Option --ignore-missing can only be used together with --dry-run")); if (chmod_arg && ((chmod_arg[0] != '-' && chmod_arg[0] != '+') || chmod_arg[1] != 'x' || chmod_arg[2])) die(_("--chmod param '%s' must be either -x or +x"), chmod_arg); add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize; require_pathspec = !(take_worktree_changes || (0 < addremove_explicit)); hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | (show_only ? ADD_CACHE_PRETEND : 0) | (intent_to_add ? ADD_CACHE_INTENT : 0) | (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | (!(addremove || take_worktree_changes) ? ADD_CACHE_IGNORE_REMOVAL : 0)); if (require_pathspec && argc == 0) { fprintf(stderr, _("Nothing specified, nothing added.\n")); fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n")); return 0; } /* * Check the "pathspec '%s' did not match any files" block * below before enabling new magic. */ parse_pathspec(&pathspec, PATHSPEC_ATTR, PATHSPEC_PREFER_FULL | PATHSPEC_SYMLINK_LEADING_PATH, prefix, argv); if (read_cache_preload(&pathspec) < 0) die(_("index file corrupt")); die_in_unpopulated_submodule(&the_index, prefix); die_path_inside_submodule(&the_index, &pathspec); if (add_new_files) { int baselen; /* Set up the default git porcelain excludes */ memset(&dir, 0, sizeof(dir)); if (!ignored_too) { dir.flags |= DIR_COLLECT_IGNORED; setup_standard_excludes(&dir); } /* This picks up the paths that are not tracked */ baselen = fill_directory(&dir, &the_index, &pathspec); if (pathspec.nr) seen = prune_directory(&dir, &pathspec, baselen); } if (refresh_only) { refresh(verbose, &pathspec); goto finish; } if (pathspec.nr) { int i; if (!seen) seen = find_pathspecs_matching_against_index(&pathspec, &the_index); /* * file_exists() assumes exact match */ GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL | PATHSPEC_GLOB | PATHSPEC_ICASE | PATHSPEC_EXCLUDE); for (i = 0; i < pathspec.nr; i++) { const char *path = pathspec.items[i].match; if (pathspec.items[i].magic & PATHSPEC_EXCLUDE) continue; if (!seen[i] && path[0] && ((pathspec.items[i].magic & (PATHSPEC_GLOB | PATHSPEC_ICASE)) || !file_exists(path))) { if (ignore_missing) { int dtype = DT_UNKNOWN; if (is_excluded(&dir, &the_index, path, &dtype)) dir_add_ignored(&dir, &the_index, path, pathspec.items[i].len); } else die(_("pathspec '%s' did not match any files"), pathspec.items[i].original); } } free(seen); } plug_bulk_checkin(); if (add_renormalize) exit_status |= renormalize_tracked_files(&pathspec, flags); else exit_status |= add_files_to_cache(prefix, &pathspec, flags); if (add_new_files) exit_status |= add_files(&dir, flags); if (chmod_arg && pathspec.nr) chmod_pathspec(&pathspec, chmod_arg[0]); unplug_bulk_checkin(); finish: if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK | SKIP_IF_UNCHANGED)) die(_("Unable to write new index file")); UNLEAK(pathspec); UNLEAK(dir); return exit_status; }
int cmd_ls_tree(int argc, const char **argv, const char *prefix) { unsigned char sha1[20]; 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_sha1(argv[0], sha1)) 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_GLOB | PATHSPEC_ICASE | PATHSPEC_EXCLUDE, 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(sha1); if (!tree) die("not a tree object"); return !!read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL); }
static int phoenixfs_release(const char *path, struct fuse_file_info *fi) { struct file_record *fr; FILE *infile, *outfile; struct stat st; unsigned char sha1[20]; char sha1_digest[40]; char outfilename[40]; char inpath[PATH_MAX]; char outpath[PATH_MAX]; int rev, ret; pthread_mutex_lock(&phoenixfs_mutexlock); /* Don't recursively backup history */ if ((rev = parse_pathspec(xpath, path))) { PHOENIXFS_DBG("release:: history: %s", path); /* Inflate the original version back onto the filesystem */ if (!(fr = find_fr(xpath, 0))) { PHOENIXFS_DBG("release:: Can't find revision 0!"); pthread_mutex_unlock(&phoenixfs_mutexlock); return 0; } print_sha1(sha1_digest, fr->sha1); sprintf(inpath, "%s/.git/loose/%s", ROOTENV->fsback, sha1_digest); build_xpath(outpath, xpath, 0); if (!(infile = fopen(inpath, "rb")) || !(outfile = fopen(outpath, "wb+"))) { pthread_mutex_unlock(&phoenixfs_mutexlock); return -errno; } PHOENIXFS_DBG("release:: history: zinflate %s onto %s", sha1_digest, outpath); rewind(infile); rewind(outfile); if (zinflate(infile, outfile) != Z_OK) PHOENIXFS_DBG("release:: zinflate issue"); fflush(outfile); fclose(infile); fclose(outfile); if (close(fi->fh) < 0) { PHOENIXFS_DBG("release:: can't really close"); pthread_mutex_unlock(&phoenixfs_mutexlock); return -errno; } pthread_mutex_unlock(&phoenixfs_mutexlock); return 0; } /* Attempt to create a backup */ build_xpath(xpath, path, 0); if (!(infile = fopen(xpath, "rb")) || (lstat(xpath, &st) < 0)) { pthread_mutex_unlock(&phoenixfs_mutexlock); return -errno; } if ((ret = sha1_file(infile, st.st_size, sha1)) < 0) { fclose(infile); pthread_mutex_unlock(&phoenixfs_mutexlock); return ret; } print_sha1(outfilename, sha1); sprintf(outpath, "%s/.git/loose/%s", ROOTENV->fsback, outfilename); if (!access(outpath, F_OK)) { /* SHA1 match; don't overwrite file as an optimization */ PHOENIXFS_DBG("release:: not overwriting: %s", outpath); goto END; } if (!(outfile = fopen(outpath, "wb"))) { fclose(infile); pthread_mutex_unlock(&phoenixfs_mutexlock); return -errno; } /* Rewind and seek back */ rewind(infile); PHOENIXFS_DBG("release:: zdeflate %s onto %s", xpath, outfilename); if (zdeflate(infile, outfile, -1) != Z_OK) PHOENIXFS_DBG("release:: zdeflate issue"); mark_for_packing(sha1, st.st_size); fclose(outfile); END: fclose(infile); if (close(fi->fh) < 0) { PHOENIXFS_DBG("release:: can't really close"); return -errno; } /* Update the fstree */ fstree_insert_update_file(path, NULL); pthread_mutex_unlock(&phoenixfs_mutexlock); return 0; }
/* * We have an origin -- check if the same path exists in the * parent and return an origin structure to represent it. */ static struct blame_origin *find_origin(struct commit *parent, struct blame_origin *origin) { struct blame_origin *porigin; struct diff_options diff_opts; const char *paths[2]; /* First check any existing origins */ for (porigin = parent->util; porigin; porigin = porigin->next) if (!strcmp(porigin->path, origin->path)) { /* * The same path between origin and its parent * without renaming -- the most common case. */ return blame_origin_incref (porigin); } /* See if the origin->path is different between parent * and origin first. Most of the time they are the * same and diff-tree is fairly efficient about this. */ diff_setup(&diff_opts); DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.detect_rename = 0; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; paths[0] = origin->path; paths[1] = NULL; parse_pathspec(&diff_opts.pathspec, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_LITERAL_PATH, "", paths); diff_setup_done(&diff_opts); if (is_null_oid(&origin->commit->object.oid)) do_diff_cache(&parent->tree->object.oid, &diff_opts); else diff_tree_oid(&parent->tree->object.oid, &origin->commit->tree->object.oid, "", &diff_opts); diffcore_std(&diff_opts); if (!diff_queued_diff.nr) { /* The path is the same as parent */ porigin = get_origin(parent, origin->path); oidcpy(&porigin->blob_oid, &origin->blob_oid); porigin->mode = origin->mode; } else { /* * Since origin->path is a pathspec, if the parent * commit had it as a directory, we will see a whole * bunch of deletion of files in the directory that we * do not care about. */ int i; struct diff_filepair *p = NULL; for (i = 0; i < diff_queued_diff.nr; i++) { const char *name; p = diff_queued_diff.queue[i]; name = p->one->path ? p->one->path : p->two->path; if (!strcmp(name, origin->path)) break; } if (!p) die("internal error in blame::find_origin"); switch (p->status) { default: die("internal error in blame::find_origin (%c)", p->status); case 'M': porigin = get_origin(parent, origin->path); oidcpy(&porigin->blob_oid, &p->one->oid); porigin->mode = p->one->mode; break; case 'A': case 'T': /* Did not exist in parent, or type changed */ break; } } diff_flush(&diff_opts); clear_pathspec(&diff_opts.pathspec); return porigin; }
static void try_to_follow_renames(const struct object_id *old_oid, const struct object_id *new_oid, struct strbuf *base, struct diff_options *opt) { struct diff_options diff_opts; struct diff_queue_struct *q = &diff_queued_diff; struct diff_filepair *choice; int i; /* * follow-rename code is very specific, we need exactly one * path. Magic that matches more than one path is not * supported. */ GUARD_PATHSPEC(&opt->pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL); #if 0 /* * We should reject wildcards as well. Unfortunately we * haven't got a reliable way to detect that 'foo\*bar' in * fact has no wildcards. nowildcard_len is merely a hint for * optimization. Let it slip for now until wildmatch is taught * about dry-run mode and returns wildcard info. */ if (opt->pathspec.has_wildcard) die("BUG:%s:%d: wildcards are not supported", __FILE__, __LINE__); #endif /* Remove the file creation entry from the diff queue, and remember it */ choice = q->queue[0]; q->nr = 0; diff_setup(&diff_opts); diff_opts.flags.recursive = 1; diff_opts.flags.find_copies_harder = 1; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_opts.single_follow = opt->pathspec.items[0].match; diff_opts.break_opt = opt->break_opt; diff_opts.rename_score = opt->rename_score; diff_setup_done(&diff_opts); ll_diff_tree_oid(old_oid, new_oid, base, &diff_opts); diffcore_std(&diff_opts); clear_pathspec(&diff_opts.pathspec); /* Go through the new set of filepairing, and see if we find a more interesting one */ opt->found_follow = 0; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; /* * Found a source? Not only do we use that for the new * diff_queued_diff, we will also use that as the path in * the future! */ if ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, opt->pathspec.items[0].match)) { const char *path[2]; /* Switch the file-pairs around */ q->queue[i] = choice; choice = p; /* Update the path we use from now on.. */ path[0] = p->one->path; path[1] = NULL; clear_pathspec(&opt->pathspec); parse_pathspec(&opt->pathspec, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_LITERAL_PATH, "", path); /* * The caller expects us to return a set of vanilla * filepairs to let a later call to diffcore_std() * it makes to sort the renames out (among other * things), but we already have found renames * ourselves; signal diffcore_std() not to muck with * rename information. */ opt->found_follow = 1; break; } } /* * Then, discard all the non-relevant file pairs... */ for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; diff_free_filepair(p); } /* * .. and re-instate the one we want (which might be either the * original one, or the rename/copy we found) */ q->queue[0] = choice; q->nr = 1; }
int cmd_checkout(int argc, const char **argv, const char *prefix) { struct checkout_opts opts; struct branch_info new_branch_info; char *conflict_style = NULL; int dwim_new_local_branch, no_dwim_new_local_branch = 0; int dwim_remotes_matched = 0; struct option options[] = { OPT__QUIET(&opts.quiet, N_("suppress progress reporting")), 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, "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_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")), 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__FORCE(&opts.force, N_("force checkout (throw away local modifications)"), PARSE_OPT_NOCOMPLETE), OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")), OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore, N_("update ignored files (default)"), PARSE_OPT_NOCOMPLETE), OPT_STRING(0, "conflict", &conflict_style, N_("style"), N_("conflict style (merge or diff3)")), 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_BOOL(0, "no-guess", &no_dwim_new_local_branch, N_("do not second guess 'git checkout <no-such-branch>'")), OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees, N_("do not check if another worktree is holding the given ref")), { OPTION_CALLBACK, 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(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")), OPT_END(), }; memset(&opts, 0, sizeof(opts)); memset(&new_branch_info, 0, sizeof(new_branch_info)); opts.overwrite_ignore = 1; opts.prefix = prefix; opts.show_progress = -1; opts.overlay_mode = -1; git_config(git_checkout_config, &opts); opts.track = BRANCH_TRACK_UNSPECIFIED; argc = parse_options(argc, argv, prefix, options, checkout_usage, PARSE_OPT_KEEP_DASHDASH); dwim_new_local_branch = !no_dwim_new_local_branch; if (opts.show_progress < 0) { if (opts.quiet) opts.show_progress = 0; else opts.show_progress = isatty(2); } if (conflict_style) { opts.merge = 1; /* implied */ git_xmerge_config("merge.conflictstyle", conflict_style, NULL); } if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1) die(_("-b, -B and --orphan are mutually exclusive")); if (opts.overlay_mode == 1 && opts.patch_mode) die(_("-p and --overlay are mutually exclusive")); /* * 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/--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 -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 -b")); 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) { struct object_id rev; int dwim_ok = !opts.patch_mode && 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, &dwim_remotes_matched); argv += n; argc -= n; } 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) 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 (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.")); } 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) { int ret = checkout_paths(&opts, new_branch_info.name); if (ret && dwim_remotes_matched > 1 && advice_checkout_ambiguous_remote_branch_name) advise(_("'%s' matched more than one remote tracking branch.\n" "We found %d remotes with a reference that matched. So we fell back\n" "on trying to resolve the argument as a path, but failed there too!\n" "\n" "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."), argv[0], dwim_remotes_matched); return ret; } else { return checkout_branch(&opts, &new_branch_info); } }
int cmd_rerere(int argc, const char **argv, const char *prefix) { struct string_list merge_rr = STRING_LIST_INIT_DUP; int i, fd, 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 rerere(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(&pathspec); } fd = setup_rerere(&merge_rr, flags); if (fd < 0) return 0; if (!strcmp(argv[0], "clear")) { rerere_clear(&merge_rr); } else if (!strcmp(argv[0], "gc")) rerere_gc(&merge_rr); else if (!strcmp(argv[0], "status")) for (i = 0; i < merge_rr.nr; i++) printf("%s\n", merge_rr.items[i].string); else if (!strcmp(argv[0], "remaining")) { rerere_remaining(&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")) for (i = 0; i < merge_rr.nr; i++) { const char *path = merge_rr.items[i].string; const char *name = (const char *)merge_rr.items[i].util; diff_two(rerere_path(name, "preimage"), path, path, path); } else usage_with_options(rerere_usage, options); string_list_clear(&merge_rr, 1); return 0; }
int cmd_status(int argc, const char **argv, const char *prefix) { static struct wt_status s; int fd; unsigned char sha1[20]; static struct option builtin_status_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT_SET_INT('s', "short", &status_format, N_("show status concisely"), STATUS_FORMAT_SHORT), OPT_BOOL('b', "branch", &s.show_branch, N_("show branch information")), OPT_SET_INT(0, "porcelain", &status_format, N_("machine-readable output"), STATUS_FORMAT_PORCELAIN), OPT_SET_INT(0, "long", &status_format, N_("show status in long format (default)"), STATUS_FORMAT_LONG), OPT_BOOL('z', "null", &s.null_termination, N_("terminate entries with NUL")), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, OPT_BOOL(0, "ignored", &show_ignored_in_status, N_("show ignored files")), { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"), N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, OPT_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")), OPT_END(), }; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_status_usage, builtin_status_options); status_init_config(&s, git_status_config); argc = parse_options(argc, argv, prefix, builtin_status_options, builtin_status_usage, 0); finalize_colopts(&s.colopts, -1); finalize_deferred_config(&s); handle_untracked_files_arg(&s); if (show_ignored_in_status) s.show_ignored_files = 1; parse_pathspec(&s.pathspec, 0, PATHSPEC_PREFER_FULL, prefix, argv); read_cache_preload(&s.pathspec); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL); fd = hold_locked_index(&index_lock, 0); if (0 <= fd) update_index_if_able(&the_index, &index_lock); s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0; s.ignore_submodule_arg = ignore_submodule_arg; wt_status_collect(&s); if (s.relative_paths) s.prefix = prefix; switch (status_format) { case STATUS_FORMAT_SHORT: wt_shortstatus_print(&s); break; case STATUS_FORMAT_PORCELAIN: wt_porcelain_print(&s); break; case STATUS_FORMAT_UNSPECIFIED: die("BUG: finalize_deferred_config() should have been called"); break; case STATUS_FORMAT_NONE: case STATUS_FORMAT_LONG: s.verbose = verbose; s.ignore_submodule_arg = ignore_submodule_arg; wt_status_print(&s); break; } return 0; }
int cmd_rm(int argc, const char **argv, const char *prefix) { int i; struct pathspec pathspec; char *seen; gitmodules_config(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_rm_options, builtin_rm_usage, 0); if (!argc) usage_with_options(builtin_rm_usage, builtin_rm_options); if (!index_only) setup_work_tree(); hold_locked_index(&lock_file, 1); if (read_cache() < 0) die(_("index file corrupt")); parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD | PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP, prefix, argv); refresh_index(&the_index, REFRESH_QUIET, &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(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()) 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 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) { unsigned char sha1[20]; if (get_sha1("HEAD", sha1)) hashclr(sha1); if (check_local_mod(sha1, index_only)) exit(1); } else if (!index_only) { if (check_submodules_use_gitfiles()) 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; for (i = 0; i < list.nr; i++) { const char *path = list.entry[i].name; if (list.entry[i].is_submodule) { if (is_empty_dir(path)) { if (!rmdir(path)) { removed = 1; if (!remove_path_from_gitmodules(path)) gitmodules_modified = 1; continue; } } else { struct strbuf buf = STRBUF_INIT; strbuf_addstr(&buf, path); if (!remove_dir_recursively(&buf, 0)) { removed = 1; if (!remove_path_from_gitmodules(path)) gitmodules_modified = 1; strbuf_release(&buf); continue; } else if (!file_exists(path)) /* Submodule was removed by user */ if (!remove_path_from_gitmodules(path)) gitmodules_modified = 1; strbuf_release(&buf); /* Fallthrough and let remove_path() fail. */ } } if (!remove_path(path)) { removed = 1; continue; } if (!removed) die_errno("git rm: '%s'", path); } if (gitmodules_modified) stage_updated_gitmodules(); } if (active_cache_changed) { if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("Unable to write new index file")); } return 0; }
static char *prepare_index(int argc, const char **argv, const char *prefix, const struct commit *current_head, int is_status) { int fd; struct string_list partial; struct pathspec pathspec; int refresh_flags = REFRESH_QUIET; if (is_status) refresh_flags |= REFRESH_UNMERGED; parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_FULL, prefix, argv); if (read_cache_preload(&pathspec) < 0) die(_("index file corrupt")); if (interactive) { char *old_index_env = NULL; fd = hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die(_("unable to create temporary index")); old_index_env = getenv(INDEX_ENVIRONMENT); setenv(INDEX_ENVIRONMENT, index_lock.filename, 1); if (interactive_add(argc, argv, prefix, patch_interactive) != 0) die(_("interactive add failed")); if (old_index_env && *old_index_env) setenv(INDEX_ENVIRONMENT, old_index_env, 1); else unsetenv(INDEX_ENVIRONMENT); discard_cache(); read_cache_from(index_lock.filename); commit_style = COMMIT_NORMAL; return index_lock.filename; } /* * Non partial, non as-is commit. * * (1) get the real index; * (2) update the_index as necessary; * (3) write the_index out to the real index (still locked); * (4) return the name of the locked index file. * * The caller should run hooks on the locked real index, and * (A) if all goes well, commit the real index; * (B) on failure, rollback the real index. */ if (all || (also && pathspec.nr)) { fd = hold_locked_index(&index_lock, 1); add_files_to_cache(also ? prefix : NULL, &pathspec, 0); refresh_cache_or_die(refresh_flags); update_main_cache_tree(WRITE_TREE_SILENT); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die(_("unable to write new_index file")); commit_style = COMMIT_NORMAL; return index_lock.filename; } /* * As-is commit. * * (1) return the name of the real index file. * * The caller should run hooks on the real index, * and create commit from the_index. * We still need to refresh the index here. */ if (!only && !pathspec.nr) { fd = hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); if (active_cache_changed) { update_main_cache_tree(WRITE_TREE_SILENT); if (write_cache(fd, active_cache, active_nr) || commit_locked_index(&index_lock)) die(_("unable to write new_index file")); } else { rollback_lock_file(&index_lock); } commit_style = COMMIT_AS_IS; return get_index_file(); } /* * A partial commit. * * (0) find the set of affected paths; * (1) get lock on the real index file; * (2) update the_index with the given paths; * (3) write the_index out to the real index (still locked); * (4) get lock on the false index file; * (5) reset the_index from HEAD; * (6) update the_index the same way as (2); * (7) write the_index out to the false index file; * (8) return the name of the false index file (still locked); * * The caller should run hooks on the locked false index, and * create commit from it. Then * (A) if all goes well, commit the real index; * (B) on failure, rollback the real index; * In either case, rollback the false index. */ commit_style = COMMIT_PARTIAL; if (whence != FROM_COMMIT) { if (whence == FROM_MERGE) die(_("cannot do a partial commit during a merge.")); else if (whence == FROM_CHERRY_PICK) die(_("cannot do a partial commit during a cherry-pick.")); } memset(&partial, 0, sizeof(partial)); partial.strdup_strings = 1; if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec)) exit(1); discard_cache(); if (read_cache() < 0) die(_("cannot read the index")); fd = hold_locked_index(&index_lock, 1); add_remove_files(&partial); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die(_("unable to write new_index file")); fd = hold_lock_file_for_update(&false_lock, git_path("next-index-%"PRIuMAX, (uintmax_t) getpid()), LOCK_DIE_ON_ERROR); create_base_index(current_head); add_remove_files(&partial); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&false_lock)) die(_("unable to write temporary index file")); discard_cache(); read_cache_from(false_lock.filename); return false_lock.filename; }
int cmd_add(int argc, const char **argv, const char *prefix) { int exit_status = 0; int newfd; struct pathspec pathspec; struct dir_struct dir; int flags; int add_new_files; int require_pathspec; char *seen = NULL; int implicit_dot = 0; struct update_callback_data update_data; git_config(add_config, NULL); argc = parse_options(argc, argv, prefix, builtin_add_options, builtin_add_usage, PARSE_OPT_KEEP_ARGV0); if (patch_interactive) add_interactive = 1; if (add_interactive) exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive)); if (edit_interactive) return(edit_patch(argc, argv, prefix)); argc--; argv++; if (0 <= addremove_explicit) addremove = addremove_explicit; else if (take_worktree_changes && ADDREMOVE_DEFAULT) addremove = 0; /* "-u" was given but not "-A" */ if (addremove && take_worktree_changes) die(_("-A and -u are mutually incompatible")); /* * Warn when "git add pathspec..." was given without "-u" or "-A" * and pathspec... covers a removed path. */ memset(&update_data, 0, sizeof(update_data)); if (!take_worktree_changes && addremove_explicit < 0) update_data.warn_add_would_remove = 1; if (!take_worktree_changes && addremove_explicit < 0 && argc) /* * Turn "git add pathspec..." to "git add -A pathspec..." * in Git 2.0 but not yet */ ; /* addremove = 1; */ if (!show_only && ignore_missing) die(_("Option --ignore-missing can only be used together with --dry-run")); if (addremove) { option_with_implicit_dot = "--all"; short_option_with_implicit_dot = "-A"; } if (take_worktree_changes) { option_with_implicit_dot = "--update"; short_option_with_implicit_dot = "-u"; } if (option_with_implicit_dot && !argc) { static const char *here[2] = { ".", NULL }; argc = 1; argv = here; implicit_dot = 1; } add_new_files = !take_worktree_changes && !refresh_only; require_pathspec = !take_worktree_changes; newfd = hold_locked_index(&lock_file, 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)) | (implicit_dot ? ADD_CACHE_IMPLICIT_DOT : 0); if (require_pathspec && argc == 0) { fprintf(stderr, _("Nothing specified, nothing added.\n")); fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n")); return 0; } if (read_cache() < 0) die(_("index file corrupt")); /* * Check the "pathspec '%s' did not match any files" block * below before enabling new magic. */ parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_FULL | PATHSPEC_SYMLINK_LEADING_PATH | PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE, prefix, argv); if (add_new_files) { int baselen; struct pathspec empty_pathspec; /* Set up the default git porcelain excludes */ memset(&dir, 0, sizeof(dir)); if (!ignored_too) { dir.flags |= DIR_COLLECT_IGNORED; setup_standard_excludes(&dir); } memset(&empty_pathspec, 0, sizeof(empty_pathspec)); /* This picks up the paths that are not tracked */ baselen = fill_directory(&dir, implicit_dot ? &empty_pathspec : &pathspec); if (pathspec.nr) seen = prune_directory(&dir, &pathspec, baselen, implicit_dot ? WARN_IMPLICIT_DOT : 0); } if (refresh_only) { refresh(verbose, &pathspec); goto finish; } if (implicit_dot && prefix) refresh_cache(REFRESH_QUIET); if (pathspec.nr) { int i; if (!seen) seen = find_pathspecs_matching_against_index(&pathspec); /* * file_exists() assumes exact match */ GUARD_PATHSPEC(&pathspec, PATHSPEC_FROMTOP | PATHSPEC_LITERAL | PATHSPEC_GLOB | PATHSPEC_ICASE); for (i = 0; i < pathspec.nr; i++) { const char *path = pathspec.items[i].match; if (!seen[i] && ((pathspec.items[i].magic & (PATHSPEC_GLOB | PATHSPEC_ICASE)) || !file_exists(path))) { if (ignore_missing) { int dtype = DT_UNKNOWN; if (is_excluded(&dir, path, &dtype)) dir_add_ignored(&dir, path, pathspec.items[i].len); } else die(_("pathspec '%s' did not match any files"), pathspec.items[i].original); } } free(seen); } plug_bulk_checkin(); if ((flags & ADD_CACHE_IMPLICIT_DOT) && prefix) { /* * Check for modified files throughout the worktree so * update_callback has a chance to warn about changes * outside the cwd. */ update_data.implicit_dot = prefix; update_data.implicit_dot_len = strlen(prefix); free_pathspec(&pathspec); memset(&pathspec, 0, sizeof(pathspec)); } update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT; update_files_in_cache(prefix, &pathspec, &update_data); exit_status |= !!update_data.add_errors; if (add_new_files) exit_status |= add_files(&dir, flags); unplug_bulk_checkin(); finish: if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) die(_("Unable to write new index file")); } return exit_status; }