int main(int argc, const char **argv) { const char *prefix = "prefix/"; const char *usage[] = { "test-parse-options <options>", NULL }; struct option options[] = { OPT_BOOLEAN('b', "boolean", &boolean, "get a boolean"), OPT_BIT('4', "or4", &boolean, "bitwise-or boolean with ...0100", 4), OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4), OPT_GROUP(""), OPT_INTEGER('i', "integer", &integer, "get a integer"), OPT_INTEGER('j', NULL, &integer, "get a integer, too"), OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23), OPT_DATE('t', NULL, ×tamp, "get timestamp of <time>"), OPT_CALLBACK('L', "length", &integer, "str", "get length of <str>", length_callback), OPT_FILENAME('F', "file", &file, "set file to <FILE>"), OPT_GROUP("String options"), OPT_STRING('s', "string", &string, "string", "get a string"), OPT_STRING(0, "string2", &string, "str", "get another string"), OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"), OPT_STRING('o', NULL, &string, "str", "get another string"), OPT_SET_PTR(0, "default-string", &string, "set string to default", (unsigned long)"default"), OPT_GROUP("Magic arguments"), OPT_ARGUMENT("quux", "means --quux"), OPT_NUMBER_CALLBACK(&integer, "set integer to NUM", number_callback), { OPTION_BOOLEAN, '+', NULL, &boolean, NULL, "same as -b", PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH }, OPT_GROUP("Standard options"), OPT__ABBREV(&abbrev), OPT__VERBOSE(&verbose), OPT__DRY_RUN(&dry_run), OPT__QUIET(&quiet), OPT_END(), }; int i; argc = parse_options(argc, argv, prefix, options, usage, 0); printf("boolean: %d\n", boolean); printf("integer: %u\n", integer); printf("timestamp: %lu\n", timestamp); printf("string: %s\n", string ? string : "(not set)"); printf("abbrev: %d\n", abbrev); printf("verbose: %d\n", verbose); printf("quiet: %s\n", quiet ? "yes" : "no"); printf("dry run: %s\n", dry_run ? "yes" : "no"); printf("file: %s\n", file ? file : "(not set)"); for (i = 0; i < argc; i++) printf("arg %02d: %s\n", i, argv[i]); return 0; }
int main(int argc, char **argv) { const char *prefix = "prefix/"; const char *usage[] = { "test-parse-options <options>", NULL }; struct string_list expect = STRING_LIST_INIT_NODUP; struct option options[] = { OPT_BOOL(0, "yes", &boolean, "get a boolean"), OPT_BOOL('D', "no-doubt", &boolean, "begins with 'no-'"), { OPTION_SET_INT, 'B', "no-fear", &boolean, NULL, "be brave", PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 }, OPT_COUNTUP('b', "boolean", &boolean, "increment by one"), OPT_BIT('4', "or4", &boolean, "bitwise-or boolean with ...0100", 4), OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4), OPT_GROUP(""), OPT_INTEGER('i', "integer", &integer, "get a integer"), OPT_INTEGER('j', NULL, &integer, "get a integer, too"), OPT_MAGNITUDE('m', "magnitude", &magnitude, "get a magnitude"), OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23), OPT_DATE('t', NULL, ×tamp, "get timestamp of <time>"), OPT_CALLBACK('L', "length", &integer, "str", "get length of <str>", length_callback), OPT_FILENAME('F', "file", &file, "set file to <file>"), OPT_GROUP("String options"), OPT_STRING('s', "string", &string, "string", "get a string"), OPT_STRING(0, "string2", &string, "str", "get another string"), OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"), OPT_STRING('o', NULL, &string, "str", "get another string"), OPT_NOOP_NOARG(0, "obsolete"), OPT_STRING_LIST(0, "list", &list, "str", "add str to list"), OPT_GROUP("Magic arguments"), OPT_ARGUMENT("quux", "means --quux"), OPT_NUMBER_CALLBACK(&integer, "set integer to NUM", number_callback), { OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b", PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH }, { OPTION_COUNTUP, 0, "ambiguous", &ambiguous, NULL, "positive ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG }, { OPTION_COUNTUP, 0, "no-ambiguous", &ambiguous, NULL, "negative ambiguity", PARSE_OPT_NOARG | PARSE_OPT_NONEG }, OPT_GROUP("Standard options"), OPT__ABBREV(&abbrev), OPT__VERBOSE(&verbose, "be verbose"), OPT__DRY_RUN(&dry_run, "dry run"), OPT__QUIET(&quiet, "be quiet"), OPT_CALLBACK(0, "expect", &expect, "string", "expected output in the variable dump", collect_expect), OPT_END(), }; int i; int ret = 0; argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0); if (length_cb.called) { const char *arg = length_cb.arg; int unset = length_cb.unset; show(&expect, &ret, "Callback: \"%s\", %d", (arg ? arg : "not set"), unset); } show(&expect, &ret, "boolean: %d", boolean); show(&expect, &ret, "integer: %d", integer); show(&expect, &ret, "magnitude: %lu", magnitude); show(&expect, &ret, "timestamp: %lu", timestamp); show(&expect, &ret, "string: %s", string ? string : "(not set)"); show(&expect, &ret, "abbrev: %d", abbrev); show(&expect, &ret, "verbose: %d", verbose); show(&expect, &ret, "quiet: %d", quiet); show(&expect, &ret, "dry run: %s", dry_run ? "yes" : "no"); show(&expect, &ret, "file: %s", file ? file : "(not set)"); for (i = 0; i < list.nr; i++) show(&expect, &ret, "list: %s", list.items[i].string); for (i = 0; i < argc; i++) show(&expect, &ret, "arg %02d: %s", i, argv[i]); return ret; }
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); if (!current_branch || !current_branch->merge || !current_branch->merge[0] || !current_branch->merge[0]->dst) { fprintf(stderr, _("Could not find a tracked" " remote branch, please" " specify <upstream> manually.\n")); usage_with_options(cherry_usage, options); } upstream = current_branch->merge[0]->dst; } init_revisions(&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 (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0) 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); list = list->next; } free_patch_ids(&ids); return 0; }
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 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")), { OPTION_CALLBACK, '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)")), { OPTION_CALLBACK, 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)
int cmd_describe(int argc, const char **argv, const char *prefix) { int contains = 0; struct option options[] = { OPT_BOOLEAN(0, "contains", &contains, N_("find the tag that comes after the commit")), OPT_BOOLEAN(0, "debug", &debug, N_("debug search strategy on stderr")), OPT_BOOLEAN(0, "all", &all, N_("use any ref")), OPT_BOOLEAN(0, "tags", &tags, N_("use any tag, even unannotated")), OPT_BOOLEAN(0, "long", &longformat, N_("always use long format")), OPT__ABBREV(&abbrev), OPT_SET_INT(0, "exact-match", &max_candidates, N_("only output exact matches"), 0), OPT_INTEGER(0, "candidates", &max_candidates, N_("consider <n> most recent tags (default: 10)")), OPT_STRING(0, "match", &pattern, N_("pattern"), N_("only consider tags matching <pattern>")), OPT_BOOLEAN(0, "always", &always, N_("show abbreviated commit object as fallback")), {OPTION_STRING, 0, "dirty", &dirty, N_("mark"), N_("append <mark> on dirty working tree (default: \"-dirty\")"), PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"}, OPT_END(), }; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, describe_usage, 0); if (abbrev < 0) abbrev = DEFAULT_ABBREV; if (max_candidates < 0) max_candidates = 0; else if (max_candidates > MAX_TAGS) max_candidates = MAX_TAGS; save_commit_buffer = 0; if (longformat && abbrev == 0) die(_("--long is incompatible with --abbrev=0")); if (contains) { const char **args = xmalloc((7 + argc) * sizeof(char *)); int i = 0; args[i++] = "name-rev"; args[i++] = "--name-only"; args[i++] = "--no-undefined"; if (always) args[i++] = "--always"; if (!all) { args[i++] = "--tags"; if (pattern) { char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1); sprintf(s, "--refs=refs/tags/%s", pattern); args[i++] = s; } } memcpy(args + i, argv, argc * sizeof(char *)); args[i + argc] = NULL; return cmd_name_rev(i + argc, args, prefix); } init_hash(&names); for_each_rawref(get_name, NULL); if (!names.nr && !always) die(_("No names found, cannot describe anything.")); if (argc == 0) { if (dirty) { static struct lock_file index_lock; int fd; read_cache_preload(NULL); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL); fd = hold_locked_index(&index_lock, 0); if (0 <= fd) update_index_if_able(&the_index, &index_lock); if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1, diff_index_args, prefix)) dirty = NULL; } describe("HEAD", 1); } else if (dirty) { die(_("--dirty is incompatible with committishes")); } else { while (argc-- > 0) { describe(*argv++, argc == 0); } } return 0; }
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; }
int cmd_describe(int argc, const char **argv, const char *prefix) { int contains = 0; struct option options[] = { OPT_BOOLEAN(0, "contains", &contains, "find the tag that comes after the commit"), OPT_BOOLEAN(0, "debug", &debug, "debug search strategy on stderr"), OPT_BOOLEAN(0, "all", &all, "use any ref in .git/refs"), OPT_BOOLEAN(0, "tags", &tags, "use any tag in .git/refs/tags"), OPT_BOOLEAN(0, "long", &longformat, "always use long format"), OPT__ABBREV(&abbrev), OPT_SET_INT(0, "exact-match", &max_candidates, "only output exact matches", 0), OPT_INTEGER(0, "candidates", &max_candidates, "consider <n> most recent tags (default: 10)"), OPT_STRING(0, "match", &pattern, "pattern", "only consider tags matching <pattern>"), OPT_BOOLEAN(0, "always", &always, "show abbreviated commit object as fallback"), OPT_END(), }; argc = parse_options(argc, argv, options, describe_usage, 0); if (max_candidates < 0) max_candidates = 0; else if (max_candidates > MAX_TAGS) max_candidates = MAX_TAGS; save_commit_buffer = 0; if (longformat && abbrev == 0) die("--long is incompatible with --abbrev=0"); if (contains) { const char **args = xmalloc((7 + argc) * sizeof(char*)); int i = 0; args[i++] = "name-rev"; args[i++] = "--name-only"; args[i++] = "--no-undefined"; if (always) args[i++] = "--always"; if (!all) { args[i++] = "--tags"; if (pattern) { char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1); sprintf(s, "--refs=refs/tags/%s", pattern); args[i++] = s; } } memcpy(args + i, argv, argc * sizeof(char*)); args[i + argc] = NULL; return cmd_name_rev(i + argc, args, prefix); } if (argc == 0) { describe("HEAD", 1); } else { while (argc-- > 0) { describe(*argv++, argc == 0); } } return 0; }
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( 0, "set-upstream", &track, N_("change upstream info"), BRANCH_TRACK_OVERRIDE), OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")), OPT_BOOL(0, "unset-upstream", &unset_upstream, "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_WITH(&filter.with_commit, N_("print only branches that 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_BOOL(0, "list", &list, N_("list branch names")), OPT_BOOL('l', "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")), OPT_MERGED(&filter, N_("print only branches that are merged")), OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
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; 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_("Show blank SHA-1 for 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), /* * The following two options are parsed by parse_revision_opt() * and are only included here to get included in the "-h" * output: */ { OPTION_LOWLEVEL_CALLBACK, 0, "indent-heuristic", NULL, NULL, N_("Use an experimental heuristic to improve diffs"), PARSE_OPT_NOARG, parse_opt_unknown_cb }, 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")), { OPTION_CALLBACK, 'C', NULL, &opt, N_("score"), N_("Find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback }, { OPTION_CALLBACK, '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; git_config(git_blame_config, &output_option); init_revisions(&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: exit(129); 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 < GIT_SHA1_HEXSZ) /* one more abbrev length is needed for the boundary commit */ abbrev++; else if (!abbrev) abbrev = GIT_SHA1_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_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; } 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); init_scoreboard(&sb); sb.revs = &revs; sb.contents_from = contents_from; sb.reverse = reverse; setup_scoreboard(&sb, path, &o); 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)) usage(blame_usage); if (lno < top || ((lno || 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) 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; 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_PORCELAIN)) find_alignment(&sb, &output_option); 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); } return 0; }
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), 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); if (!current_branch || !current_branch->merge || !current_branch->merge[0] || !current_branch->merge[0]->dst) { fprintf(stderr, "Could not find a tracked" " remote branch, please" " specify <upstream> manually.\n"); usage_with_options(cherry_usage, options); } upstream = current_branch->merge[0]->dst; } init_revisions(&revs, prefix); revs.diff = 1; revs.combine_merges = 0; revs.ignore_merges = 1; DIFF_OPT_SET(&revs.diffopt, RECURSIVE); 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 (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0) return 0; } get_patch_ids(&revs, &ids, prefix); 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) { /* ignore merges */ if (commit->parents && commit->parents->next) continue; commit_list_insert(commit, &list); } while (list) { char sign = '+'; commit = list->item; if (has_commit_patch_id(commit, &ids)) sign = '-'; if (verbose) { struct strbuf buf = STRBUF_INIT; struct pretty_print_context ctx = {0}; pretty_print_commit(CMIT_FMT_ONELINE, commit, &buf, &ctx); printf("%c %s %s\n", sign, find_unique_abbrev(commit->object.sha1, abbrev), buf.buf); strbuf_release(&buf); } else { printf("%c %s\n", sign, find_unique_abbrev(commit->object.sha1, abbrev)); } list = list->next; } free_patch_ids(&ids); return 0; }