/* * Opposite of the above: the command line did not have -- marker * and we parsed the arg as a refname. It should not be interpretable * as a filename. */ void verify_non_filename(const char *prefix, const char *arg) { if (!is_inside_work_tree() || is_inside_git_dir()) return; if (*arg == '-') return; /* flag */ if (!check_filename(prefix, arg)) return; die("ambiguous argument '%s': both revision and filename\n" "Use '--' to separate filenames from revisions", arg); }
/* * Opposite of the above: the command line did not have -- marker * and we parsed the arg as a refname. It should not be interpretable * as a filename. */ void verify_non_filename(const char *prefix, const char *arg) { if (!is_inside_work_tree() || is_inside_git_dir()) return; if (*arg == '-') return; /* flag */ if (!check_filename(prefix, arg)) return; die("ambiguous argument '%s': both revision and filename\n" "Use '--' to separate paths from revisions, like this:\n" "'git <command> [<revision>...] -- [<file>...]'", arg); }
/* * Opposite of the above: the command line did not have -- marker * and we parsed the arg as a refname. It should not be interpretable * as a filename. */ void verify_non_filename(const char *prefix, const char *arg) { const char *name; struct stat st; if (!is_inside_work_tree() || is_inside_git_dir()) return; if (*arg == '-') return; /* flag */ name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg; if (!lstat(name, &st)) die("ambiguous argument '%s': both revision and filename\n" "Use '--' to separate filenames from revisions", arg); if (errno != ENOENT && errno != ENOTDIR) die_errno("failed to stat '%s'", arg); }
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; unsigned char sha1[20]; const char *name = NULL; 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 == 2 && !strcmp("--local-env-vars", argv[1])) { int i; for (i = 0; local_repo_env[i]; i++) printf("%s\n", local_repo_env[i]); return 0; } if (argc > 2 && !strcmp(argv[1], "--resolve-git-dir")) { const char *gitdir = resolve_gitdir(argv[2]); if (!gitdir) die("not a gitdir '%s'", argv[2]); puts(gitdir); return 0; } if (argc > 1 && !strcmp("-h", argv[1])) usage(builtin_rev_parse_usage); prefix = setup_git_directory(); git_config(git_default_config, NULL); for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (as_is) { if (show_file(arg) && 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 (!prefixcmp(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); continue; } if (!strcmp(arg, "--default")) { def = argv[i+1]; i++; 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; continue; } if (!strcmp(arg, "--short") || !prefixcmp(arg, "--short=")) { filter &= ~(DO_FLAGS|DO_NOREV); verify = 1; abbrev = DEFAULT_ABBREV; if (arg[7] == '=') abbrev = strtoul(arg + 8, NULL, 10); if (abbrev < MINIMUM_ABBREV) abbrev = MINIMUM_ABBREV; else if (40 <= abbrev) abbrev = 40; 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 (!prefixcmp(arg, "--abbrev-ref") && (!arg[12] || arg[12] == '=')) { abbrev_ref = 1; abbrev_ref_strict = warn_ambiguous_refs; if (arg[12] == '=') { if (!strcmp(arg + 13, "strict")) abbrev_ref_strict = 1; else if (!strcmp(arg + 13, "loose")) abbrev_ref_strict = 0; else die("unknown mode for %s", arg); } continue; } if (!strcmp(arg, "--all")) { for_each_ref(show_reference, NULL); continue; } if (!prefixcmp(arg, "--disambiguate=")) { for_each_abbrev(arg + 15, show_abbrev, NULL); continue; } if (!strcmp(arg, "--bisect")) { for_each_ref_in("refs/bisect/bad", show_reference, NULL); for_each_ref_in("refs/bisect/good", anti_reference, NULL); continue; } if (!prefixcmp(arg, "--branches=")) { for_each_glob_ref_in(show_reference, arg + 11, "refs/heads/", NULL); continue; } if (!strcmp(arg, "--branches")) { for_each_branch_ref(show_reference, NULL); continue; } if (!prefixcmp(arg, "--tags=")) { for_each_glob_ref_in(show_reference, arg + 7, "refs/tags/", NULL); continue; } if (!strcmp(arg, "--tags")) { for_each_tag_ref(show_reference, NULL); continue; } if (!prefixcmp(arg, "--glob=")) { for_each_glob_ref(show_reference, arg + 7, NULL); continue; } if (!prefixcmp(arg, "--remotes=")) { for_each_glob_ref_in(show_reference, arg + 10, "refs/remotes/", NULL); continue; } if (!strcmp(arg, "--remotes")) { for_each_remote_ref(show_reference, NULL); continue; } if (!strcmp(arg, "--show-toplevel")) { const char *work_tree = get_git_work_tree(); if (work_tree) puts(work_tree); 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")) { const char *gitdir = getenv(GIT_DIR_ENVIRONMENT); static char cwd[PATH_MAX]; int len; if (gitdir) { puts(gitdir); continue; } if (!prefix) { puts(".git"); continue; } if (!getcwd(cwd, PATH_MAX)) die_errno("unable to get current working directory"); len = strlen(cwd); printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : ""); 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 (!prefixcmp(arg, "--since=")) { show_datestring("--max-age=", arg+8); continue; } if (!prefixcmp(arg, "--after=")) { show_datestring("--max-age=", arg+8); continue; } if (!prefixcmp(arg, "--before=")) { show_datestring("--min-age=", arg+9); continue; } if (!prefixcmp(arg, "--until=")) { show_datestring("--min-age=", arg+8); 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_sha1(name, sha1)) { if (verify) revs_count++; else show_rev(type, sha1, name); continue; } if (verify) die_no_single_rev(quiet); as_is = 1; if (!show_file(arg)) continue; verify_filename(prefix, arg, 1); } if (verify) { if (revs_count == 1) { show_rev(type, sha1, name); return 0; } else if (revs_count == 0 && show_default()) return 0; die_no_single_rev(quiet); } else show_default(); return 0; }
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; unsigned char sha1[20]; unsigned int flags = 0; const char *name = NULL; struct object_context unused; 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"); puts(git_path("%s", argv[i + 1])); 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_SHA1_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 (40 <= abbrev) abbrev = 40; 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); continue; } if (skip_prefix(arg, "--disambiguate=", &arg)) { for_each_abbrev(arg, show_abbrev, NULL); continue; } if (!strcmp(arg, "--bisect")) { for_each_ref_in("refs/bisect/bad", show_reference, NULL); for_each_ref_in("refs/bisect/good", anti_reference, NULL); 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); 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")) { const char *gitdir = getenv(GIT_DIR_ENVIRONMENT); char *cwd; int len; if (gitdir) { puts(gitdir); continue; } if (!prefix) { puts(".git"); 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")) { const char *pfx = prefix ? prefix : ""; puts(prefix_filename(pfx, strlen(pfx), get_git_common_dir())); 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, "--shared-index-path")) { if (read_cache() < 0) die(_("Could not read the index")); if (the_index.split_index) { const unsigned char *sha1 = the_index.split_index->base_sha1; puts(git_path("sharedindex.%s", sha1_to_hex(sha1))); } 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 (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_sha1_with_context(name, flags, sha1, &unused)) { if (verify) revs_count++; else show_rev(type, sha1, 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); } if (verify) { if (revs_count == 1) { show_rev(type, sha1, name); return 0; } else if (revs_count == 0 && show_default()) return 0; die_no_single_rev(quiet); } else show_default(); return 0; }
int cmd_reset(int argc, const char **argv, const char *prefix) { int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0; int patch_mode = 0; const char *rev = "HEAD"; unsigned char sha1[20], *orig = NULL, sha1_orig[20], *old_orig = NULL, sha1_old_orig[20]; struct commit *commit; char *reflog_action, msg[1024]; const struct option options[] = { OPT_SET_INT(0, "mixed", &reset_type, "reset HEAD and index", MIXED), OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT), OPT_SET_INT(0, "hard", &reset_type, "reset HEAD, index and working tree", HARD), OPT_SET_INT(0, "merge", &reset_type, "reset HEAD, index and working tree", MERGE), OPT_BOOLEAN('q', NULL, &quiet, "disable showing new HEAD in hard reset and progress message"), OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"), OPT_END() }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_reset_usage, PARSE_OPT_KEEP_DASHDASH); reflog_action = args_to_str(argv); setenv("GIT_REFLOG_ACTION", reflog_action, 0); /* * Possible arguments are: * * git reset [-opts] <rev> <paths>... * git reset [-opts] <rev> -- <paths>... * git reset [-opts] -- <paths>... * git reset [-opts] <paths>... * * At this point, argv[i] points immediately after [-opts]. */ if (i < argc) { if (!strcmp(argv[i], "--")) { i++; /* reset to HEAD, possibly with paths */ } else if (i + 1 < argc && !strcmp(argv[i+1], "--")) { rev = argv[i]; i += 2; } /* * Otherwise, argv[i] could be either <rev> or <paths> and * has to be unambiguous. */ else if (!get_sha1(argv[i], sha1)) { /* * Ok, argv[i] looks like a rev; it should not * be a filename. */ verify_non_filename(prefix, argv[i]); rev = argv[i++]; } else { /* Otherwise we treat this as a filename */ verify_filename(prefix, argv[i]); } } if (get_sha1(rev, sha1)) die("Failed to resolve '%s' as a valid ref.", rev); commit = lookup_commit_reference(sha1); if (!commit) die("Could not parse object '%s'.", rev); hashcpy(sha1, commit->object.sha1); if (patch_mode) { if (reset_type != NONE) die("--patch is incompatible with --{hard,mixed,soft}"); return interactive_reset(rev, argv + i, prefix); } /* 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 (i < argc) { if (reset_type == MIXED) warning("--mixed option is deprecated with paths."); else if (reset_type != NONE) die("Cannot do %s reset with paths.", reset_type_names[reset_type]); return read_from_tree(prefix, argv + i, sha1, quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN); } if (reset_type == NONE) reset_type = MIXED; /* by default */ if ((reset_type == HARD || reset_type == MERGE) && !is_inside_work_tree()) die("%s reset requires a work tree", reset_type_names[reset_type]); /* 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) { if (is_merge() || read_cache() < 0 || unmerged_cache()) die("Cannot do a soft reset in the middle of a merge."); } else if (reset_index_file(sha1, reset_type, quiet)) die("Could not reset index file to revision '%s'.", rev); /* Any resets update HEAD to the head being switched to, * saving the previous head in ORIG_HEAD before. */ if (!get_sha1("ORIG_HEAD", sha1_old_orig)) old_orig = sha1_old_orig; if (!get_sha1("HEAD", sha1_orig)) { orig = sha1_orig; prepend_reflog_action("updating ORIG_HEAD", msg, sizeof(msg)); update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR); } else if (old_orig) delete_ref("ORIG_HEAD", old_orig, 0); prepend_reflog_action("updating HEAD", msg, sizeof(msg)); update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR); switch (reset_type) { case HARD: if (!update_ref_status && !quiet) print_new_head_line(commit); break; case SOFT: /* Nothing else to do. */ break; case MIXED: /* Report what has not been updated. */ update_index_refresh(0, NULL, quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN); break; } remove_branch_state(); free(reflog_action); return update_ref_status; }
int cmd_ls_files(int argc, const char **argv, const char *prefix) { int i; int exc_given = 0, require_work_tree = 0; struct dir_struct dir; memset(&dir, 0, sizeof(dir)); if (prefix) prefix_offset = strlen(prefix); git_config(git_default_config, NULL); for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--")) { i++; break; } if (!strcmp(arg, "-z")) { line_terminator = 0; continue; } if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) { tag_cached = "H "; tag_unmerged = "M "; tag_removed = "R "; tag_modified = "C "; tag_other = "? "; tag_killed = "K "; if (arg[1] == 'v') show_valid_bit = 1; continue; } if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) { show_cached = 1; continue; } if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) { show_deleted = 1; continue; } if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) { show_modified = 1; require_work_tree = 1; continue; } if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) { show_others = 1; require_work_tree = 1; continue; } if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) { dir.show_ignored = 1; require_work_tree = 1; continue; } if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) { show_stage = 1; continue; } if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) { show_killed = 1; require_work_tree = 1; continue; } if (!strcmp(arg, "--directory")) { dir.show_other_directories = 1; continue; } if (!strcmp(arg, "--no-empty-directory")) { dir.hide_empty_directories = 1; continue; } if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) { /* There's no point in showing unmerged unless * you also show the stage information. */ show_stage = 1; show_unmerged = 1; continue; } if (!strcmp(arg, "-x") && i+1 < argc) { exc_given = 1; add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]); continue; } if (!prefixcmp(arg, "--exclude=")) { exc_given = 1; add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]); continue; } if (!strcmp(arg, "-X") && i+1 < argc) { exc_given = 1; add_excludes_from_file(&dir, argv[++i]); continue; } if (!prefixcmp(arg, "--exclude-from=")) { exc_given = 1; add_excludes_from_file(&dir, arg+15); continue; } if (!prefixcmp(arg, "--exclude-per-directory=")) { exc_given = 1; dir.exclude_per_dir = arg + 24; continue; } if (!strcmp(arg, "--exclude-standard")) { exc_given = 1; setup_standard_excludes(&dir); continue; } if (!strcmp(arg, "--full-name")) { prefix_offset = 0; continue; } if (!strcmp(arg, "--error-unmatch")) { error_unmatch = 1; continue; } if (!prefixcmp(arg, "--with-tree=")) { with_tree = arg + 12; continue; } if (!prefixcmp(arg, "--abbrev=")) { abbrev = strtoul(arg+9, NULL, 10); if (abbrev && abbrev < MINIMUM_ABBREV) abbrev = MINIMUM_ABBREV; else if (abbrev > 40) abbrev = 40; continue; } if (!strcmp(arg, "--abbrev")) { abbrev = DEFAULT_ABBREV; continue; } if (*arg == '-') usage(ls_files_usage); break; } if (require_work_tree && !is_inside_work_tree()) setup_work_tree(); pathspec = get_pathspec(prefix, argv + i); /* Verify that the pathspec matches the prefix */ if (pathspec) prefix = verify_pathspec(prefix); /* Treat unmatching pathspec elements as errors */ if (pathspec && error_unmatch) { int num; for (num = 0; pathspec[num]; num++) ; ps_matched = xcalloc(1, num); } if (dir.show_ignored && !exc_given) { fprintf(stderr, "%s: --ignored needs some exclude pattern\n", argv[0]); exit(1); } /* With no flags, we default to showing the cached files */ if (!(show_stage | show_deleted | show_others | show_unmerged | show_killed | show_modified)) show_cached = 1; read_cache(); if (prefix) prune_cache(prefix); 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_cache(with_tree, prefix); } show_files(&dir, prefix); if (ps_matched) { int bad; bad = report_path_error(ps_matched, pathspec, prefix_offset); if (bad) fprintf(stderr, "Did you forget to 'git add'?\n"); return bad ? 1 : 0; } return 0; }
int cmd_ls_files(int argc, const char **argv, const char *prefix) { int require_work_tree = 0, show_tag = 0; struct dir_struct dir; struct option builtin_ls_files_options[] = { { OPTION_CALLBACK, 'z', NULL, NULL, NULL, "paths are separated with NUL character", PARSE_OPT_NOARG, option_parse_z }, OPT_BOOLEAN('t', NULL, &show_tag, "identify the file status with tags"), OPT_BOOLEAN('v', NULL, &show_valid_bit, "use lowercase letters for 'assume unchanged' files"), OPT_BOOLEAN('c', "cached", &show_cached, "show cached files in the output (default)"), OPT_BOOLEAN('d', "deleted", &show_deleted, "show deleted files in the output"), OPT_BOOLEAN('m', "modified", &show_modified, "show modified files in the output"), OPT_BOOLEAN('o', "others", &show_others, "show other files in the output"), OPT_BIT('i', "ignored", &dir.flags, "show ignored files in the output", DIR_SHOW_IGNORED), OPT_BOOLEAN('s', "stage", &show_stage, "show staged contents' object name in the output"), OPT_BOOLEAN('k', "killed", &show_killed, "show files on the filesystem that need to be removed"), OPT_BIT(0, "directory", &dir.flags, "show 'other' directories' name only", DIR_SHOW_OTHER_DIRECTORIES), OPT_NEGBIT(0, "empty-directory", &dir.flags, "don't show empty directories", DIR_HIDE_EMPTY_DIRECTORIES), OPT_BOOLEAN('u', "unmerged", &show_unmerged, "show unmerged files in the output"), OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo, "show resolve-undo information"), { OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern", "skip files matching pattern", 0, option_parse_exclude }, { OPTION_CALLBACK, 'X', "exclude-from", &dir, "file", "exclude patterns are read from <file>", 0, option_parse_exclude_from }, OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, "file", "read additional per-directory exclude patterns in <file>"), { OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL, "add the standard git exclusions", PARSE_OPT_NOARG, option_parse_exclude_standard }, { OPTION_SET_INT, 0, "full-name", &prefix_offset, NULL, "make the output relative to the project top directory", PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL }, OPT_BOOLEAN(0, "error-unmatch", &error_unmatch, "if any <file> is not in the index, treat this as an error"), OPT_STRING(0, "with-tree", &with_tree, "tree-ish", "pretend that paths removed since <tree-ish> are still present"), OPT__ABBREV(&abbrev), OPT_END() }; memset(&dir, 0, sizeof(dir)); if (prefix) prefix_offset = strlen(prefix); git_config(git_default_config, NULL); if (read_cache() < 0) die("index file corrupt"); argc = parse_options(argc, argv, prefix, builtin_ls_files_options, ls_files_usage, 0); if (show_tag || show_valid_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(); pathspec = get_pathspec(prefix, argv); /* be nice with submodule paths ending in a slash */ if (pathspec) strip_trailing_slash_from_submodules(); /* Verify that the pathspec matches the prefix */ if (pathspec) prefix = verify_pathspec(prefix); /* Treat unmatching pathspec elements as errors */ if (pathspec && error_unmatch) { int num; for (num = 0; pathspec[num]; num++) ; ps_matched = xcalloc(1, num); } 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 (prefix) prune_cache(prefix); 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_cache(with_tree, prefix); } show_files(&dir, prefix); if (show_resolve_undo) show_ru_info(prefix); if (ps_matched) { int bad; bad = report_path_error(ps_matched, pathspec, prefix_offset); if (bad) fprintf(stderr, "Did you forget to 'git add'?\n"); return bad ? 1 : 0; } return 0; }