int cmd_prune(int argc, const char **argv, const char *prefix) { struct rev_info revs; const struct option options[] = { OPT__DRY_RUN(&show_only, "do not remove, show only"), OPT__VERBOSE(&verbose, "report pruned objects"), OPT_DATE(0, "expire", &expire, "expire objects older than <time>"), OPT_END() }; char *s; expire = ULONG_MAX; save_commit_buffer = 0; read_replace_refs = 0; init_revisions(&revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); while (argc--) { unsigned char sha1[20]; const char *name = *argv++; if (!get_sha1(name, sha1)) { struct object *object = parse_object(sha1); if (!object) die("bad object: %s", name); add_pending_object(&revs, object, ""); } else die("unrecognized argument: %s", name); } mark_reachable_objects(&revs, 1); prune_object_dir(get_object_directory()); prune_packed_objects(show_only); remove_temporary_files(get_object_directory()); s = xstrdup(mkpath("%s/pack", get_object_directory())); remove_temporary_files(s); free(s); return 0; }
int sequencer_pick_revisions(struct replay_opts *opts) { struct commit_list *todo_list = NULL; unsigned char sha1[20]; int i; if (opts->subcommand == REPLAY_NONE) assert(opts->revs); read_and_refresh_cache(opts); /* * Decide what to do depending on the arguments; a fresh * cherry-pick should be handled differently from an existing * one that is being continued */ if (opts->subcommand == REPLAY_REMOVE_STATE) { remove_sequencer_state(); return 0; } if (opts->subcommand == REPLAY_ROLLBACK) return sequencer_rollback(opts); if (opts->subcommand == REPLAY_CONTINUE) return sequencer_continue(opts); for (i = 0; i < opts->revs->pending.nr; i++) { unsigned char sha1[20]; const char *name = opts->revs->pending.objects[i].name; /* This happens when using --stdin. */ if (!strlen(name)) continue; if (!get_sha1(name, sha1)) { if (!lookup_commit_reference_gently(sha1, 1)) { enum object_type type = sha1_object_info(sha1, NULL); die(_("%s: can't cherry-pick a %s"), name, typename(type)); } } else die(_("%s: bad revision"), name); }
static void append_one_rev(const char *av) { unsigned char revkey[20]; if (!get_sha1(av, revkey)) { append_ref(av, revkey, 0); return; } if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) { /* glob style match */ int saved_matches = ref_name_cnt; match_ref_pattern = av; match_ref_slash = count_slash(av); for_each_ref(append_matching_ref, NULL); if (saved_matches == ref_name_cnt && ref_name_cnt < MAX_REVS) error("no matching refs with %s", av); if (saved_matches + 1 < ref_name_cnt) sort_ref_range(saved_matches, ref_name_cnt); return; } die("bad sha1 reference %s", av); }
static int parse_insn_line(struct todo_item *item, const char *bol, char *eol) { unsigned char commit_sha1[20]; char *end_of_object_name; int i, saved, status, padding; /* left-trim */ bol += strspn(bol, " \t"); for (i = 0; i < ARRAY_SIZE(todo_command_strings); i++) if (skip_prefix(bol, todo_command_strings[i], &bol)) { item->command = i; break; } if (i >= ARRAY_SIZE(todo_command_strings)) return -1; /* Eat up extra spaces/ tabs before object name */ padding = strspn(bol, " \t"); if (!padding) return -1; bol += padding; end_of_object_name = (char *) bol + strcspn(bol, " \t\n"); saved = *end_of_object_name; *end_of_object_name = '\0'; status = get_sha1(bol, commit_sha1); *end_of_object_name = saved; item->arg = end_of_object_name + strspn(end_of_object_name, " \t"); item->arg_len = (int)(eol - item->arg); if (status < 0) return -1; item->commit = lookup_commit_reference(commit_sha1); return !item->commit; }
static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn, struct wt_status *s) { unsigned char sha1[20]; if (s->relative_paths) s->prefix = prefix; if (amend) { s->amend = 1; s->reference = "HEAD^1"; } s->verbose = verbose; s->index_file = index_file; s->fp = fp; s->nowarn = nowarn; s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0; wt_status_collect(s); 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: wt_status_print(s); break; } return s->commitable; }
static int parse_reuse_arg(const struct option *opt, const char *arg, int unset) { struct msg_arg *msg = opt->value; char *buf; unsigned char object[20]; enum object_type type; unsigned long len; if (msg->buf.len) strbuf_addch(&(msg->buf), '\n'); if (get_sha1(arg, object)) die("Failed to resolve '%s' as a valid ref.", arg); if (!(buf = read_sha1_file(object, &type, &len)) || !len) { free(buf); die("Failed to read object '%s'.", arg);; } strbuf_add(&(msg->buf), buf, len); free(buf); msg->given = 1; return 0; }
static int verify_commit(const char *name, unsigned flags) { enum object_type type; unsigned char sha1[20]; char *buf; unsigned long size; int ret; if (get_sha1(name, sha1)) return error("commit '%s' not found.", name); buf = read_sha1_file(sha1, &type, &size); if (!buf) return error("%s: unable to read file.", name); if (type != OBJ_COMMIT) return error("%s: cannot verify a non-commit object of type %s.", name, typename(type)); ret = run_gpg_verify(sha1, buf, size, flags); free(buf); return ret; }
static int show(int argc, const char **argv, const char *prefix) { const char *object_ref; struct notes_tree *t; unsigned char object[20]; const unsigned char *note; int retval; struct option options[] = { OPT_END() }; argc = parse_options(argc, argv, prefix, options, git_notes_show_usage, 0); if (1 < argc) { error("too many parameters"); usage_with_options(git_notes_show_usage, options); } object_ref = argc ? argv[0] : "HEAD"; if (get_sha1(object_ref, object)) die("Failed to resolve '%s' as a valid ref.", object_ref); t = init_notes_check("show"); note = get_note(t, object); if (!note) retval = error("No note found for object %s.", sha1_to_hex(object)); else { const char *show_args[3] = {"show", sha1_to_hex(note), NULL}; retval = execv_git_cmd(show_args); } free_notes(t); return retval; }
static void wt_status_get_detached_from(struct wt_status_state *state) { struct grab_1st_switch_cbdata cb; struct commit *commit; unsigned char sha1[20]; char *ref = NULL; strbuf_init(&cb.buf, 0); if (for_each_reflog_ent_reverse("HEAD", grab_1st_switch, &cb) <= 0) { strbuf_release(&cb.buf); return; } if (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 && /* sha1 is a commit? match without further lookup */ (!hashcmp(cb.nsha1, sha1) || /* perhaps sha1 is a tag, try to dereference to a commit */ ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL && !hashcmp(cb.nsha1, commit->object.sha1)))) { int ofs; if (starts_with(ref, "refs/tags/")) ofs = strlen("refs/tags/"); else if (starts_with(ref, "refs/remotes/")) ofs = strlen("refs/remotes/"); else ofs = 0; state->detached_from = xstrdup(ref + ofs); } else state->detached_from = xstrdup(find_unique_abbrev(cb.nsha1, DEFAULT_ABBREV)); hashcpy(state->detached_sha1, cb.nsha1); state->detached_at = !get_sha1("HEAD", sha1) && !hashcmp(sha1, state->detached_sha1); free(ref); strbuf_release(&cb.buf); }
int decode_cloanto_rom_do (uae_u8 *mem, int size, int real_size) { int cnt, t, i; for (i = ROM_KEY_NUM - 1; i >= 0; i--) { uae_u8 sha1[SHA1_SIZE]; struct romdata *rd; int keysize = keyring[i].size; uae_u8 *key = keyring[i].key; if (!key) continue; for (t = cnt = 0; cnt < size; cnt++, t = (t + 1) % keysize) { mem[cnt] ^= key[t]; if (real_size == cnt + 1) t = keysize - 1; } if ((mem[2] == 0x4e && mem[3] == 0xf9) || (mem[0] == 0x11 && (mem[1] == 0x11 || mem[1] == 0x14))) { cloanto_rom = 1; return 1; } get_sha1 (mem, size, sha1); rd = checkromdata (sha1, size, -1); if (rd) { if (rd->cloanto) cloanto_rom = 1; return 1; } if (i == 0) break; for (t = cnt = 0; cnt < size; cnt++, t = (t + 1) % keysize) { mem[cnt] ^= key[t]; if (real_size == cnt + 1) t = keysize - 1; } } return 0; }
static int remove_cmd(int argc, const char **argv, const char *prefix) { struct option options[] = { OPT_END() }; const char *object_ref; struct notes_tree *t; unsigned char object[20]; int retval; argc = parse_options(argc, argv, prefix, options, git_notes_remove_usage, 0); if (1 < argc) { error("too many parameters"); usage_with_options(git_notes_remove_usage, options); } object_ref = argc ? argv[0] : "HEAD"; if (get_sha1(object_ref, object)) die("Failed to resolve '%s' as a valid ref.", object_ref); t = init_notes_check("remove"); retval = remove_note(t, object); if (retval) fprintf(stderr, "Object %s has no note\n", sha1_to_hex(object)); else { fprintf(stderr, "Removing note for object %s\n", sha1_to_hex(object)); commit_notes(t, "Notes removed by 'git notes remove'"); } free_notes(t); return retval; }
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; char *match[2]; ret = get_sha1(ref, sha1); if(ret) return ret; reprepare_packed_git(); root = parse_tree_indirect(sha1); if(!root) return -1; ce = xcalloc(1, cache_entry_size(strlen(path))); match[0] = path; match[1] = NULL; ret = read_tree_recursive(root,"",0,0,match,update_some,ce); if(ret) { free(ce); return ret; } memset(&state, 0, sizeof(state)); state.force = 1; state.refresh_cache = 0; ret = write_entry(ce, outputpath, &state, 0); free(ce); return ret; }
static int list(int argc, const char **argv, const char *prefix) { struct notes_tree *t; unsigned char object[20]; const unsigned char *note; int retval = -1; struct option options[] = { OPT_END() }; if (argc) argc = parse_options(argc, argv, prefix, options, git_notes_list_usage, 0); if (1 < argc) { error("too many parameters"); usage_with_options(git_notes_list_usage, options); } t = init_notes_check("list"); if (argc) { if (get_sha1(argv[0], object)) die("Failed to resolve '%s' as a valid ref.", argv[0]); note = get_note(t, object); if (note) { puts(sha1_to_hex(note)); retval = 0; } else retval = error("No note found for object %s.", sha1_to_hex(object)); } else retval = for_each_note(t, 0, list_each_note, NULL); free_notes(t); return retval; }
int cmd_grep(int argc, const char **argv, const char *prefix) { int hit = 0; int cached = 0; int seen_dashdash = 0; int external_grep_allowed__ignored; struct grep_opt opt; struct object_array list = { 0, 0, NULL }; const char **paths = NULL; int i; int dummy; int nongit = 0, use_index = 1; struct option options[] = { OPT_BOOLEAN(0, "cached", &cached, "search in index instead of in the work tree"), OPT_BOOLEAN(0, "index", &use_index, "--no-index finds in contents not managed by git"), OPT_GROUP(""), OPT_BOOLEAN('v', "invert-match", &opt.invert, "show non-matching lines"), OPT_BOOLEAN('i', "ignore-case", &opt.ignore_case, "case insensitive matching"), OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp, "match patterns only at word boundaries"), OPT_SET_INT('a', "text", &opt.binary, "process binary files as text", GREP_BINARY_TEXT), OPT_SET_INT('I', NULL, &opt.binary, "don't match patterns in binary files", GREP_BINARY_NOMATCH), { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth", "descend at most <depth> levels", PARSE_OPT_NONEG, NULL, 1 }, OPT_GROUP(""), OPT_BIT('E', "extended-regexp", &opt.regflags, "use extended POSIX regular expressions", REG_EXTENDED), OPT_NEGBIT('G', "basic-regexp", &opt.regflags, "use basic POSIX regular expressions (default)", REG_EXTENDED), OPT_BOOLEAN('F', "fixed-strings", &opt.fixed, "interpret patterns as fixed strings"), OPT_GROUP(""), OPT_BOOLEAN('n', NULL, &opt.linenum, "show line numbers"), OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1), OPT_BIT('H', NULL, &opt.pathname, "show filenames", 1), OPT_NEGBIT(0, "full-name", &opt.relative, "show filenames relative to top directory", 1), OPT_BOOLEAN('l', "files-with-matches", &opt.name_only, "show only filenames instead of matching lines"), OPT_BOOLEAN(0, "name-only", &opt.name_only, "synonym for --files-with-matches"), OPT_BOOLEAN('L', "files-without-match", &opt.unmatch_name_only, "show only the names of files without match"), OPT_BOOLEAN('z', "null", &opt.null_following_name, "print NUL after filenames"), OPT_BOOLEAN('c', "count", &opt.count, "show the number of matches instead of matching lines"), OPT_SET_INT(0, "color", &opt.color, "highlight matches", 1), OPT_GROUP(""), OPT_CALLBACK('C', NULL, &opt, "n", "show <n> context lines before and after matches", context_callback), OPT_INTEGER('B', NULL, &opt.pre_context, "show <n> context lines before matches"), OPT_INTEGER('A', NULL, &opt.post_context, "show <n> context lines after matches"), OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM", context_callback), OPT_BOOLEAN('p', "show-function", &opt.funcname, "show a line with the function name before matches"), OPT_GROUP(""), OPT_CALLBACK('f', NULL, &opt, "file", "read patterns from file", file_callback), { OPTION_CALLBACK, 'e', NULL, &opt, "pattern", "match <pattern>", PARSE_OPT_NONEG, pattern_callback }, { OPTION_CALLBACK, 0, "and", &opt, NULL, "combine patterns specified with -e", PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback }, OPT_BOOLEAN(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_BOOLEAN('q', "quiet", &opt.status_only, "indicate hit with exit status without output"), OPT_BOOLEAN(0, "all-match", &opt.all_match, "show only matches from files that match all patterns"), OPT_GROUP(""), OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored, "allow calling of grep(1) (ignored by this build)"), { OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage", PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback }, OPT_END() }; prefix = setup_git_directory_gently(&nongit); /* * 'git grep -h', unlike 'git grep -h <pattern>', is a request * to show usage information and exit. */ if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(grep_usage, options); memset(&opt, 0, sizeof(opt)); opt.prefix = prefix; opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0; opt.relative = 1; opt.pathname = 1; opt.pattern_tail = &opt.pattern_list; opt.regflags = REG_NEWLINE; opt.max_depth = -1; strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD); opt.color = -1; git_config(grep_config, &opt); if (opt.color == -1) opt.color = git_use_color_default; /* * 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 | PARSE_OPT_NO_INTERNAL_HELP); if (use_index && nongit) /* 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 (!opt.pattern_list) die("no pattern given."); if (!opt.fixed && opt.ignore_case) opt.regflags |= REG_ICASE; if ((opt.regflags != REG_NEWLINE) && opt.fixed) die("cannot mix --fixed-strings and regexp"); #ifndef NO_PTHREADS if (online_cpus() == 1 || !grep_threads_ok(&opt)) use_threads = 0; if (use_threads) start_threads(&opt); #else use_threads = 0; #endif compile_grep_patterns(&opt); /* Check revs and then paths */ for (i = 0; i < argc; i++) { const char *arg = argv[i]; unsigned char sha1[20]; /* Is it a rev? */ if (!get_sha1(arg, sha1)) { struct object *object = parse_object(sha1); if (!object) die("bad object %s", arg); add_object_array(object, arg, &list); continue; } if (!strcmp(arg, "--")) { i++; seen_dashdash = 1; } break; } /* The rest are paths */ if (!seen_dashdash) { int j; for (j = i; j < argc; j++) verify_filename(prefix, argv[j]); } if (i < argc) paths = get_pathspec(prefix, argv + i); else if (prefix) { paths = xcalloc(2, sizeof(const char *)); paths[0] = prefix; paths[1] = NULL; } if (!use_index) { int hit; if (cached) die("--cached cannot be used with --no-index."); if (list.nr) die("--no-index cannot be used with revs."); hit = grep_directory(&opt, paths); if (use_threads) hit |= wait_all(); return !hit; } if (!list.nr) { int hit; if (!cached) setup_work_tree(); hit = grep_cache(&opt, paths, cached); if (use_threads) hit |= wait_all(); return !hit; } if (cached) die("both --cached and trees are given."); for (i = 0; i < list.nr; i++) { struct object *real_obj; real_obj = deref_tag(list.objects[i].item, NULL, 0); if (grep_object(&opt, paths, real_obj, list.objects[i].name)) { hit = 1; if (opt.status_only) break; } } if (use_threads) hit |= wait_all(); 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, unborn; const char *rev; unsigned char sha1[20]; const char **pathspec = NULL; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_SET_INT(0, "mixed", &reset_type, N_("reset HEAD and index"), MIXED), OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT), OPT_SET_INT(0, "hard", &reset_type, N_("reset HEAD, index and working tree"), HARD), OPT_SET_INT(0, "merge", &reset_type, N_("reset HEAD, index and working tree"), MERGE), OPT_SET_INT(0, "keep", &reset_type, N_("reset HEAD but keep local changes"), KEEP), OPT_BOOL('p', "patch", &patch_mode, N_("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); pathspec = parse_args(argv, prefix, &rev); unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", sha1); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ hashcpy(sha1, EMPTY_TREE_SHA1_BIN); } else if (!pathspec) { struct commit *commit; if (get_sha1_committish(rev, sha1)) die(_("Failed to resolve '%s' as a valid revision."), rev); commit = lookup_commit_reference(sha1); if (!commit) die(_("Could not parse object '%s'."), rev); hashcpy(sha1, commit->object.sha1); } else { struct tree *tree; if (get_sha1_treeish(rev, sha1)) die(_("Failed to resolve '%s' as a valid tree."), rev); tree = parse_tree_indirect(sha1); if (!tree) die(_("Could not parse object '%s'."), rev); hashcpy(sha1, tree->object.sha1); } if (patch_mode) { if (reset_type != NONE) die(_("--patch is incompatible with --{hard,mixed,soft}")); return run_add_interactive(sha1_to_hex(sha1), "--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) { 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) 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])); /* 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 = xcalloc(1, sizeof(struct lock_file)); int newfd = hold_locked_index(lock, 1); if (reset_type == MIXED) { if (read_from_tree(pathspec, sha1)) return 1; } else { int err = reset_index(sha1, reset_type, quiet); if (reset_type == KEEP && !err) err = reset_index(sha1, MIXED, quiet); if (err) die(_("Could not reset index file to revision '%s'."), rev); } if (reset_type == MIXED) { /* Report what has not been updated. */ int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN; refresh_index(&the_index, flags, NULL, NULL, _("Unstaged changes after reset:")); } if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(lock)) die(_("Could not write new index file.")); } if (!pathspec && !unborn) { /* Any resets without paths update HEAD to the head being * switched to, saving the previous head in ORIG_HEAD before. */ update_ref_status = update_refs(rev, sha1); if (reset_type == HARD && !update_ref_status && !quiet) print_new_head_line(lookup_commit_reference(sha1)); } if (!pathspec) remove_branch_state(); return update_ref_status; }
static int do_pick_commit(struct commit *commit, struct replay_opts *opts) { unsigned char head[20]; struct commit *base, *next, *parent; const char *base_label, *next_label; struct commit_message msg = { NULL, NULL, NULL, NULL }; struct strbuf msgbuf = STRBUF_INIT; int res, unborn = 0, allow; if (opts->no_commit) { /* * We do not intend to commit immediately. We just want to * merge the differences in, so let's compute the tree * that represents the "current" state for merge-recursive * to work on. */ if (write_cache_as_tree(head, 0, NULL)) die (_("Your index file is unmerged.")); } else { unborn = get_sha1("HEAD", head); if (unborn) hashcpy(head, EMPTY_TREE_SHA1_BIN); if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0)) return error_dirty_index(opts); } discard_cache(); if (!commit->parents) { parent = NULL; } else if (commit->parents->next) { /* Reverting or cherry-picking a merge commit */ int cnt; struct commit_list *p; if (!opts->mainline) return error(_("Commit %s is a merge but no -m option was given."), oid_to_hex(&commit->object.oid)); for (cnt = 1, p = commit->parents; cnt != opts->mainline && p; cnt++) p = p->next; if (cnt != opts->mainline || !p) return error(_("Commit %s does not have parent %d"), oid_to_hex(&commit->object.oid), opts->mainline); parent = p->item; } else if (0 < opts->mainline) return error(_("Mainline was specified but commit %s is not a merge."), oid_to_hex(&commit->object.oid)); else parent = commit->parents->item; if (opts->allow_ff && ((parent && !hashcmp(parent->object.oid.hash, head)) || (!parent && unborn))) return fast_forward_to(commit->object.oid.hash, head, unborn, opts); if (parent && parse_commit(parent) < 0) /* TRANSLATORS: The first %s will be "revert" or "cherry-pick", the second %s a SHA1 */ return error(_("%s: cannot parse parent commit %s"), action_name(opts), oid_to_hex(&parent->object.oid)); if (get_message(commit, &msg) != 0) return error(_("Cannot get commit message for %s"), oid_to_hex(&commit->object.oid)); /* * "commit" is an existing commit. We would want to apply * the difference it introduces since its first parent "prev" * on top of the current HEAD if we are cherry-pick. Or the * reverse of it if we are revert. */ if (opts->action == REPLAY_REVERT) { base = commit; base_label = msg.label; next = parent; next_label = msg.parent_label; strbuf_addstr(&msgbuf, "Revert \""); strbuf_addstr(&msgbuf, msg.subject); strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit "); strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid)); if (commit->parents && commit->parents->next) { strbuf_addstr(&msgbuf, ", reversing\nchanges made to "); strbuf_addstr(&msgbuf, oid_to_hex(&parent->object.oid)); } strbuf_addstr(&msgbuf, ".\n"); } else { const char *p; base = parent; base_label = msg.parent_label; next = commit; next_label = msg.label; /* * Append the commit log message to msgbuf; it starts * after the tree, parent, author, committer * information followed by "\n\n". */ p = strstr(msg.message, "\n\n"); if (p) strbuf_addstr(&msgbuf, skip_blank_lines(p + 2)); if (opts->record_origin) { if (!has_conforming_footer(&msgbuf, NULL, 0)) strbuf_addch(&msgbuf, '\n'); strbuf_addstr(&msgbuf, cherry_picked_prefix); strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid)); strbuf_addstr(&msgbuf, ")\n"); } } if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) { res = do_recursive_merge(base, next, base_label, next_label, head, &msgbuf, opts); write_message(&msgbuf, git_path_merge_msg()); } else { struct commit_list *common = NULL; struct commit_list *remotes = NULL; write_message(&msgbuf, git_path_merge_msg()); commit_list_insert(base, &common); commit_list_insert(next, &remotes); res = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts, common, sha1_to_hex(head), remotes); free_commit_list(common); free_commit_list(remotes); } /* * If the merge was clean or if it failed due to conflict, we write * CHERRY_PICK_HEAD for the subsequent invocation of commit to use. * However, if the merge did not even start, then we don't want to * write it at all. */ if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1)) update_ref(NULL, "CHERRY_PICK_HEAD", commit->object.oid.hash, NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR); if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1)) update_ref(NULL, "REVERT_HEAD", commit->object.oid.hash, NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR); if (res) { error(opts->action == REPLAY_REVERT ? _("could not revert %s... %s") : _("could not apply %s... %s"), find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), msg.subject); print_advice(res == 1, opts); rerere(opts->allow_rerere_auto); goto leave; } allow = allow_empty(opts, commit); if (allow < 0) { res = allow; goto leave; } if (!opts->no_commit) res = run_git_commit(git_path_merge_msg(), opts, allow); leave: free_message(commit, &msg); return res; }
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_commit(int argc, const char **argv, const char *prefix) { static struct wt_status s; static struct option builtin_commit_options[] = { OPT__QUIET(&quiet, N_("suppress summary after successful commit")), OPT__VERBOSE(&verbose, N_("show diff in commit message template")), OPT_GROUP(N_("Commit message options")), OPT_FILENAME('F', "file", &logfile, N_("read message from file")), OPT_STRING(0, "author", &force_author, N_("author"), N_("override author for commit")), OPT_STRING(0, "date", &force_date, N_("date"), N_("override date for commit")), OPT_CALLBACK('m', "message", &message, N_("message"), N_("commit message"), opt_parse_m), OPT_STRING('c', "reedit-message", &edit_message, N_("commit"), N_("reuse and edit message from specified commit")), OPT_STRING('C', "reuse-message", &use_message, N_("commit"), N_("reuse message from specified commit")), OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup specified commit")), OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")), OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")), OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")), OPT_FILENAME('t', "template", &template_file, N_("use specified template file")), OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")), OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")), OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, /* end commit message options */ OPT_GROUP(N_("Commit contents options")), OPT_BOOL('a', "all", &all, N_("commit all changed files")), OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")), OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")), OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")), OPT_BOOL('o', "only", &only, N_("commit only specified files")), OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit hook")), OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")), OPT_SET_INT(0, "short", &status_format, N_("show status concisely"), STATUS_FORMAT_SHORT), OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")), OPT_SET_INT(0, "porcelain", &status_format, N_("machine-readable output"), STATUS_FORMAT_PORCELAIN), OPT_SET_INT(0, "long", &status_format, N_("show status in long format (default)"), STATUS_FORMAT_LONG), OPT_BOOL('z', "null", &s.null_termination, N_("terminate entries with NUL")), OPT_BOOL(0, "amend", &amend, N_("amend previous commit")), OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, /* end commit contents options */ OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty, N_("ok to record an empty change")), OPT_HIDDEN_BOOL(0, "allow-empty-message", &allow_empty_message, N_("ok to record a change with an empty message")), OPT_END() }; struct strbuf sb = STRBUF_INIT; struct strbuf author_ident = STRBUF_INIT; const char *index_file, *reflog_msg; char *nl; unsigned char sha1[20]; struct ref_lock *ref_lock; struct commit_list *parents = NULL, **pptr = &parents; struct stat statbuf; struct commit *current_head = NULL; struct commit_extra_header *extra = NULL; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_commit_usage, builtin_commit_options); status_init_config(&s, git_commit_config); status_format = STATUS_FORMAT_NONE; /* Ignore status.short */ s.colopts = 0; if (get_sha1("HEAD", sha1)) current_head = NULL; else { current_head = lookup_commit_or_die(sha1, "HEAD"); if (parse_commit(current_head)) die(_("could not parse HEAD commit")); } argc = parse_and_validate_options(argc, argv, builtin_commit_options, builtin_commit_usage, prefix, current_head, &s); if (dry_run) return dry_run_commit(argc, argv, prefix, current_head, &s); index_file = prepare_index(argc, argv, prefix, current_head, 0); /* Set up everything for writing the commit object. This includes running hooks, writing the trees, and interacting with the user. */ if (!prepare_to_commit(index_file, prefix, current_head, &s, &author_ident)) { rollback_index_files(); return 1; } /* Determine parents */ reflog_msg = getenv("GIT_REFLOG_ACTION"); if (!current_head) { if (!reflog_msg) reflog_msg = "commit (initial)"; } else if (amend) { struct commit_list *c; if (!reflog_msg) reflog_msg = "commit (amend)"; for (c = current_head->parents; c; c = c->next) pptr = &commit_list_insert(c->item, pptr)->next; } else if (whence == FROM_MERGE) { struct strbuf m = STRBUF_INIT; FILE *fp; int allow_fast_forward = 1; if (!reflog_msg) reflog_msg = "commit (merge)"; pptr = &commit_list_insert(current_head, pptr)->next; fp = fopen(git_path("MERGE_HEAD"), "r"); if (fp == NULL) die_errno(_("could not open '%s' for reading"), git_path("MERGE_HEAD")); while (strbuf_getline(&m, fp, '\n') != EOF) { struct commit *parent; parent = get_merge_parent(m.buf); if (!parent) die(_("Corrupt MERGE_HEAD file (%s)"), m.buf); pptr = &commit_list_insert(parent, pptr)->next; } fclose(fp); strbuf_release(&m); if (!stat(git_path("MERGE_MODE"), &statbuf)) { if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0) die_errno(_("could not read MERGE_MODE")); if (!strcmp(sb.buf, "no-ff")) allow_fast_forward = 0; } if (allow_fast_forward) parents = reduce_heads(parents); } else { if (!reflog_msg) reflog_msg = (whence == FROM_CHERRY_PICK) ? "commit (cherry-pick)" : "commit"; pptr = &commit_list_insert(current_head, pptr)->next; } /* Finally, get the commit message */ strbuf_reset(&sb); if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) { int saved_errno = errno; rollback_index_files(); die(_("could not read commit message: %s"), strerror(saved_errno)); } /* Truncate the message just before the diff, if any. */ if (verbose) wt_status_truncate_message_at_cut_line(&sb); if (cleanup_mode != CLEANUP_NONE) stripspace(&sb, cleanup_mode == CLEANUP_ALL); if (template_untouched(&sb) && !allow_empty_message) { rollback_index_files(); fprintf(stderr, _("Aborting commit; you did not edit the message.\n")); exit(1); } if (message_is_empty(&sb) && !allow_empty_message) { rollback_index_files(); fprintf(stderr, _("Aborting commit due to empty commit message.\n")); exit(1); } if (amend) { const char *exclude_gpgsig[2] = { "gpgsig", NULL }; extra = read_commit_extra_headers(current_head, exclude_gpgsig); } else { struct commit_extra_header **tail = &extra; append_merge_tag_headers(parents, &tail); } if (commit_tree_extended(&sb, active_cache_tree->sha1, parents, sha1, author_ident.buf, sign_commit, extra)) { rollback_index_files(); die(_("failed to write commit object")); } strbuf_release(&author_ident); free_commit_extra_headers(extra); ref_lock = lock_any_ref_for_update("HEAD", !current_head ? NULL : current_head->object.sha1, 0, NULL); nl = strchr(sb.buf, '\n'); if (nl) strbuf_setlen(&sb, nl + 1 - sb.buf); else strbuf_addch(&sb, '\n'); strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg)); strbuf_insert(&sb, strlen(reflog_msg), ": ", 2); if (!ref_lock) { rollback_index_files(); die(_("cannot lock HEAD ref")); } if (write_ref_sha1(ref_lock, sha1, sb.buf) < 0) { rollback_index_files(); die(_("cannot update HEAD ref")); } unlink(git_path("CHERRY_PICK_HEAD")); unlink(git_path("REVERT_HEAD")); unlink(git_path("MERGE_HEAD")); unlink(git_path("MERGE_MSG")); unlink(git_path("MERGE_MODE")); unlink(git_path("SQUASH_MSG")); if (commit_index_files()) die (_("Repository has been updated, but unable to write\n" "new_index file. Check that disk is not full or quota is\n" "not exceeded, and then \"git reset HEAD\" to recover.")); rerere(0); run_hook(get_index_file(), "post-commit", NULL); if (amend && !no_post_rewrite) { struct notes_rewrite_cfg *cfg; cfg = init_copy_notes_for_rewrite("amend"); if (cfg) { /* we are amending, so current_head is not NULL */ copy_note_for_rewrite(cfg, current_head->object.sha1, sha1); finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'"); } run_rewrite_hook(current_head->object.sha1, sha1); } if (!quiet) print_summary(prefix, sha1, !current_head); return 0; }
static int prepare_to_commit(const char *index_file, const char *prefix, struct commit *current_head, struct wt_status *s, struct strbuf *author_ident) { struct stat statbuf; struct strbuf committer_ident = STRBUF_INIT; int commitable; struct strbuf sb = STRBUF_INIT; const char *hook_arg1 = NULL; const char *hook_arg2 = NULL; int clean_message_contents = (cleanup_mode != CLEANUP_NONE); int old_display_comment_prefix; /* This checks and barfs if author is badly specified */ determine_author_info(author_ident); if (!no_verify && run_hook(index_file, "pre-commit", NULL)) return 0; if (squash_message) { /* * Insert the proper subject line before other commit * message options add their content. */ if (use_message && !strcmp(use_message, squash_message)) strbuf_addstr(&sb, "squash! "); else { struct pretty_print_context ctx = {0}; struct commit *c; c = lookup_commit_reference_by_name(squash_message); if (!c) die(_("could not lookup commit %s"), squash_message); ctx.output_encoding = get_commit_output_encoding(); format_commit_message(c, "squash! %s\n\n", &sb, &ctx); } } if (message.len) { strbuf_addbuf(&sb, &message); hook_arg1 = "message"; } else if (logfile && !strcmp(logfile, "-")) { if (isatty(0)) fprintf(stderr, _("(reading log message from standard input)\n")); if (strbuf_read(&sb, 0, 0) < 0) die_errno(_("could not read log from standard input")); hook_arg1 = "message"; } else if (logfile) { if (strbuf_read_file(&sb, logfile, 0) < 0) die_errno(_("could not read log file '%s'"), logfile); hook_arg1 = "message"; } else if (use_message) { char *buffer; buffer = strstr(use_message_buffer, "\n\n"); if (!use_editor && (!buffer || buffer[2] == '\0')) die(_("commit has empty message")); strbuf_add(&sb, buffer + 2, strlen(buffer + 2)); hook_arg1 = "commit"; hook_arg2 = use_message; } else if (fixup_message) { struct pretty_print_context ctx = {0}; struct commit *commit; commit = lookup_commit_reference_by_name(fixup_message); if (!commit) die(_("could not lookup commit %s"), fixup_message); ctx.output_encoding = get_commit_output_encoding(); format_commit_message(commit, "fixup! %s\n\n", &sb, &ctx); hook_arg1 = "message"; } else if (!stat(git_path("MERGE_MSG"), &statbuf)) { if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0) die_errno(_("could not read MERGE_MSG")); hook_arg1 = "merge"; } else if (!stat(git_path("SQUASH_MSG"), &statbuf)) { if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0) die_errno(_("could not read SQUASH_MSG")); hook_arg1 = "squash"; } else if (template_file) { if (strbuf_read_file(&sb, template_file, 0) < 0) die_errno(_("could not read '%s'"), template_file); hook_arg1 = "template"; clean_message_contents = 0; } /* * The remaining cases don't modify the template message, but * just set the argument(s) to the prepare-commit-msg hook. */ else if (whence == FROM_MERGE) hook_arg1 = "merge"; else if (whence == FROM_CHERRY_PICK) { hook_arg1 = "commit"; hook_arg2 = "CHERRY_PICK_HEAD"; } if (squash_message) { /* * If squash_commit was used for the commit subject, * then we're possibly hijacking other commit log options. * Reset the hook args to tell the real story. */ hook_arg1 = "message"; hook_arg2 = ""; } s->fp = fopen(git_path(commit_editmsg), "w"); if (s->fp == NULL) die_errno(_("could not open '%s'"), git_path(commit_editmsg)); /* Ignore status.displayCommentPrefix: we do need comments in COMMIT_EDITMSG. */ old_display_comment_prefix = s->display_comment_prefix; s->display_comment_prefix = 1; /* * Most hints are counter-productive when the commit has * already started. */ s->hints = 0; if (clean_message_contents) stripspace(&sb, 0); if (signoff) { /* * See if we have a Conflicts: block at the end. If yes, count * its size, so we can ignore it. */ int ignore_footer = 0; int i, eol, previous = 0; const char *nl; for (i = 0; i < sb.len; i++) { nl = memchr(sb.buf + i, '\n', sb.len - i); if (nl) eol = nl - sb.buf; else eol = sb.len; if (starts_with(sb.buf + previous, "\nConflicts:\n")) { ignore_footer = sb.len - previous; break; } while (i < eol) i++; previous = eol; } append_signoff(&sb, ignore_footer, 0); } if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len) die_errno(_("could not write commit template")); strbuf_release(&sb); /* This checks if committer ident is explicitly given */ strbuf_addstr(&committer_ident, git_committer_info(IDENT_STRICT)); if (use_editor && include_status) { int ident_shown = 0; int saved_color_setting; char *ai_tmp, *ci_tmp; if (whence != FROM_COMMIT) status_printf_ln(s, GIT_COLOR_NORMAL, whence == FROM_MERGE ? _("\n" "It looks like you may be committing a merge.\n" "If this is not correct, please remove the file\n" " %s\n" "and try again.\n") : _("\n" "It looks like you may be committing a cherry-pick.\n" "If this is not correct, please remove the file\n" " %s\n" "and try again.\n"), git_path(whence == FROM_MERGE ? "MERGE_HEAD" : "CHERRY_PICK_HEAD")); fprintf(s->fp, "\n"); if (cleanup_mode == CLEANUP_ALL) status_printf(s, GIT_COLOR_NORMAL, _("Please enter the commit message for your changes." " Lines starting\nwith '%c' will be ignored, and an empty" " message aborts the commit.\n"), comment_line_char); else /* CLEANUP_SPACE, that is. */ status_printf(s, GIT_COLOR_NORMAL, _("Please enter the commit message for your changes." " Lines starting\n" "with '%c' will be kept; you may remove them" " yourself if you want to.\n" "An empty message aborts the commit.\n"), comment_line_char); if (only_include_assumed) status_printf_ln(s, GIT_COLOR_NORMAL, "%s", only_include_assumed); ai_tmp = cut_ident_timestamp_part(author_ident->buf); ci_tmp = cut_ident_timestamp_part(committer_ident.buf); if (strcmp(author_ident->buf, committer_ident.buf)) status_printf_ln(s, GIT_COLOR_NORMAL, _("%s" "Author: %s"), ident_shown++ ? "" : "\n", author_ident->buf); if (!committer_ident_sufficiently_given()) status_printf_ln(s, GIT_COLOR_NORMAL, _("%s" "Committer: %s"), ident_shown++ ? "" : "\n", committer_ident.buf); if (ident_shown) status_printf_ln(s, GIT_COLOR_NORMAL, ""); saved_color_setting = s->use_color; s->use_color = 0; commitable = run_status(s->fp, index_file, prefix, 1, s); s->use_color = saved_color_setting; *ai_tmp = ' '; *ci_tmp = ' '; } else { unsigned char sha1[20]; const char *parent = "HEAD"; if (!active_nr && read_cache() < 0) die(_("Cannot read index")); if (amend) parent = "HEAD^1"; if (get_sha1(parent, sha1)) commitable = !!active_nr; else commitable = index_differs_from(parent, 0); } strbuf_release(&committer_ident); fclose(s->fp); /* * Reject an attempt to record a non-merge empty commit without * explicit --allow-empty. In the cherry-pick case, it may be * empty due to conflict resolution, which the user should okay. */ if (!commitable && whence != FROM_MERGE && !allow_empty && !(amend && is_a_merge(current_head))) { s->display_comment_prefix = old_display_comment_prefix; run_status(stdout, index_file, prefix, 0, s); if (amend) fputs(_(empty_amend_advice), stderr); else if (whence == FROM_CHERRY_PICK) { fputs(_(empty_cherry_pick_advice), stderr); if (!sequencer_in_use) fputs(_(empty_cherry_pick_advice_single), stderr); else fputs(_(empty_cherry_pick_advice_multi), stderr); } return 0; } /* * Re-read the index as pre-commit hook could have updated it, * and write it out as a tree. We must do this before we invoke * the editor and after we invoke run_status above. */ discard_cache(); read_cache_from(index_file); if (update_main_cache_tree(0)) { error(_("Error building trees")); return 0; } if (run_hook(index_file, "prepare-commit-msg", git_path(commit_editmsg), hook_arg1, hook_arg2, NULL)) return 0; if (use_editor) { char index[PATH_MAX]; const char *env[2] = { NULL }; env[0] = index; snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); if (launch_editor(git_path(commit_editmsg), NULL, env)) { fprintf(stderr, _("Please supply the message using either -m or -F option.\n")); exit(1); } } if (!no_verify && run_hook(index_file, "commit-msg", git_path(commit_editmsg), NULL)) { return 0; } return 1; }
static int append_edit(int argc, const char **argv, const char *prefix) { const char *object_ref; struct notes_tree *t; unsigned char object[20], new_note[20]; const unsigned char *note; char logmsg[100]; const char * const *usage; struct msg_arg msg = { 0, 0, STRBUF_INIT }; struct option options[] = { { OPTION_CALLBACK, 'm', "message", &msg, "MSG", "note contents as a string", PARSE_OPT_NONEG, parse_msg_arg}, { OPTION_CALLBACK, 'F', "file", &msg, "FILE", "note contents in a file", PARSE_OPT_NONEG, parse_file_arg}, { OPTION_CALLBACK, 'c', "reedit-message", &msg, "OBJECT", "reuse and edit specified note object", PARSE_OPT_NONEG, parse_reedit_arg}, { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT", "reuse specified note object", PARSE_OPT_NONEG, parse_reuse_arg}, OPT_END() }; int edit = !strcmp(argv[0], "edit"); usage = edit ? git_notes_edit_usage : git_notes_append_usage; argc = parse_options(argc, argv, prefix, options, usage, PARSE_OPT_KEEP_ARGV0); if (2 < argc) { error("too many parameters"); usage_with_options(usage, options); } if (msg.given && edit) fprintf(stderr, "The -m/-F/-c/-C options have been deprecated " "for the 'edit' subcommand.\n" "Please use 'git notes add -f -m/-F/-c/-C' instead.\n"); object_ref = 1 < argc ? argv[1] : "HEAD"; if (get_sha1(object_ref, object)) die("Failed to resolve '%s' as a valid ref.", object_ref); t = init_notes_check(argv[0]); note = get_note(t, object); create_note(object, &msg, !edit, note, new_note); if (is_null_sha1(new_note)) remove_note(t, object); else add_note(t, object, new_note, combine_notes_overwrite); snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'", is_null_sha1(new_note) ? "removed" : "added", argv[0]); commit_notes(t, logmsg); free_notes(t); strbuf_release(&(msg.buf)); return 0; }
static int copy(int argc, const char **argv, const char *prefix) { int retval = 0, force = 0, from_stdin = 0; const unsigned char *from_note, *note; const char *object_ref; unsigned char object[20], from_obj[20]; struct notes_tree *t; const char *rewrite_cmd = NULL; struct option options[] = { OPT_BOOLEAN('f', "force", &force, "replace existing notes"), OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"), OPT_STRING(0, "for-rewrite", &rewrite_cmd, "command", "load rewriting config for <command> (implies " "--stdin)"), OPT_END() }; argc = parse_options(argc, argv, prefix, options, git_notes_copy_usage, 0); if (from_stdin || rewrite_cmd) { if (argc) { error("too many parameters"); usage_with_options(git_notes_copy_usage, options); } else { return notes_copy_from_stdin(force, rewrite_cmd); } } if (argc < 2) { error("too few parameters"); usage_with_options(git_notes_copy_usage, options); } if (2 < argc) { error("too many parameters"); usage_with_options(git_notes_copy_usage, options); } if (get_sha1(argv[0], from_obj)) die("Failed to resolve '%s' as a valid ref.", argv[0]); object_ref = 1 < argc ? argv[1] : "HEAD"; if (get_sha1(object_ref, object)) die("Failed to resolve '%s' as a valid ref.", object_ref); t = init_notes_check("copy"); note = get_note(t, object); if (note) { if (!force) { retval = error("Cannot copy notes. Found existing " "notes for object %s. Use '-f' to " "overwrite existing notes", sha1_to_hex(object)); goto out; } fprintf(stderr, "Overwriting existing notes for object %s\n", sha1_to_hex(object)); } from_note = get_note(t, from_obj); if (!from_note) { retval = error("Missing notes on source object %s. Cannot " "copy.", sha1_to_hex(from_obj)); goto out; } add_note(t, object, from_note, combine_notes_overwrite); commit_notes(t, "Notes added by 'git notes copy'"); out: free_notes(t); return retval; }
static int add(int argc, const char **argv, const char *prefix) { int retval = 0, force = 0; const char *object_ref; struct notes_tree *t; unsigned char object[20], new_note[20]; char logmsg[100]; const unsigned char *note; struct msg_arg msg = { 0, 0, STRBUF_INIT }; struct option options[] = { { OPTION_CALLBACK, 'm', "message", &msg, "MSG", "note contents as a string", PARSE_OPT_NONEG, parse_msg_arg}, { OPTION_CALLBACK, 'F', "file", &msg, "FILE", "note contents in a file", PARSE_OPT_NONEG, parse_file_arg}, { OPTION_CALLBACK, 'c', "reedit-message", &msg, "OBJECT", "reuse and edit specified note object", PARSE_OPT_NONEG, parse_reedit_arg}, { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT", "reuse specified note object", PARSE_OPT_NONEG, parse_reuse_arg}, OPT_BOOLEAN('f', "force", &force, "replace existing notes"), OPT_END() }; argc = parse_options(argc, argv, prefix, options, git_notes_add_usage, 0); if (1 < argc) { error("too many parameters"); usage_with_options(git_notes_add_usage, options); } object_ref = argc ? argv[0] : "HEAD"; if (get_sha1(object_ref, object)) die("Failed to resolve '%s' as a valid ref.", object_ref); t = init_notes_check("add"); note = get_note(t, object); if (note) { if (!force) { retval = error("Cannot add notes. Found existing notes " "for object %s. Use '-f' to overwrite " "existing notes", sha1_to_hex(object)); goto out; } fprintf(stderr, "Overwriting existing notes for object %s\n", sha1_to_hex(object)); } create_note(object, &msg, 0, note, new_note); if (is_null_sha1(new_note)) remove_note(t, object); else add_note(t, object, new_note, combine_notes_overwrite); snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'", is_null_sha1(new_note) ? "removed" : "added", "add"); commit_notes(t, logmsg); out: free_notes(t); strbuf_release(&(msg.buf)); return retval; }
int cmd_show_branch(int ac, const char **av, const char *prefix) { struct commit *rev[MAX_REVS], *commit; char *reflog_msg[MAX_REVS]; struct commit_list *list = NULL, *seen = NULL; unsigned int rev_mask[MAX_REVS]; int num_rev, i, extra = 0; int all_heads = 0, all_remotes = 0; int all_mask, all_revs; int lifo = 1; char head[128]; const char *head_p; int head_len; unsigned char head_sha1[20]; int merge_base = 0; int independent = 0; int no_name = 0; int sha1_name = 0; int shown_merge_point = 0; int with_current_branch = 0; int head_at = -1; int topics = 0; int dense = 1; const char *reflog_base = NULL; struct option builtin_show_branch_options[] = { OPT_BOOLEAN('a', "all", &all_heads, "show remote-tracking and local branches"), OPT_BOOLEAN('r', "remotes", &all_remotes, "show remote-tracking branches"), OPT__COLOR(&showbranch_use_color, "color '*!+-' corresponding to the branch"), { OPTION_INTEGER, 0, "more", &extra, "n", "show <n> more commits after the common ancestor", PARSE_OPT_OPTARG, NULL, (intptr_t)1 }, OPT_SET_INT(0, "list", &extra, "synonym to more=-1", -1), OPT_BOOLEAN(0, "no-name", &no_name, "suppress naming strings"), OPT_BOOLEAN(0, "current", &with_current_branch, "include the current branch"), OPT_BOOLEAN(0, "sha1-name", &sha1_name, "name commits with their object names"), OPT_BOOLEAN(0, "merge-base", &merge_base, "show possible merge bases"), OPT_BOOLEAN(0, "independent", &independent, "show refs unreachable from any other ref"), OPT_BOOLEAN(0, "topo-order", &lifo, "show commits in topological order"), OPT_BOOLEAN(0, "topics", &topics, "show only commits not on the first branch"), OPT_SET_INT(0, "sparse", &dense, "show merges reachable from only one tip", 0), OPT_SET_INT(0, "date-order", &lifo, "show commits where no parent comes before its " "children", 0), { OPTION_CALLBACK, 'g', "reflog", &reflog_base, "<n>[,<base>]", "show <n> most recent ref-log entries starting at " "base", PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parse_reflog_param }, OPT_END() }; git_config(git_show_branch_config, NULL); if (showbranch_use_color == -1) showbranch_use_color = git_use_color_default; /* If nothing is specified, try the default first */ if (ac == 1 && default_num) { ac = default_num; av = default_arg; } ac = parse_options(ac, av, prefix, builtin_show_branch_options, show_branch_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (all_heads) all_remotes = 1; if (extra || reflog) { /* "listing" mode is incompatible with * independent nor merge-base modes. */ if (independent || merge_base) usage_with_options(show_branch_usage, builtin_show_branch_options); if (reflog && ((0 < extra) || all_heads || all_remotes)) /* * Asking for --more in reflog mode does not * make sense. --list is Ok. * * Also --all and --remotes do not make sense either. */ die("--reflog is incompatible with --all, --remotes, " "--independent or --merge-base"); } /* If nothing is specified, show all branches by default */ if (ac + all_heads + all_remotes == 0) all_heads = 1; if (reflog) { unsigned char sha1[20]; char nth_desc[256]; char *ref; int base = 0; if (ac == 0) { static const char *fake_av[2]; const char *refname; refname = resolve_ref("HEAD", sha1, 1, NULL); fake_av[0] = xstrdup(refname); fake_av[1] = NULL; av = fake_av; ac = 1; } if (ac != 1) die("--reflog option needs one branch name"); if (MAX_REVS < reflog) die("Only %d entries can be shown at one time.", MAX_REVS); if (!dwim_ref(*av, strlen(*av), sha1, &ref)) die("No such ref %s", *av); /* Has the base been specified? */ if (reflog_base) { char *ep; base = strtoul(reflog_base, &ep, 10); if (*ep) { /* Ah, that is a date spec... */ unsigned long at; at = approxidate(reflog_base); read_ref_at(ref, at, -1, sha1, NULL, NULL, NULL, &base); } } for (i = 0; i < reflog; i++) { char *logmsg, *m; const char *msg; unsigned long timestamp; int tz; if (read_ref_at(ref, 0, base+i, sha1, &logmsg, ×tamp, &tz, NULL)) { reflog = i; break; } msg = strchr(logmsg, '\t'); if (!msg) msg = "(none)"; else msg++; m = xmalloc(strlen(msg) + 200); sprintf(m, "(%s) %s", show_date(timestamp, tz, 1), msg); reflog_msg[i] = m; free(logmsg); sprintf(nth_desc, "%s@{%d}", *av, base+i); append_ref(nth_desc, sha1, 1); } } else if (all_heads + all_remotes) snarf_refs(all_heads, all_remotes); else { while (0 < ac) { append_one_rev(*av); ac--; av++; } } head_p = resolve_ref("HEAD", head_sha1, 1, NULL); if (head_p) { head_len = strlen(head_p); memcpy(head, head_p, head_len + 1); } else { head_len = 0; head[0] = 0; } if (with_current_branch && head_p) { int has_head = 0; for (i = 0; !has_head && i < ref_name_cnt; i++) { /* We are only interested in adding the branch * HEAD points at. */ if (rev_is_head(head, head_len, ref_name[i], head_sha1, NULL)) has_head++; } if (!has_head) { int offset = !prefixcmp(head, "refs/heads/") ? 11 : 0; append_one_rev(head + offset); } } if (!ref_name_cnt) { fprintf(stderr, "No revs to be shown.\n"); exit(0); } for (num_rev = 0; ref_name[num_rev]; num_rev++) { unsigned char revkey[20]; unsigned int flag = 1u << (num_rev + REV_SHIFT); if (MAX_REVS <= num_rev) die("cannot handle more than %d revs.", MAX_REVS); if (get_sha1(ref_name[num_rev], revkey)) die("'%s' is not a valid ref.", ref_name[num_rev]); commit = lookup_commit_reference(revkey); if (!commit) die("cannot find commit %s (%s)", ref_name[num_rev], revkey); parse_commit(commit); mark_seen(commit, &seen); /* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1, * and so on. REV_SHIFT bits from bit 0 are used for * internal bookkeeping. */ commit->object.flags |= flag; if (commit->object.flags == flag) commit_list_insert_by_date(commit, &list); rev[num_rev] = commit; } for (i = 0; i < num_rev; i++) rev_mask[i] = rev[i]->object.flags; if (0 <= extra) join_revs(&list, &seen, num_rev, extra); commit_list_sort_by_date(&seen); if (merge_base) return show_merge_base(seen, num_rev); if (independent) return show_independent(rev, num_rev, ref_name, rev_mask); /* Show list; --more=-1 means list-only */ if (1 < num_rev || extra < 0) { for (i = 0; i < num_rev; i++) { int j; int is_head = rev_is_head(head, head_len, ref_name[i], head_sha1, rev[i]->object.sha1); if (extra < 0) printf("%c [%s] ", is_head ? '*' : ' ', ref_name[i]); else { for (j = 0; j < i; j++) putchar(' '); printf("%s%c%s [%s] ", get_color_code(i % COLUMN_COLORS_MAX), is_head ? '*' : '!', get_color_reset_code(), ref_name[i]); } if (!reflog) { /* header lines never need name */ show_one_commit(rev[i], 1); } else puts(reflog_msg[i]); if (is_head) head_at = i; } if (0 <= extra) { for (i = 0; i < num_rev; i++) putchar('-'); putchar('\n'); } } if (extra < 0) exit(0); /* Sort topologically */ sort_in_topological_order(&seen, lifo); /* Give names to commits */ if (!sha1_name && !no_name) name_commits(seen, rev, ref_name, num_rev); all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); all_revs = all_mask & ~((1u << REV_SHIFT) - 1); while (seen) { struct commit *commit = pop_one_commit(&seen); int this_flag = commit->object.flags; int is_merge_point = ((this_flag & all_revs) == all_revs); shown_merge_point |= is_merge_point; if (1 < num_rev) { int is_merge = !!(commit->parents && commit->parents->next); if (topics && !is_merge_point && (this_flag & (1u << REV_SHIFT))) continue; if (dense && is_merge && omit_in_dense(commit, rev, num_rev)) continue; for (i = 0; i < num_rev; i++) { int mark; if (!(this_flag & (1u << (i + REV_SHIFT)))) mark = ' '; else if (is_merge) mark = '-'; else if (i == head_at) mark = '*'; else mark = '+'; printf("%s%c%s", get_color_code(i % COLUMN_COLORS_MAX), mark, get_color_reset_code()); } putchar(' '); } show_one_commit(commit, no_name); if (shown_merge_point && --extra < 0) break; } return 0; }
int notes_merge(struct notes_merge_options *o, struct notes_tree *local_tree, unsigned char *result_sha1) { unsigned char local_sha1[20], remote_sha1[20]; struct commit *local, *remote; struct commit_list *bases = NULL; const unsigned char *base_sha1, *base_tree_sha1; int result = 0; assert(o->local_ref && o->remote_ref); assert(!strcmp(o->local_ref, local_tree->ref)); hashclr(result_sha1); trace_printf("notes_merge(o->local_ref = %s, o->remote_ref = %s)\n", o->local_ref, o->remote_ref); /* Dereference o->local_ref into local_sha1 */ if (read_ref_full(o->local_ref, local_sha1, 0, NULL)) die("Failed to resolve local notes ref '%s'", o->local_ref); else if (!check_refname_format(o->local_ref, 0) && is_null_sha1(local_sha1)) local = NULL; /* local_sha1 == null_sha1 indicates unborn ref */ else if (!(local = lookup_commit_reference(local_sha1))) die("Could not parse local commit %s (%s)", sha1_to_hex(local_sha1), o->local_ref); trace_printf("\tlocal commit: %.7s\n", sha1_to_hex(local_sha1)); /* Dereference o->remote_ref into remote_sha1 */ if (get_sha1(o->remote_ref, remote_sha1)) { /* * Failed to get remote_sha1. If o->remote_ref looks like an * unborn ref, perform the merge using an empty notes tree. */ if (!check_refname_format(o->remote_ref, 0)) { hashclr(remote_sha1); remote = NULL; } else { die("Failed to resolve remote notes ref '%s'", o->remote_ref); } } else if (!(remote = lookup_commit_reference(remote_sha1))) { die("Could not parse remote commit %s (%s)", sha1_to_hex(remote_sha1), o->remote_ref); } trace_printf("\tremote commit: %.7s\n", sha1_to_hex(remote_sha1)); if (!local && !remote) die("Cannot merge empty notes ref (%s) into empty notes ref " "(%s)", o->remote_ref, o->local_ref); if (!local) { /* result == remote commit */ hashcpy(result_sha1, remote_sha1); goto found_result; } if (!remote) { /* result == local commit */ hashcpy(result_sha1, local_sha1); goto found_result; } assert(local && remote); /* Find merge bases */ bases = get_merge_bases(local, remote, 1); if (!bases) { base_sha1 = null_sha1; base_tree_sha1 = EMPTY_TREE_SHA1_BIN; if (o->verbosity >= 4) printf("No merge base found; doing history-less merge\n"); } else if (!bases->next) { base_sha1 = bases->item->object.sha1; base_tree_sha1 = bases->item->tree->object.sha1; if (o->verbosity >= 4) printf("One merge base found (%.7s)\n", sha1_to_hex(base_sha1)); } else { /* TODO: How to handle multiple merge-bases? */ base_sha1 = bases->item->object.sha1; base_tree_sha1 = bases->item->tree->object.sha1; if (o->verbosity >= 3) printf("Multiple merge bases found. Using the first " "(%.7s)\n", sha1_to_hex(base_sha1)); } if (o->verbosity >= 4) printf("Merging remote commit %.7s into local commit %.7s with " "merge-base %.7s\n", sha1_to_hex(remote->object.sha1), sha1_to_hex(local->object.sha1), sha1_to_hex(base_sha1)); if (!hashcmp(remote->object.sha1, base_sha1)) { /* Already merged; result == local commit */ if (o->verbosity >= 2) printf("Already up-to-date!\n"); hashcpy(result_sha1, local->object.sha1); goto found_result; } if (!hashcmp(local->object.sha1, base_sha1)) { /* Fast-forward; result == remote commit */ if (o->verbosity >= 2) printf("Fast-forward\n"); hashcpy(result_sha1, remote->object.sha1); goto found_result; } result = merge_from_diffs(o, base_tree_sha1, local->tree->object.sha1, remote->tree->object.sha1, local_tree); if (result != 0) { /* non-trivial merge (with or without conflicts) */ /* Commit (partial) result */ struct commit_list *parents = NULL; commit_list_insert(remote, &parents); /* LIFO order */ commit_list_insert(local, &parents); create_notes_commit(local_tree, parents, o->commit_msg.buf, result_sha1); } found_result: free_commit_list(bases); strbuf_release(&(o->commit_msg)); trace_printf("notes_merge(): result = %i, result_sha1 = %.7s\n", result, sha1_to_hex(result_sha1)); return result; }
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_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); }
void cgit_print_commit(char *hex, const char *prefix) { struct commit *commit, *parent; struct commitinfo *info, *parent_info; struct commit_list *p; struct strbuf notes = STRBUF_INIT; unsigned char sha1[20]; char *tmp, *tmp2; int parents = 0; if (!hex) hex = ctx.qry.head; if (get_sha1(hex, sha1)) { cgit_print_error("Bad object id: %s", hex); return; } commit = lookup_commit_reference(sha1); if (!commit) { cgit_print_error("Bad commit reference: %s", hex); return; } info = cgit_parse_commit(commit); format_display_notes(sha1, ¬es, PAGE_ENCODING, 0); load_ref_decorations(DECORATE_FULL_REFS); cgit_print_diff_ctrls(); html("<table summary='commit info' class='commit-info'>\n"); html("<tr><th>author</th><td>"); html_txt(info->author); if (!ctx.cfg.noplainemail) { html(" "); html_txt(info->author_email); } html("</td><td class='right'>"); cgit_print_date(info->author_date, FMT_LONGDATE, ctx.cfg.local_time); html("</td></tr>\n"); html("<tr><th>committer</th><td>"); html_txt(info->committer); if (!ctx.cfg.noplainemail) { html(" "); html_txt(info->committer_email); } html("</td><td class='right'>"); cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time); html("</td></tr>\n"); html("<tr><th>commit</th><td colspan='2' class='sha1'>"); tmp = sha1_to_hex(commit->object.sha1); cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp, prefix, 0); html(" ("); cgit_patch_link("patch", NULL, NULL, NULL, tmp, prefix); html(")</td></tr>\n"); html("<tr><th>tree</th><td colspan='2' class='sha1'>"); tmp = xstrdup(hex); cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL, ctx.qry.head, tmp, NULL); if (prefix) { html(" /"); cgit_tree_link(prefix, NULL, NULL, ctx.qry.head, tmp, prefix); } free(tmp); html("</td></tr>\n"); for (p = commit->parents; p; p = p->next) { parent = lookup_commit_reference(p->item->object.sha1); if (!parent) { html("<tr><td colspan='3'>"); cgit_print_error("Error reading parent commit"); html("</td></tr>"); continue; } html("<tr><th>parent</th>" "<td colspan='2' class='sha1'>"); tmp = tmp2 = sha1_to_hex(p->item->object.sha1); if (ctx.repo->enable_subject_links) { parent_info = cgit_parse_commit(parent); tmp2 = parent_info->subject; } cgit_commit_link(tmp2, NULL, NULL, ctx.qry.head, tmp, prefix, 0); html(" ("); cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex, sha1_to_hex(p->item->object.sha1), prefix, 0); html(")</td></tr>"); parents++; } if (ctx.repo->snapshots) { html("<tr><th>download</th><td colspan='2' class='sha1'>"); cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head, hex, ctx.repo->snapshots); html("</td></tr>"); } html("</table>\n"); html("<div class='commit-subject'>"); if (ctx.repo->commit_filter) cgit_open_filter(ctx.repo->commit_filter); html_txt(info->subject); if (ctx.repo->commit_filter) cgit_close_filter(ctx.repo->commit_filter); show_commit_decorations(commit); html("</div>"); html("<div class='commit-msg'>"); if (ctx.repo->commit_filter) cgit_open_filter(ctx.repo->commit_filter); html_txt(info->msg); if (ctx.repo->commit_filter) cgit_close_filter(ctx.repo->commit_filter); html("</div>"); if (notes.len != 0) { html("<div class='notes-header'>Notes</div>"); html("<div class='notes'>"); if (ctx.repo->commit_filter) cgit_open_filter(ctx.repo->commit_filter); html_txt(notes.buf); if (ctx.repo->commit_filter) cgit_close_filter(ctx.repo->commit_filter); html("</div>"); html("<div class='notes-footer'></div>"); } if (parents < 3) { if (parents) tmp = sha1_to_hex(commit->parents->item->object.sha1); else tmp = NULL; cgit_print_diff(ctx.qry.sha1, tmp, prefix, 0); } strbuf_release(¬es); cgit_free_commitinfo(info); }
int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) { int i, newfd, stage = 0; unsigned char sha1[20]; struct tree_desc t[MAX_UNPACK_TREES]; struct unpack_trees_options opts; int prefix_set = 0; const struct option read_tree_options[] = { { OPTION_CALLBACK, 0, "index-output", NULL, N_("file"), N_("write resulting index to <file>"), PARSE_OPT_NONEG, index_output_cb }, OPT_SET_INT(0, "empty", &read_empty, N_("only empty the index"), 1), OPT__VERBOSE(&opts.verbose_update, N_("be verbose")), OPT_GROUP(N_("Merging")), OPT_SET_INT('m', NULL, &opts.merge, N_("perform a merge in addition to a read"), 1), OPT_SET_INT(0, "trivial", &opts.trivial_merges_only, N_("3-way merge if no file level merging required"), 1), OPT_SET_INT(0, "aggressive", &opts.aggressive, N_("3-way merge in presence of adds and removes"), 1), OPT_SET_INT(0, "reset", &opts.reset, N_("same as -m, but discard unmerged entries"), 1), { OPTION_STRING, 0, "prefix", &opts.prefix, N_("<subdirectory>/"), N_("read the tree into the index under <subdirectory>/"), PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP }, OPT_SET_INT('u', NULL, &opts.update, N_("update working tree with merge result"), 1), { OPTION_CALLBACK, 0, "exclude-per-directory", &opts, N_("gitignore"), N_("allow explicitly ignored files to be overwritten"), PARSE_OPT_NONEG, exclude_per_directory_cb }, OPT_SET_INT('i', NULL, &opts.index_only, N_("don't check the working tree after merging"), 1), OPT__DRY_RUN(&opts.dry_run, N_("don't update the index or the work tree")), OPT_SET_INT(0, "no-sparse-checkout", &opts.skip_sparse_checkout, N_("skip applying sparse checkout filter"), 1), OPT_SET_INT(0, "debug-unpack", &opts.debug_unpack, N_("debug unpack-trees"), 1), OPT_END() }; memset(&opts, 0, sizeof(opts)); opts.head_idx = -1; opts.src_index = &the_index; opts.dst_index = &the_index; git_config(git_default_config, NULL); argc = parse_options(argc, argv, unused_prefix, read_tree_options, read_tree_usage, 0); newfd = hold_locked_index(&lock_file, 1); prefix_set = opts.prefix ? 1 : 0; if (1 < opts.merge + opts.reset + prefix_set) die("Which one? -m, --reset, or --prefix?"); if (opts.reset || opts.merge || opts.prefix) { if (read_cache_unmerged() && (opts.prefix || opts.merge)) die("You need to resolve your current index first"); stage = opts.merge = 1; } resolve_undo_clear(); for (i = 0; i < argc; i++) { const char *arg = argv[i]; if (get_sha1(arg, sha1)) die("Not a valid object name %s", arg); if (list_tree(sha1) < 0) die("failed to unpack tree object %s", arg); stage++; } if (nr_trees == 0 && !read_empty) warning("read-tree: emptying the index with no arguments is deprecated; use --empty"); else if (nr_trees > 0 && read_empty) die("passing trees as arguments contradicts --empty"); if (1 < opts.index_only + opts.update) die("-u and -i at the same time makes no sense"); if ((opts.update||opts.index_only) && !opts.merge) die("%s is meaningless without -m, --reset, or --prefix", opts.update ? "-u" : "-i"); if ((opts.dir && !opts.update)) die("--exclude-per-directory is meaningless unless -u"); if (opts.merge && !opts.index_only) setup_work_tree(); if (opts.merge) { if (stage < 2) die("just how do you expect me to merge %d trees?", stage-1); switch (stage - 1) { case 1: opts.fn = opts.prefix ? bind_merge : oneway_merge; break; case 2: opts.fn = twoway_merge; opts.initial_checkout = is_cache_unborn(); break; case 3: default: opts.fn = threeway_merge; break; } if (stage - 1 >= 3) opts.head_idx = stage - 2; else opts.head_idx = 1; } if (opts.debug_unpack) opts.fn = debug_merge; cache_tree_free(&active_cache_tree); for (i = 0; i < nr_trees; i++) { struct tree *tree = trees[i]; parse_tree(tree); init_tree_desc(t+i, tree->buffer, tree->size); } if (unpack_trees(nr_trees, t, &opts)) return 128; if (opts.debug_unpack || opts.dry_run) return 0; /* do not write the index out */ /* * When reading only one tree (either the most basic form, * "-m ent" or "--reset ent" form), we can obtain a fully * valid cache-tree because the index must match exactly * what came from the tree. */ if (nr_trees == 1 && !opts.prefix) prime_cache_tree(&active_cache_tree, trees[0]); if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) die("unable to write new index file"); return 0; }
int cmd_grep(int argc, const char **argv, const char *prefix) { int hit = 0; int cached = 0, untracked = 0, opt_exclude = -1; int seen_dashdash = 0; int external_grep_allowed__ignored; const char *show_in_pager = NULL, *default_pager = "dummy"; struct grep_opt opt; struct object_array list = OBJECT_ARRAY_INIT; const char **paths = NULL; 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_BOOLEAN(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_BOOLEAN(0, "untracked", &untracked, N_("search in both tracked and untracked files")), OPT_SET_INT(0, "exclude-standard", &opt_exclude, N_("search also in ignored files"), 1), OPT_GROUP(""), OPT_BOOLEAN('v', "invert-match", &opt.invert, N_("show non-matching lines")), OPT_BOOLEAN('i', "ignore-case", &opt.ignore_case, N_("case insensitive matching")), OPT_BOOLEAN('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), { 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_BOOLEAN('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_BOOLEAN('l', "files-with-matches", &opt.name_only, N_("show only filenames instead of matching lines")), OPT_BOOLEAN(0, "name-only", &opt.name_only, N_("synonym for --files-with-matches")), OPT_BOOLEAN('L', "files-without-match", &opt.unmatch_name_only, N_("show only the names of files without match")), OPT_BOOLEAN('z', "null", &opt.null_following_name, N_("print NUL after filenames")), OPT_BOOLEAN('c', "count", &opt.count, N_("show the number of matches instead of matching lines")), OPT__COLOR(&opt.color, N_("highlight matches")), OPT_BOOLEAN(0, "break", &opt.file_break, N_("print empty line between matches from different files")), OPT_BOOLEAN(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_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"), context_callback), OPT_BOOLEAN('p', "show-function", &opt.funcname, N_("show a line with the function name before matches")), OPT_BOOLEAN('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_BOOLEAN(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_BOOLEAN(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_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored, N_("allow calling of grep(1) (ignored by this build)")), { OPTION_CALLBACK, 0, "help-all", &options, NULL, N_("show usage"), PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback }, OPT_END() }; /* * 'git grep -h', unlike 'git grep -h <pattern>', is a request * to show usage information and exit. */ if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(grep_usage, options); init_grep_defaults(); git_config(grep_cmd_config, NULL); grep_init(&opt, 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 | PARSE_OPT_NO_INTERNAL_HELP); grep_commit_pattern_type(pattern_type_arg, &opt); if (use_index && !startup_info->have_repository) /* 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); use_threads = 0; } if ((opt.binary & GREP_BINARY_NOMATCH)) use_threads = 0; 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]; /* Is it a rev? */ if (!get_sha1(arg, sha1)) { struct object *object = parse_object(sha1); if (!object) die(_("bad object %s"), arg); add_object_array(object, arg, &list); continue; } if (!strcmp(arg, "--")) { i++; seen_dashdash = 1; } break; } #ifndef NO_PTHREADS if (list.nr || cached || online_cpus() == 1) use_threads = 0; #else use_threads = 0; #endif #ifndef NO_PTHREADS if (use_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); } paths = get_pathspec(prefix, argv + i); init_pathspec(&pathspec, paths); pathspec.max_depth = opt.max_depth; pathspec.recursive = 1; 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 (!show_in_pager) 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); } 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 (use_threads) hit |= wait_all(); if (hit && show_in_pager) run_pager(&opt, prefix); free_grep_patterns(&opt); return !hit; }
int cmd_rm(int argc, const char **argv, const char *prefix) { int i, newfd; const char **pathspec; char *seen; 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(); newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) die(_("index file corrupt")); /* * Drop trailing directory separators from directories so we'll find * submodules in the index. */ for (i = 0; i < argc; i++) { size_t pathlen = strlen(argv[i]); if (pathlen && is_dir_sep(argv[i][pathlen - 1]) && is_directory(argv[i])) { do { pathlen--; } while (pathlen && is_dir_sep(argv[i][pathlen - 1])); argv[i] = xmemdupz(argv[i], pathlen); } } pathspec = get_pathspec(prefix, argv); refresh_index(&the_index, REFRESH_QUIET, pathspec, NULL, NULL); seen = NULL; for (i = 0; pathspec[i] ; i++) /* nothing */; seen = xcalloc(i, 1); for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen)) continue; ALLOC_GROW(list.entry, list.nr + 1, list.alloc); list.entry[list.nr].name = ce->name; list.entry[list.nr++].is_submodule = S_ISGITLINK(ce->ce_mode); } if (pathspec) { const char *match; int seen_any = 0; for (i = 0; (match = pathspec[i]) != NULL ; i++) { if (!seen[i]) { if (!ignore_unmatch) { die(_("pathspec '%s' did not match any files"), match); } } else { seen_any = 1; } if (!recursive && seen[i] == MATCHED_RECURSIVELY) die(_("not removing '%s' recursively without -r"), *match ? match : "."); } 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; 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; continue; } } else { struct strbuf buf = STRBUF_INIT; strbuf_addstr(&buf, path); if (!remove_dir_recursively(&buf, 0)) { removed = 1; strbuf_release(&buf); continue; } 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 (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) die(_("Unable to write new index file")); } return 0; }