void cgit_diff_tree(const unsigned char *old_sha1, const unsigned char *new_sha1, filepair_fn fn, const char *prefix, int ignorews) { struct diff_options opt; struct pathspec_item item; diff_setup(&opt); opt.output_format = DIFF_FORMAT_CALLBACK; opt.detect_rename = 1; opt.rename_limit = ctx.cfg.renamelimit; DIFF_OPT_SET(&opt, RECURSIVE); if (ignorews) DIFF_XDL_SET(&opt, IGNORE_WHITESPACE); opt.format_callback = cgit_diff_tree_cb; opt.format_callback_data = fn; if (prefix) { item.match = prefix; item.len = strlen(prefix); opt.pathspec.nr = 1; opt.pathspec.items = &item; } diff_setup_done(&opt); if (old_sha1 && !is_null_sha1(old_sha1)) diff_tree_sha1(old_sha1, new_sha1, "", &opt); else diff_root_tree_sha1(new_sha1, "", &opt); diffcore_std(&opt); diff_flush(&opt); }
void check_for_new_submodule_commits(unsigned char new_sha1[20]) { struct rev_info rev; struct commit *commit; const char *argv[] = {NULL, NULL, "--not", "--all", NULL}; int argc = ARRAY_SIZE(argv) - 1; init_revisions(&rev, NULL); argv[1] = xstrdup(sha1_to_hex(new_sha1)); setup_revisions(argc, argv, &rev, NULL); if (prepare_revision_walk(&rev)) die("revision walk setup failed"); /* * Collect all submodules (whether checked out or not) for which new * commits have been recorded upstream in "changed_submodule_paths". */ while ((commit = get_revision(&rev))) { struct commit_list *parent = commit->parents; while (parent) { struct diff_options diff_opts; diff_setup(&diff_opts); diff_opts.output_format |= DIFF_FORMAT_CALLBACK; diff_opts.format_callback = submodule_collect_changed_cb; if (diff_setup_done(&diff_opts) < 0) die("diff_setup_done failed"); diff_tree_sha1(parent->item->object.sha1, commit->object.sha1, "", &diff_opts); diffcore_std(&diff_opts); diff_flush(&diff_opts); parent = parent->next; } } free((char *)argv[1]); }
static int check_changes_tracked_files(struct pathspec ps) { int result; struct rev_info rev; struct object_id dummy; /* No initial commit. */ if (get_oid("HEAD", &dummy)) return -1; if (read_cache() < 0) return -1; init_revisions(&rev, NULL); rev.prune_data = ps; rev.diffopt.flags.quick = 1; rev.diffopt.flags.ignore_submodules = 1; rev.abbrev = 0; add_head_to_pending(&rev); diff_setup_done(&rev.diffopt); result = run_diff_index(&rev, 1); if (diff_result_code(&rev.diffopt, result)) return 1; object_array_clear(&rev.pending); result = run_diff_files(&rev, 0); if (diff_result_code(&rev.diffopt, result)) return 1; return 0; }
void cgit_diff_tree(const unsigned char *old_sha1, const unsigned char *new_sha1, filepair_fn fn, const char *prefix) { struct diff_options opt; int ret; int prefixlen; diff_setup(&opt); opt.output_format = DIFF_FORMAT_CALLBACK; opt.detect_rename = 1; opt.rename_limit = ctx.cfg.renamelimit; DIFF_OPT_SET(&opt, RECURSIVE); opt.format_callback = cgit_diff_tree_cb; opt.format_callback_data = fn; if (prefix) { opt.nr_paths = 1; opt.paths = &prefix; prefixlen = strlen(prefix); opt.pathlens = &prefixlen; } diff_setup_done(&opt); if (old_sha1 && !is_null_sha1(old_sha1)) ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); else ret = diff_root_tree_sha1(new_sha1, "", &opt); diffcore_std(&opt); diff_flush(&opt); }
void diff_no_index(struct rev_info *revs, int argc, const char **argv, const char *prefix) { int i, prefixlen; const char *paths[2]; diff_setup(&revs->diffopt); for (i = 1; i < argc - 2; ) { int j; if (!strcmp(argv[i], "--no-index")) i++; else if (!strcmp(argv[i], "--")) i++; else { j = diff_opt_parse(&revs->diffopt, argv + i, argc - i); if (j <= 0) die("invalid diff option/value: %s", argv[i]); i += j; } } prefixlen = prefix ? strlen(prefix) : 0; for (i = 0; i < 2; i++) { const char *p = argv[argc - 2 + i]; if (!strcmp(p, "-")) /* * stdin should be spelled as "-"; if you have * path that is "-", spell it as "./-". */ p = file_from_standard_input; else if (prefixlen) p = xstrdup(prefix_filename(prefix, prefixlen, p)); paths[i] = p; } revs->diffopt.skip_stat_unmatch = 1; if (!revs->diffopt.output_format) revs->diffopt.output_format = DIFF_FORMAT_PATCH; DIFF_OPT_SET(&revs->diffopt, NO_INDEX); revs->max_count = -2; diff_setup_done(&revs->diffopt); setup_diff_pager(&revs->diffopt); DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); if (queue_diff(&revs->diffopt, paths[0], paths[1])) exit(1); diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/"); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); /* * The return code for --no-index imitates diff(1): * 0 = no changes, 1 = changes, else error */ exit(diff_result_code(&revs->diffopt, 0)); }
/* * Get information of all renames which occurred between 'o_tree' and * 'tree'. We need the three trees in the merge ('o_tree', 'a_tree' and * 'b_tree') to be able to associate the correct cache entries with * the rename information. 'tree' is always equal to either a_tree or b_tree. */ static struct string_list *get_renames(struct merge_options *o, struct tree *tree, struct tree *o_tree, struct tree *a_tree, struct tree *b_tree, struct string_list *entries) { int i; struct string_list *renames; struct diff_options opts; renames = xcalloc(1, sizeof(struct string_list)); diff_setup(&opts); DIFF_OPT_SET(&opts, RECURSIVE); opts.detect_rename = DIFF_DETECT_RENAME; opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit : o->diff_rename_limit >= 0 ? o->diff_rename_limit : 500; opts.warn_on_too_large_rename = 1; opts.output_format = DIFF_FORMAT_NO_OUTPUT; if (diff_setup_done(&opts) < 0) die("diff setup failed"); diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts); diffcore_std(&opts); for (i = 0; i < diff_queued_diff.nr; ++i) { struct string_list_item *item; struct rename *re; struct diff_filepair *pair = diff_queued_diff.queue[i]; if (pair->status != 'R') { diff_free_filepair(pair); continue; } re = xmalloc(sizeof(*re)); re->processed = 0; re->pair = pair; item = string_list_lookup(re->pair->one->path, entries); if (!item) re->src_entry = insert_stage_data(re->pair->one->path, o_tree, a_tree, b_tree, entries); else re->src_entry = item->util; item = string_list_lookup(re->pair->two->path, entries); if (!item) re->dst_entry = insert_stage_data(re->pair->two->path, o_tree, a_tree, b_tree, entries); else re->dst_entry = item->util; item = string_list_insert(pair->one->path, renames); item->util = re; } opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_queued_diff.nr = 0; diff_flush(&opts); return renames; }
int init_patch_ids(struct patch_ids *ids) { memset(ids, 0, sizeof(*ids)); diff_setup(&ids->diffopts); ids->diffopts.detect_rename = 0; DIFF_OPT_SET(&ids->diffopts, RECURSIVE); diff_setup_done(&ids->diffopts); hashmap_init(&ids->patches, (hashmap_cmp_fn)patch_id_cmp, 256); return 0; }
int init_patch_ids(struct patch_ids *ids) { memset(ids, 0, sizeof(*ids)); diff_setup(&ids->diffopts); ids->diffopts.detect_rename = 0; ids->diffopts.flags.recursive = 1; diff_setup_done(&ids->diffopts); hashmap_init(&ids->patches, patch_id_neq, &ids->diffopts, 256); return 0; }
/** * Returns 1 if there are unstaged changes, 0 otherwise. */ static int has_unstaged_changes(const char *prefix) { struct rev_info rev_info; int result; init_revisions(&rev_info, prefix); DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES); DIFF_OPT_SET(&rev_info.diffopt, QUICK); diff_setup_done(&rev_info.diffopt); result = run_diff_files(&rev_info, 0); return diff_result_code(&rev_info.diffopt, result); }
static void show_local_changes(struct object *head, const struct diff_options *opts) { struct rev_info rev; /* I think we want full paths, even if we're in a subdirectory. */ repo_init_revisions(the_repository, &rev, NULL); rev.diffopt.flags = opts->flags; rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS; diff_setup_done(&rev.diffopt); add_pending_object(&rev, head, NULL); run_diff_index(&rev, 0); }
static void finish(struct commit *head_commit, struct commit_list *remoteheads, const unsigned char *new_head, const char *msg) { struct strbuf reflog_message = STRBUF_INIT; const unsigned char *head = head_commit->object.oid.hash; if (!msg) strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION")); else { if (verbosity >= 0) printf("%s\n", msg); strbuf_addf(&reflog_message, "%s: %s", getenv("GIT_REFLOG_ACTION"), msg); } if (squash) { squash_message(head_commit, remoteheads); } else { if (verbosity >= 0 && !merge_msg.len) printf(_("No merge message -- not updating HEAD\n")); else { const char *argv_gc_auto[] = { "gc", "--auto", NULL }; update_ref(reflog_message.buf, "HEAD", new_head, head, 0, UPDATE_REFS_DIE_ON_ERR); /* * We ignore errors in 'gc --auto', since the * user should see them. */ close_all_packs(); run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); } } if (new_head && show_diffstat) { struct diff_options opts; diff_setup(&opts); opts.stat_width = -1; /* use full terminal width */ opts.stat_graph_width = -1; /* respect statGraphWidth config */ opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; opts.detect_rename = DIFF_DETECT_RENAME; diff_setup_done(&opts); diff_tree_sha1(head, new_head, "", &opts); diffcore_std(&opts); diff_flush(&opts); } /* Run a post-merge hook */ run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL); strbuf_release(&reflog_message); }
static void finish(const unsigned char *new_head, const char *msg) { struct strbuf reflog_message = STRBUF_INIT; if (!msg) strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION")); else { if (verbosity >= 0) printf("%s\n", msg); strbuf_addf(&reflog_message, "%s: %s", getenv("GIT_REFLOG_ACTION"), msg); } if (squash) { squash_message(); } else { if (verbosity >= 0 && !merge_msg.len) printf(_("No merge message -- not updating HEAD\n")); else { const char *argv_gc_auto[] = { "gc", "--auto", NULL }; update_ref(reflog_message.buf, "HEAD", new_head, head, 0, DIE_ON_ERR); /* * We ignore errors in 'gc --auto', since the * user should see them. */ run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); } } if (new_head && show_diffstat) { struct diff_options opts; diff_setup(&opts); opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; opts.detect_rename = DIFF_DETECT_RENAME; if (diff_use_color_default > 0) DIFF_OPT_SET(&opts, COLOR_DIFF); if (diff_setup_done(&opts) < 0) die(_("diff_setup_done failed")); diff_tree_sha1(head, new_head, "", &opts); diffcore_std(&opts); diff_flush(&opts); } /* Run a post-merge hook */ run_hook(NULL, "post-merge", squash ? "1" : "0", NULL); strbuf_release(&reflog_message); }
static void prepare_bases(struct base_tree_info *bases, struct commit *base, struct commit **list, int total) { struct commit *commit; struct rev_info revs; struct diff_options diffopt; int i; if (!base) return; diff_setup(&diffopt); DIFF_OPT_SET(&diffopt, RECURSIVE); diff_setup_done(&diffopt); oidcpy(&bases->base_commit, &base->object.oid); init_revisions(&revs, NULL); revs.max_parents = 1; revs.topo_order = 1; for (i = 0; i < total; i++) { list[i]->object.flags &= ~UNINTERESTING; add_pending_object(&revs, &list[i]->object, "rev_list"); list[i]->util = (void *)1; } base->object.flags |= UNINTERESTING; add_pending_object(&revs, &base->object, "base"); if (prepare_revision_walk(&revs)) die(_("revision walk setup failed")); /* * Traverse the commits list, get prerequisite patch ids * and stuff them in bases structure. */ while ((commit = get_revision(&revs)) != NULL) { unsigned char sha1[20]; struct object_id *patch_id; if (commit->util) continue; if (commit_patch_id(commit, &diffopt, sha1)) die(_("cannot get patch id")); ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id); patch_id = bases->patch_id + bases->nr_patch_id; hashcpy(patch_id->hash, sha1); bases->nr_patch_id++; } }
int main(int ac, const char **av) { int stage1, stage2; setup_git_directory(); git_config(git_diff_config); read_cache(); diff_setup(&diff_options); while (1 < ac && av[1][0] == '-') { const char *arg = av[1]; if (!strcmp(arg, "-r")) ; /* as usual */ else { int diff_opt_cnt; diff_opt_cnt = diff_opt_parse(&diff_options, av+1, ac-1); if (diff_opt_cnt < 0) usage(diff_stages_usage); else if (diff_opt_cnt) { av += diff_opt_cnt; ac -= diff_opt_cnt; continue; } else usage(diff_stages_usage); } ac--; av++; } if (ac < 3 || sscanf(av[1], "%d", &stage1) != 1 || ! (0 <= stage1 && stage1 <= 3) || sscanf(av[2], "%d", &stage2) != 1 || ! (0 <= stage2 && stage2 <= 3)) usage(diff_stages_usage); av += 3; /* The rest from av[0] are for paths restriction. */ diff_options.paths = av; if (diff_setup_done(&diff_options) < 0) usage(diff_stages_usage); diff_stages(stage1, stage2); diffcore_std(&diff_options); diff_flush(&diff_options); return 0; }
/** * Returns 1 if there are uncommitted changes, 0 otherwise. */ static int has_uncommitted_changes(const char *prefix) { struct rev_info rev_info; int result; if (is_cache_unborn()) return 0; init_revisions(&rev_info, prefix); DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES); DIFF_OPT_SET(&rev_info.diffopt, QUICK); add_head_to_pending(&rev_info); diff_setup_done(&rev_info.diffopt); result = run_diff_index(&rev_info, 1); return diff_result_code(&rev_info.diffopt, result); }
static void calculate_changed_submodule_paths(void) { struct rev_info rev; struct commit *commit; struct argv_array argv = ARGV_ARRAY_INIT; /* No need to check if there are no submodules configured */ if (!config_name_for_path.nr) return; init_revisions(&rev, NULL); argv_array_push(&argv, "--"); /* argv[0] program name */ sha1_array_for_each_unique(&ref_tips_after_fetch, add_sha1_to_argv, &argv); argv_array_push(&argv, "--not"); sha1_array_for_each_unique(&ref_tips_before_fetch, add_sha1_to_argv, &argv); setup_revisions(argv.argc, argv.argv, &rev, NULL); if (prepare_revision_walk(&rev)) die("revision walk setup failed"); /* * Collect all submodules (whether checked out or not) for which new * commits have been recorded upstream in "changed_submodule_paths". */ while ((commit = get_revision(&rev))) { struct commit_list *parent = commit->parents; while (parent) { struct diff_options diff_opts; diff_setup(&diff_opts); DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.output_format |= DIFF_FORMAT_CALLBACK; diff_opts.format_callback = submodule_collect_changed_cb; if (diff_setup_done(&diff_opts) < 0) die("diff_setup_done failed"); diff_tree_sha1(parent->item->object.sha1, commit->object.sha1, "", &diff_opts); diffcore_std(&diff_opts); diff_flush(&diff_opts); parent = parent->next; } } argv_array_clear(&argv); sha1_array_clear(&ref_tips_before_fetch); sha1_array_clear(&ref_tips_after_fetch); initialized_fetch_ref_tips = 0; }
static void print_summary(const char *prefix, const unsigned char *sha1) { struct rev_info rev; struct commit *commit; static const char *format = "format:%h: \"%s\""; unsigned char junk_sha1[20]; const char *head = resolve_ref("HEAD", junk_sha1, 0, NULL); commit = lookup_commit(sha1); if (!commit) die("couldn't look up newly created commit"); if (!commit || parse_commit(commit)) die("could not parse newly created commit"); init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); rev.abbrev = 0; rev.diff = 1; rev.diffopt.output_format = DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY; rev.verbose_header = 1; rev.show_root_diff = 1; get_commit_format(format, &rev); rev.always_show_header = 0; rev.diffopt.detect_rename = 1; rev.diffopt.rename_limit = 100; rev.diffopt.break_opt = 0; diff_setup_done(&rev.diffopt); printf("[%s%s]: created ", !prefixcmp(head, "refs/heads/") ? head + 11 : !strcmp(head, "HEAD") ? "detached HEAD" : head, initial_commit ? " (root-commit)" : ""); if (!log_tree_commit(&rev, commit)) { struct strbuf buf = STRBUF_INIT; format_commit_message(commit, format + 7, &buf, DATE_NORMAL); printf("%s\n", buf.buf); strbuf_release(&buf); } }
static void print_summary(const char *prefix, const unsigned char *sha1) { struct rev_info rev; struct commit *commit; commit = lookup_commit(sha1); if (!commit) die("couldn't look up newly created commit"); if (!commit || parse_commit(commit)) die("could not parse newly created commit"); init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); rev.abbrev = 0; rev.diff = 1; rev.diffopt.output_format = DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY; rev.verbose_header = 1; rev.show_root_diff = 1; get_commit_format("format:%h: %s", &rev); rev.always_show_header = 0; rev.diffopt.detect_rename = 1; rev.diffopt.rename_limit = 100; rev.diffopt.break_opt = 0; diff_setup_done(&rev.diffopt); printf("Created %scommit ", initial_commit ? "initial " : ""); if (!log_tree_commit(&rev, commit)) { struct strbuf buf = STRBUF_INIT; format_commit_message(commit, "%h: %s", &buf); printf("%s\n", buf.buf); strbuf_release(&buf); } }
/* * We have an origin -- find the path that corresponds to it in its * parent and return an origin structure to represent it. */ static struct blame_origin *find_rename(struct commit *parent, struct blame_origin *origin) { struct blame_origin *porigin = NULL; struct diff_options diff_opts; int i; diff_setup(&diff_opts); DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.detect_rename = DIFF_DETECT_RENAME; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_opts.single_follow = origin->path; diff_setup_done(&diff_opts); if (is_null_oid(&origin->commit->object.oid)) do_diff_cache(&parent->tree->object.oid, &diff_opts); else diff_tree_oid(&parent->tree->object.oid, &origin->commit->tree->object.oid, "", &diff_opts); diffcore_std(&diff_opts); for (i = 0; i < diff_queued_diff.nr; i++) { struct diff_filepair *p = diff_queued_diff.queue[i]; if ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, origin->path)) { porigin = get_origin(parent, p->one->path); oidcpy(&porigin->blob_oid, &p->one->oid); porigin->mode = p->one->mode; break; } } diff_flush(&diff_opts); clear_pathspec(&diff_opts.pathspec); return porigin; }
static void print_summary(const char *prefix, const unsigned char *sha1, int initial_commit) { struct rev_info rev; struct commit *commit; struct strbuf format = STRBUF_INIT; unsigned char junk_sha1[20]; const char *head; struct pretty_print_context pctx = {0}; struct strbuf author_ident = STRBUF_INIT; struct strbuf committer_ident = STRBUF_INIT; commit = lookup_commit(sha1); if (!commit) die(_("couldn't look up newly created commit")); if (parse_commit(commit)) die(_("could not parse newly created commit")); strbuf_addstr(&format, "format:%h] %s"); format_commit_message(commit, "%an <%ae>", &author_ident, &pctx); format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx); if (strbuf_cmp(&author_ident, &committer_ident)) { strbuf_addstr(&format, "\n Author: "); strbuf_addbuf_percentquote(&format, &author_ident); } if (!committer_ident_sufficiently_given()) { strbuf_addstr(&format, "\n Committer: "); strbuf_addbuf_percentquote(&format, &committer_ident); if (advice_implicit_identity) { strbuf_addch(&format, '\n'); strbuf_addstr(&format, _(implicit_ident_advice)); } } strbuf_release(&author_ident); strbuf_release(&committer_ident); init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); rev.diff = 1; rev.diffopt.output_format = DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY; rev.verbose_header = 1; rev.show_root_diff = 1; get_commit_format(format.buf, &rev); rev.always_show_header = 0; rev.diffopt.detect_rename = 1; rev.diffopt.break_opt = 0; diff_setup_done(&rev.diffopt); head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL); printf("[%s%s ", starts_with(head, "refs/heads/") ? head + 11 : !strcmp(head, "HEAD") ? _("detached HEAD") : head, initial_commit ? _(" (root-commit)") : ""); if (!log_tree_commit(&rev, commit)) { rev.always_show_header = 1; rev.use_terminator = 1; log_tree_commit(&rev, commit); } strbuf_release(&format); }
static void make_cover_letter(struct rev_info *rev, int use_stdout, int numbered, int numbered_files, struct commit *origin, int nr, struct commit **list, struct commit *head) { const char *committer; const char *subject_start = NULL; const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n"; const char *msg; const char *extra_headers = rev->extra_headers; struct shortlog log; struct strbuf sb = STRBUF_INIT; int i; const char *encoding = "UTF-8"; struct diff_options opts; int need_8bit_cte = 0; struct commit *commit = NULL; if (rev->commit_format != CMIT_FMT_EMAIL) die("Cover letter needs email format"); committer = git_committer_info(0); if (!numbered_files) { /* * We fake a commit for the cover letter so we get the filename * desired. */ commit = xcalloc(1, sizeof(*commit)); commit->buffer = xmalloc(400); snprintf(commit->buffer, 400, "tree 0000000000000000000000000000000000000000\n" "parent %s\n" "author %s\n" "committer %s\n\n" "cover letter\n", sha1_to_hex(head->object.sha1), committer, committer); } if (!use_stdout && reopen_stdout(commit, rev)) return; if (commit) { free(commit->buffer); free(commit); } log_write_email_headers(rev, head, &subject_start, &extra_headers, &need_8bit_cte); for (i = 0; !need_8bit_cte && i < nr; i++) if (has_non_ascii(list[i]->buffer)) need_8bit_cte = 1; msg = body; pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822, encoding); pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers, encoding, need_8bit_cte); pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0); printf("%s\n", sb.buf); strbuf_release(&sb); shortlog_init(&log); log.wrap_lines = 1; log.wrap = 72; log.in1 = 2; log.in2 = 4; for (i = 0; i < nr; i++) shortlog_add_commit(&log, list[i]); shortlog_output(&log); /* * We can only do diffstat with a unique reference point */ if (!origin) return; memcpy(&opts, &rev->diffopt, sizeof(opts)); opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; diff_setup_done(&opts); diff_tree_sha1(origin->tree->object.sha1, head->tree->object.sha1, "", &opts); diffcore_std(&opts); diff_flush(&opts); printf("\n"); }
int cmd_diff(int argc, const char **argv, const char *prefix) { int i; struct rev_info rev; struct object_array_entry ent[100]; int ents = 0, blobs = 0, paths = 0; const char *path = NULL; struct blobinfo blob[2]; int nongit; int result = 0; /* * We could get N tree-ish in the rev.pending_objects list. * Also there could be M blobs there, and P pathspecs. * * N=0, M=0: * cache vs files (diff-files) * N=0, M=2: * compare two random blobs. P must be zero. * N=0, M=1, P=1: * compare a blob with a working tree file. * * N=1, M=0: * tree vs cache (diff-index --cached) * * N=2, M=0: * tree vs tree (diff-tree) * * N=0, M=0, P=2: * compare two filesystem entities (aka --no-index). * * Other cases are errors. */ prefix = setup_git_directory_gently(&nongit); gitmodules_config(); git_config(git_diff_ui_config, NULL); if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; init_revisions(&rev, prefix); /* If this is a no-index diff, just run it and exit there. */ diff_no_index(&rev, argc, argv, nongit, prefix); /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; /* Default to let external and textconv be used */ DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL); DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV); if (nongit) die("Not a git repository"); argc = setup_revisions(argc, argv, &rev, NULL); if (!rev.diffopt.output_format) { rev.diffopt.output_format = DIFF_FORMAT_PATCH; if (diff_setup_done(&rev.diffopt) < 0) die("diff_setup_done failed"); } DIFF_OPT_SET(&rev.diffopt, RECURSIVE); /* * If the user asked for our exit code then don't start a * pager or we would end up reporting its exit code instead. */ if (!DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS) && check_pager_config("diff") != 0) setup_pager(); /* * Do we have --cached and not have a pending object, then * default to HEAD by hand. Eek. */ if (!rev.pending.nr) { int i; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--")) break; else if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged")) { add_head_to_pending(&rev); if (!rev.pending.nr) { struct tree *tree; tree = lookup_tree((const unsigned char*)EMPTY_TREE_SHA1_BIN); add_pending_object(&rev, &tree->object, "HEAD"); } break; } } } for (i = 0; i < rev.pending.nr; i++) { struct object_array_entry *list = rev.pending.objects+i; struct object *obj = list->item; const char *name = list->name; int flags = (obj->flags & UNINTERESTING); if (!obj->parsed) obj = parse_object(obj->sha1); obj = deref_tag(obj, NULL, 0); if (!obj) die("invalid object '%s' given.", name); if (obj->type == OBJ_COMMIT) obj = &((struct commit *)obj)->tree->object; if (obj->type == OBJ_TREE) { if (ARRAY_SIZE(ent) <= ents) die("more than %d trees given: '%s'", (int) ARRAY_SIZE(ent), name); obj->flags |= flags; ent[ents].item = obj; ent[ents].name = name; ents++; continue; } if (obj->type == OBJ_BLOB) { if (2 <= blobs) die("more than two blobs given: '%s'", name); hashcpy(blob[blobs].sha1, obj->sha1); blob[blobs].name = name; blob[blobs].mode = list->mode; blobs++; continue; } die("unhandled object '%s' given.", name); } if (rev.prune_data.nr) { if (!path) path = rev.prune_data.items[0].match; paths += rev.prune_data.nr; } /* * Now, do the arguments look reasonable? */ if (!ents) { switch (blobs) { case 0: result = builtin_diff_files(&rev, argc, argv); break; case 1: if (paths != 1) usage(builtin_diff_usage); result = builtin_diff_b_f(&rev, argc, argv, blob, path); break; case 2: if (paths) usage(builtin_diff_usage); result = builtin_diff_blobs(&rev, argc, argv, blob); break; default: usage(builtin_diff_usage); } } else if (blobs) usage(builtin_diff_usage); else if (ents == 1) result = builtin_diff_index(&rev, argc, argv); else if (ents == 2) result = builtin_diff_tree(&rev, argc, argv, ent); else if (ent[0].item->flags & UNINTERESTING) { /* * diff A...B where there is at least one merge base * between A and B. We have ent[0] == merge-base, * ent[ents-2] == A, and ent[ents-1] == B. Show diff * between the base and B. Note that we pick one * merge base at random if there are more than one. */ ent[1] = ent[ents-1]; result = builtin_diff_tree(&rev, argc, argv, ent); } else result = builtin_diff_combined(&rev, argc, argv, ent, ents); result = diff_result_code(&rev.diffopt, result); if (1 < rev.diffopt.skip_stat_unmatch) refresh_index_quietly(); return result; }
static void make_cover_letter(struct rev_info *rev, int use_stdout, int numbered, int numbered_files, struct commit *origin, int nr, struct commit **list, struct commit *head, const char *branch_name, int quiet) { const char *committer; const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n"; const char *msg; struct shortlog log; struct strbuf sb = STRBUF_INIT; int i; const char *encoding = "UTF-8"; struct diff_options opts; int need_8bit_cte = 0; struct pretty_print_context pp = {0}; if (rev->commit_format != CMIT_FMT_EMAIL) die(_("Cover letter needs email format")); committer = git_committer_info(0); if (!use_stdout && reopen_stdout(NULL, numbered_files ? NULL : "cover-letter", rev, quiet)) return; log_write_email_headers(rev, head, &pp.subject, &pp.after_subject, &need_8bit_cte); for (i = 0; !need_8bit_cte && i < nr; i++) if (has_non_ascii(list[i]->buffer)) need_8bit_cte = 1; msg = body; pp.fmt = CMIT_FMT_EMAIL; pp.date_mode = DATE_RFC2822; pp_user_info(&pp, NULL, &sb, committer, encoding); pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte); pp_remainder(&pp, &msg, &sb, 0); add_branch_description(&sb, branch_name); printf("%s\n", sb.buf); strbuf_release(&sb); shortlog_init(&log); log.wrap_lines = 1; log.wrap = 72; log.in1 = 2; log.in2 = 4; for (i = 0; i < nr; i++) shortlog_add_commit(&log, list[i]); shortlog_output(&log); /* * We can only do diffstat with a unique reference point */ if (!origin) return; memcpy(&opts, &rev->diffopt, sizeof(opts)); opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; diff_setup_done(&opts); diff_tree_sha1(origin->tree->object.sha1, head->tree->object.sha1, "", &opts); diffcore_std(&opts); diff_flush(&opts); printf("\n"); print_signature(); }
static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt) { struct diff_options diff_opts; struct diff_queue_struct *q = &diff_queued_diff; struct diff_filepair *choice; const char *paths[1]; int i; /* Remove the file creation entry from the diff queue, and remember it */ choice = q->queue[0]; q->nr = 0; diff_setup(&diff_opts); DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.detect_rename = DIFF_DETECT_RENAME; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_opts.single_follow = opt->paths[0]; diff_opts.break_opt = opt->break_opt; paths[0] = NULL; diff_tree_setup_paths(paths, &diff_opts); if (diff_setup_done(&diff_opts) < 0) die("unable to set up diff options to follow renames"); diff_tree(t1, t2, base, &diff_opts); diffcore_std(&diff_opts); diff_tree_release_paths(&diff_opts); /* Go through the new set of filepairing, and see if we find a more interesting one */ for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; /* * Found a source? Not only do we use that for the new * diff_queued_diff, we will also use that as the path in * the future! */ if ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, opt->paths[0])) { /* Switch the file-pairs around */ q->queue[i] = choice; choice = p; /* Update the path we use from now on.. */ diff_tree_release_paths(opt); opt->paths[0] = xstrdup(p->one->path); diff_tree_setup_paths(opt->paths, opt); break; } } /* * Then, discard all the non-relevane file pairs... */ for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; diff_free_filepair(p); } /* * .. and re-instate the one we want (which might be either the * original one, or the rename/copy we found) */ q->queue[0] = choice; q->nr = 1; }
void diff_no_index(struct rev_info *revs, int argc, const char **argv, int nongit, const char *prefix) { int i; int no_index = 0; unsigned options = 0; /* Were we asked to do --no-index explicitly? */ for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--")) return; if (!strcmp(argv[i], "--no-index")) no_index = 1; if (argv[i][0] != '-') break; } if (!no_index && !nongit) { /* * Inside a git repository, without --no-index. Only * when a path outside the repository is given, * e.g. "git diff /var/tmp/[12]", or "git diff * Makefile /var/tmp/Makefile", allow it to be used as * a colourful "diff" replacement. */ if ((argc != i + 2) || (!path_outside_repo(argv[i]) && !path_outside_repo(argv[i+1]))) return; } if (argc != i + 2) die("git diff %s takes two paths", no_index ? "--no-index" : "[--no-index]"); /* * If the user asked for our exit code then don't start a * pager or we would end up reporting its exit code instead. */ if (!DIFF_OPT_TST(&revs->diffopt, EXIT_WITH_STATUS)) setup_pager(); diff_setup(&revs->diffopt); if (!revs->diffopt.output_format) revs->diffopt.output_format = DIFF_FORMAT_PATCH; for (i = 1; i < argc - 2; ) { int j; if (!strcmp(argv[i], "--no-index")) i++; else if (!strcmp(argv[1], "-q")) options |= DIFF_SILENT_ON_REMOVED; else { j = diff_opt_parse(&revs->diffopt, argv + i, argc - i); if (!j) die("invalid diff option/value: %s", argv[i]); i += j; } } if (prefix) { int len = strlen(prefix); revs->diffopt.paths = xcalloc(2, sizeof(char*)); for (i = 0; i < 2; i++) { const char *p = argv[argc - 2 + i]; /* * stdin should be spelled as '-'; if you have * path that is '-', spell it as ./-. */ p = (strcmp(p, "-") ? xstrdup(prefix_filename(prefix, len, p)) : p); revs->diffopt.paths[i] = p; } } else revs->diffopt.paths = argv + argc - 2; revs->diffopt.nr_paths = 2; DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); DIFF_OPT_SET(&revs->diffopt, NO_INDEX); revs->max_count = -2; if (diff_setup_done(&revs->diffopt) < 0) die("diff_setup_done failed"); if (queue_diff(&revs->diffopt, revs->diffopt.paths[0], revs->diffopt.paths[1])) exit(1); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); /* * The return code for --no-index imitates diff(1): * 0 = no changes, 1 = changes, else error */ exit(revs->diffopt.found_changes); }
/* * Parse revision information, filling in the "rev_info" structure, * and removing the used arguments from the argument list. * * Returns the number of arguments left that weren't recognized * (which are also moved to the head of the argument list) */ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def) { int i, flags, left, seen_dashdash; /* First, search for "--" */ seen_dashdash = 0; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (strcmp(arg, "--")) continue; argv[i] = NULL; argc = i; if (argv[i + 1]) revs->prune_data = get_pathspec(revs->prefix, argv + i + 1); seen_dashdash = 1; break; } /* Second, deal with arguments and options */ flags = 0; for (left = i = 1; i < argc; i++) { const char *arg = argv[i]; if (*arg == '-') { int opts; if (!strcmp(arg, "--all")) { handle_refs(revs, flags, for_each_ref); continue; } if (!strcmp(arg, "--branches")) { handle_refs(revs, flags, for_each_branch_ref); continue; } if (!strcmp(arg, "--tags")) { handle_refs(revs, flags, for_each_tag_ref); continue; } if (!strcmp(arg, "--remotes")) { handle_refs(revs, flags, for_each_remote_ref); continue; } if (!strcmp(arg, "--reflog")) { handle_reflog(revs, flags); continue; } if (!strcmp(arg, "--not")) { flags ^= UNINTERESTING; continue; } if (!strcmp(arg, "--no-walk")) { revs->no_walk = 1; continue; } if (!strcmp(arg, "--do-walk")) { revs->no_walk = 0; continue; } opts = handle_revision_opt(revs, argc - i, argv + i, &left, argv); if (opts > 0) { i += opts - 1; continue; } if (opts < 0) exit(128); continue; } if (handle_revision_arg(arg, revs, flags, seen_dashdash)) { int j; if (seen_dashdash || *arg == '^') die("bad revision '%s'", arg); /* If we didn't have a "--": * (1) all filenames must exist; * (2) all rev-args must not be interpretable * as a valid filename. * but the latter we have checked in the main loop. */ for (j = i; j < argc; j++) verify_filename(revs->prefix, argv[j]); revs->prune_data = get_pathspec(revs->prefix, argv + i); break; } } if (revs->def == NULL) revs->def = def; if (revs->show_merge) prepare_show_merge(revs); if (revs->def && !revs->pending.nr) { unsigned char sha1[20]; struct object *object; unsigned mode; if (get_sha1_with_mode(revs->def, sha1, &mode)) die("bad default revision '%s'", revs->def); object = get_reference(revs, revs->def, sha1, 0); add_pending_object_with_mode(revs, object, revs->def, mode); } /* Did the user ask for any diff output? Run the diff! */ if (revs->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) revs->diff = 1; /* Pickaxe, diff-filter and rename following need diffs */ if (revs->diffopt.pickaxe || revs->diffopt.filter || DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES)) revs->diff = 1; if (revs->topo_order) revs->limited = 1; if (revs->prune_data) { diff_tree_setup_paths(revs->prune_data, &revs->pruning); /* Can't prune commits with rename following: the paths change.. */ if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES)) revs->prune = 1; if (!revs->full_diff) diff_tree_setup_paths(revs->prune_data, &revs->diffopt); } if (revs->combine_merges) { revs->ignore_merges = 0; if (revs->dense_combined_merges && !revs->diffopt.output_format) revs->diffopt.output_format = DIFF_FORMAT_PATCH; } revs->diffopt.abbrev = revs->abbrev; if (diff_setup_done(&revs->diffopt) < 0) die("diff_setup_done failed"); compile_grep_patterns(&revs->grep_filter); if (revs->reverse && revs->reflog_info) die("cannot combine --reverse with --walk-reflogs"); if (revs->rewrite_parents && revs->children.name) die("cannot combine --parents and --children"); /* * Limitations on the graph functionality */ if (revs->reverse && revs->graph) die("cannot combine --reverse with --graph"); if (revs->reflog_info && revs->graph) die("cannot combine --walk-reflogs with --graph"); return left; }
static void diff_tree_local(struct notes_merge_options *o, struct notes_merge_pair *changes, int len, const unsigned char *base, const unsigned char *local) { struct diff_options opt; int i; trace_printf("\tdiff_tree_local(len = %i, base = %.7s, local = %.7s)\n", len, sha1_to_hex(base), sha1_to_hex(local)); diff_setup(&opt); DIFF_OPT_SET(&opt, RECURSIVE); opt.output_format = DIFF_FORMAT_NO_OUTPUT; if (diff_setup_done(&opt) < 0) die("diff_setup_done failed"); diff_tree_sha1(base, local, "", &opt); diffcore_std(&opt); for (i = 0; i < diff_queued_diff.nr; i++) { struct diff_filepair *p = diff_queued_diff.queue[i]; struct notes_merge_pair *mp; int match; unsigned char obj[20]; if (verify_notes_filepair(p, obj)) { trace_printf("\t\tCannot merge entry '%s' (%c): " "%.7s -> %.7s. Skipping!\n", p->one->path, p->status, sha1_to_hex(p->one->sha1), sha1_to_hex(p->two->sha1)); continue; } mp = find_notes_merge_pair_pos(changes, len, obj, 0, &match); if (!match) { trace_printf("\t\tIgnoring local-only change for %s: " "%.7s -> %.7s\n", sha1_to_hex(obj), sha1_to_hex(p->one->sha1), sha1_to_hex(p->two->sha1)); continue; } assert(!hashcmp(mp->obj, obj)); if (is_null_sha1(p->two->sha1)) { /* deletion */ /* * Either this is a true deletion (1), or it is part * of an A/D pair (2), or D/A pair (3): * * (1) mp->local is uninitialized; set it to null_sha1 * (2) mp->local is not uninitialized; don't touch it * (3) mp->local is uninitialized; set it to null_sha1 * (will be overwritten by following addition) */ if (!hashcmp(mp->local, uninitialized)) hashclr(mp->local); } else if (is_null_sha1(p->one->sha1)) { /* addition */ /* * Either this is a true addition (1), or it is part * of an A/D pair (2), or D/A pair (3): * * (1) mp->local is uninitialized; set to p->two->sha1 * (2) mp->local is uninitialized; set to p->two->sha1 * (3) mp->local is null_sha1; set to p->two->sha1 */ assert(is_null_sha1(mp->local) || !hashcmp(mp->local, uninitialized)); hashcpy(mp->local, p->two->sha1); } else { /* modification */ /* * This is a true modification. p->one->sha1 shall * match mp->base, and mp->local shall be uninitialized. * Set mp->local to p->two->sha1. */ assert(!hashcmp(p->one->sha1, mp->base)); assert(!hashcmp(mp->local, uninitialized)); hashcpy(mp->local, p->two->sha1); } trace_printf("\t\tStored local change for %s: %.7s -> %.7s\n", sha1_to_hex(mp->obj), sha1_to_hex(mp->base), sha1_to_hex(mp->local)); } diff_flush(&opt); diff_tree_release_paths(&opt); }
static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, const unsigned char *base, const unsigned char *remote, int *num_changes) { struct diff_options opt; struct notes_merge_pair *changes; int i, len = 0; trace_printf("\tdiff_tree_remote(base = %.7s, remote = %.7s)\n", sha1_to_hex(base), sha1_to_hex(remote)); diff_setup(&opt); DIFF_OPT_SET(&opt, RECURSIVE); opt.output_format = DIFF_FORMAT_NO_OUTPUT; if (diff_setup_done(&opt) < 0) die("diff_setup_done failed"); diff_tree_sha1(base, remote, "", &opt); diffcore_std(&opt); changes = xcalloc(diff_queued_diff.nr, sizeof(struct notes_merge_pair)); for (i = 0; i < diff_queued_diff.nr; i++) { struct diff_filepair *p = diff_queued_diff.queue[i]; struct notes_merge_pair *mp; int occupied; unsigned char obj[20]; if (verify_notes_filepair(p, obj)) { trace_printf("\t\tCannot merge entry '%s' (%c): " "%.7s -> %.7s. Skipping!\n", p->one->path, p->status, sha1_to_hex(p->one->sha1), sha1_to_hex(p->two->sha1)); continue; } mp = find_notes_merge_pair_pos(changes, len, obj, 1, &occupied); if (occupied) { /* We've found an addition/deletion pair */ assert(!hashcmp(mp->obj, obj)); if (is_null_sha1(p->one->sha1)) { /* addition */ assert(is_null_sha1(mp->remote)); hashcpy(mp->remote, p->two->sha1); } else if (is_null_sha1(p->two->sha1)) { /* deletion */ assert(is_null_sha1(mp->base)); hashcpy(mp->base, p->one->sha1); } else assert(!"Invalid existing change recorded"); } else { hashcpy(mp->obj, obj); hashcpy(mp->base, p->one->sha1); hashcpy(mp->local, uninitialized); hashcpy(mp->remote, p->two->sha1); len++; } trace_printf("\t\tStored remote change for %s: %.7s -> %.7s\n", sha1_to_hex(mp->obj), sha1_to_hex(mp->base), sha1_to_hex(mp->remote)); } diff_flush(&opt); diff_tree_release_paths(&opt); *num_changes = len; return changes; }
int cmd_diff(int argc, const char **argv, const char *prefix) { int i; struct rev_info rev; struct object_array ent = OBJECT_ARRAY_INIT; int blobs = 0, paths = 0; struct blobinfo blob[2]; int nongit = 0, no_index = 0; int result = 0; /* * We could get N tree-ish in the rev.pending_objects list. * Also there could be M blobs there, and P pathspecs. * * N=0, M=0: * cache vs files (diff-files) * N=0, M=2: * compare two random blobs. P must be zero. * N=0, M=1, P=1: * compare a blob with a working tree file. * * N=1, M=0: * tree vs cache (diff-index --cached) * * N=2, M=0: * tree vs tree (diff-tree) * * N=0, M=0, P=2: * compare two filesystem entities (aka --no-index). * * Other cases are errors. */ /* Were we asked to do --no-index explicitly? */ for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--")) { i++; break; } if (!strcmp(argv[i], "--no-index")) no_index = DIFF_NO_INDEX_EXPLICIT; if (argv[i][0] != '-') break; } if (!no_index) prefix = setup_git_directory_gently(&nongit); /* * Treat git diff with at least one path outside of the * repo the same as if the command would have been executed * outside of a git repository. In this case it behaves * the same way as "git diff --no-index <a> <b>", which acts * as a colourful "diff" replacement. */ if (nongit || ((argc == i + 2) && (!path_inside_repo(prefix, argv[i]) || !path_inside_repo(prefix, argv[i + 1])))) no_index = DIFF_NO_INDEX_IMPLICIT; if (!no_index) gitmodules_config(); init_diff_ui_defaults(); git_config(git_diff_ui_config, NULL); precompose_argv(argc, argv); init_revisions(&rev, prefix); if (no_index && argc != i + 2) { if (no_index == DIFF_NO_INDEX_IMPLICIT) { /* * There was no --no-index and there were not two * paths. It is possible that the user intended * to do an inside-repository operation. */ fprintf(stderr, "Not a git repository\n"); fprintf(stderr, "To compare two paths outside a working tree:\n"); } /* Give the usage message for non-repository usage and exit. */ usagef("git diff %s <path> <path>", no_index == DIFF_NO_INDEX_EXPLICIT ? "--no-index" : "[--no-index]"); } if (no_index) /* If this is a no-index diff, just run it and exit there. */ diff_no_index(&rev, argc, argv); /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; /* Scale to real terminal size and respect statGraphWidth config */ rev.diffopt.stat_width = -1; rev.diffopt.stat_graph_width = -1; /* Default to let external and textconv be used */ DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL); DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV); if (nongit) die(_("Not a git repository")); argc = setup_revisions(argc, argv, &rev, NULL); if (!rev.diffopt.output_format) { rev.diffopt.output_format = DIFF_FORMAT_PATCH; diff_setup_done(&rev.diffopt); } DIFF_OPT_SET(&rev.diffopt, RECURSIVE); setup_diff_pager(&rev.diffopt); /* * Do we have --cached and not have a pending object, then * default to HEAD by hand. Eek. */ if (!rev.pending.nr) { int i; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--")) break; else if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged")) { add_head_to_pending(&rev); if (!rev.pending.nr) { struct tree *tree; tree = lookup_tree(EMPTY_TREE_SHA1_BIN); add_pending_object(&rev, &tree->object, "HEAD"); } break; } } } for (i = 0; i < rev.pending.nr; i++) { struct object_array_entry *entry = &rev.pending.objects[i]; struct object *obj = entry->item; const char *name = entry->name; int flags = (obj->flags & UNINTERESTING); if (!obj->parsed) obj = parse_object(obj->oid.hash); obj = deref_tag(obj, NULL, 0); if (!obj) die(_("invalid object '%s' given."), name); if (obj->type == OBJ_COMMIT) obj = &((struct commit *)obj)->tree->object; if (obj->type == OBJ_TREE) { obj->flags |= flags; add_object_array(obj, name, &ent); } else if (obj->type == OBJ_BLOB) { if (2 <= blobs) die(_("more than two blobs given: '%s'"), name); hashcpy(blob[blobs].sha1, obj->oid.hash); blob[blobs].name = name; blob[blobs].mode = entry->mode; blobs++; } else { die(_("unhandled object '%s' given."), name); } } if (rev.prune_data.nr) paths += rev.prune_data.nr; /* * Now, do the arguments look reasonable? */ if (!ent.nr) { switch (blobs) { case 0: result = builtin_diff_files(&rev, argc, argv); break; case 1: if (paths != 1) usage(builtin_diff_usage); result = builtin_diff_b_f(&rev, argc, argv, blob); break; case 2: if (paths) usage(builtin_diff_usage); result = builtin_diff_blobs(&rev, argc, argv, blob); break; default: usage(builtin_diff_usage); } } else if (blobs) usage(builtin_diff_usage); else if (ent.nr == 1) result = builtin_diff_index(&rev, argc, argv); else if (ent.nr == 2) result = builtin_diff_tree(&rev, argc, argv, &ent.objects[0], &ent.objects[1]); else if (ent.objects[0].item->flags & UNINTERESTING) { /* * diff A...B where there is at least one merge base * between A and B. We have ent.objects[0] == * merge-base, ent.objects[ents-2] == A, and * ent.objects[ents-1] == B. Show diff between the * base and B. Note that we pick one merge base at * random if there are more than one. */ result = builtin_diff_tree(&rev, argc, argv, &ent.objects[0], &ent.objects[ent.nr-1]); } else result = builtin_diff_combined(&rev, argc, argv, ent.objects, ent.nr); result = diff_result_code(&rev.diffopt, result); if (1 < rev.diffopt.skip_stat_unmatch) refresh_index_quietly(); return result; }
static void make_cover_letter(struct rev_info *rev, int use_stdout, int numbered, int numbered_files, struct commit *origin, int nr, struct commit **list, struct commit *head) { const char *committer; char *head_sha1; const char *subject_start = NULL; const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n"; const char *msg; const char *extra_headers = rev->extra_headers; struct shortlog log; struct strbuf sb = STRBUF_INIT; int i; const char *encoding = "utf-8"; struct diff_options opts; int need_8bit_cte = 0; if (rev->commit_format != CMIT_FMT_EMAIL) die("Cover letter needs email format"); if (!use_stdout && reopen_stdout(numbered_files ? NULL : "cover-letter", 0, rev->total)) return; head_sha1 = sha1_to_hex(head->object.sha1); log_write_email_headers(rev, head_sha1, &subject_start, &extra_headers, &need_8bit_cte); committer = git_committer_info(0); msg = body; pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822, encoding); pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers, encoding, need_8bit_cte); pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0); printf("%s\n", sb.buf); strbuf_release(&sb); shortlog_init(&log); log.wrap_lines = 1; log.wrap = 72; log.in1 = 2; log.in2 = 4; for (i = 0; i < nr; i++) shortlog_add_commit(&log, list[i]); shortlog_output(&log); /* * We can only do diffstat with a unique reference point */ if (!origin) return; memcpy(&opts, &rev->diffopt, sizeof(opts)); opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; diff_setup_done(&opts); diff_tree_sha1(origin->tree->object.sha1, head->tree->object.sha1, "", &opts); diffcore_std(&opts); diff_flush(&opts); printf("\n"); }