static int diff_tree_commit_oid(const struct object_id *oid) { struct commit *commit = lookup_commit_reference(oid); if (!commit) return -1; return log_tree_commit(&log_tree_opt, commit); }
static int cmd_log_walk(struct rev_info *rev) { struct commit *commit; if (rev->early_output) setup_early_output(rev); if (prepare_revision_walk(rev)) die("revision walk setup failed"); if (rev->early_output) finish_early_output(rev); /* * For --check and --exit-code, the exit code is based on CHECK_FAILED * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to * retain that state information if replacing rev->diffopt in this loop */ while ((commit = get_revision(rev)) != NULL) { log_tree_commit(rev, commit); if (!rev->reflog_info) { /* we allow cycles in reflog ancestry */ free(commit->buffer); commit->buffer = NULL; } free_commit_list(commit->parents); commit->parents = NULL; } if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF && DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) { return 02; } return diff_result_code(&rev->diffopt, 0); }
/* Diff one or more commits. */ static int stdin_diff_commit(struct commit *commit, char *line, int len) { unsigned char sha1[20]; if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) { /* Graft the fake parents locally to the commit */ int pos = 41; struct commit_list **pptr, *parents; /* Free the real parent list */ for (parents = commit->parents; parents; ) { struct commit_list *tmp = parents->next; free(parents); parents = tmp; } commit->parents = NULL; pptr = &(commit->parents); while (line[pos] && !get_sha1_hex(line + pos, sha1)) { struct commit *parent = lookup_commit(sha1); if (parent) { pptr = &commit_list_insert(parent, pptr)->next; } pos += 41; } } return log_tree_commit(&log_tree_opt, commit); }
static int diff_tree_commit_sha1(const unsigned char *sha1) { struct commit *commit = lookup_commit_reference(sha1); if (!commit) return -1; return log_tree_commit(&log_tree_opt, commit); }
int git_get_log_nextcommit(GIT_LOG handle, GIT_COMMIT *commit, int follow) { int ret =0; if(commit == NULL) return -1; memset(commit, 0, sizeof(GIT_COMMIT)); commit->m_pGitCommit = get_revision(handle); if( commit->m_pGitCommit == NULL) return -2; if (follow && !log_tree_commit(handle, commit->m_pGitCommit)) { commit->m_ignore = 1; return 0; } commit->m_ignore = 0; ret=git_parse_commit(commit); if(ret) return ret; return 0; }
static int cmd_log_walk(struct rev_info *rev) { struct commit *commit; if (rev->early_output) setup_early_output(rev); if (prepare_revision_walk(rev)) die("revision walk setup failed"); if (rev->early_output) finish_early_output(rev); while ((commit = get_revision(rev)) != NULL) { log_tree_commit(rev, commit); if (!rev->reflog_info) { /* we allow cycles in reflog ancestry */ free(commit->buffer); commit->buffer = NULL; } free_commit_list(commit->parents); commit->parents = NULL; } return 0; }
static int cmd_log_walk(struct rev_info *rev) { struct commit *commit; int saved_nrl = 0; int saved_dcctc = 0; if (rev->early_output) setup_early_output(rev); if (prepare_revision_walk(rev)) die(_("revision walk setup failed")); if (rev->early_output) finish_early_output(rev); /* * For --check and --exit-code, the exit code is based on CHECK_FAILED * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to * retain that state information if replacing rev->diffopt in this loop */ while ((commit = get_revision(rev)) != NULL) { if (!log_tree_commit(rev, commit) && rev->max_count >= 0) /* * We decremented max_count in get_revision, * but we didn't actually show the commit. */ rev->max_count++; if (!rev->reflog_info) { /* we allow cycles in reflog ancestry */ free(commit->buffer); commit->buffer = NULL; } free_commit_list(commit->parents); commit->parents = NULL; if (saved_nrl < rev->diffopt.needed_rename_limit) saved_nrl = rev->diffopt.needed_rename_limit; if (rev->diffopt.degraded_cc_to_c) saved_dcctc = 1; } rev->diffopt.degraded_cc_to_c = saved_dcctc; rev->diffopt.needed_rename_limit = saved_nrl; if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF && DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) { return 02; } return diff_result_code(&rev->diffopt, 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 log_show_early(struct rev_info *revs, struct commit_list *list) { int i = revs->early_output; int show_header = 1; sort_in_topological_order(&list, revs->lifo); while (list && i) { struct commit *commit = list->item; switch (simplify_commit(revs, commit)) { case commit_show: if (show_header) { int n = estimate_commit_count(revs, list); show_early_header(revs, "incomplete", n); show_header = 0; } log_tree_commit(revs, commit); i--; break; case commit_ignore: break; case commit_error: return; } list = list->next; } /* Did we already get enough commits for the early output? */ if (!i) return; /* * ..if no, then repeat it twice a second until we * do. * * NOTE! We don't use "it_interval", because if the * reader isn't listening, we want our output to be * throttled by the writing, and not have the timer * trigger every second even if we're blocked on a * reader! */ early_output_timer.it_value.tv_sec = 0; early_output_timer.it_value.tv_usec = 500000; setitimer(ITIMER_REAL, &early_output_timer, NULL); }
/* Diff one or more commits. */ static int stdin_diff_commit(struct commit *commit, const char *p) { struct object_id oid; struct commit_list **pptr = NULL; /* Graft the fake parents locally to the commit */ while (isspace(*p++) && !parse_oid_hex(p, &oid, &p)) { struct commit *parent = lookup_commit(&oid); if (!pptr) { /* Free the real parent list */ free_commit_list(commit->parents); commit->parents = NULL; pptr = &(commit->parents); } if (parent) { pptr = &commit_list_insert(parent, pptr)->next; } } return log_tree_commit(&log_tree_opt, commit); }
/* * This does "git diff-tree --pretty COMMIT" without one fork+exec. */ static void show_diff_tree(const char *prefix, struct commit *commit) { struct rev_info opt; /* diff-tree init */ init_revisions(&opt, prefix); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ opt.abbrev = 0; opt.diff = 1; /* This is what "--pretty" does */ opt.verbose_header = 1; opt.use_terminator = 0; opt.commit_format = CMIT_FMT_DEFAULT; /* diff-tree init */ if (!opt.diffopt.output_format) opt.diffopt.output_format = DIFF_FORMAT_RAW; log_tree_commit(&opt, commit); }
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); } }
int cmd_format_patch(int argc, const char **argv, const char *prefix) { struct commit *commit; struct commit **list = NULL; struct rev_info rev; struct setup_revision_opt s_r_opt; int nr = 0, total, i; int use_stdout = 0; int start_number = -1; int just_numbers = 0; int ignore_if_in_upstream = 0; int cover_letter = -1; int boundary_count = 0; int no_binary_diff = 0; struct commit *origin = NULL; const char *in_reply_to = NULL; struct patch_ids ids; struct strbuf buf = STRBUF_INIT; int use_patch_format = 0; int quiet = 0; int reroll_count = -1; char *branch_name = NULL; char *from = NULL; const struct option builtin_format_patch_options[] = { { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL, N_("use [PATCH n/m] even with a single patch"), PARSE_OPT_NOARG, numbered_callback }, { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL, N_("use [PATCH] even with multiple patches"), PARSE_OPT_NOARG, no_numbered_callback }, OPT_BOOL('s', "signoff", &do_signoff, N_("add Signed-off-by:")), OPT_BOOL(0, "stdout", &use_stdout, N_("print patches to standard out")), OPT_BOOL(0, "cover-letter", &cover_letter, N_("generate a cover letter")), OPT_BOOL(0, "numbered-files", &just_numbers, N_("use simple number sequence for output file names")), OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"), N_("use <sfx> instead of '.patch'")), OPT_INTEGER(0, "start-number", &start_number, N_("start numbering patches at <n> instead of 1")), OPT_INTEGER('v', "reroll-count", &reroll_count, N_("mark the series as Nth re-roll")), { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"), N_("Use [<prefix>] instead of [PATCH]"), PARSE_OPT_NONEG, subject_prefix_callback }, { OPTION_CALLBACK, 'o', "output-directory", &output_directory, N_("dir"), N_("store resulting files in <dir>"), PARSE_OPT_NONEG, output_directory_callback }, { OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL, N_("don't strip/add [PATCH]"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback }, OPT_BOOL(0, "no-binary", &no_binary_diff, N_("don't output binary diffs")), OPT_BOOL(0, "ignore-if-in-upstream", &ignore_if_in_upstream, N_("don't include a patch matching a commit upstream")), { OPTION_SET_INT, 'p', "no-stat", &use_patch_format, NULL, N_("show patch format instead of default (patch + stat)"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1}, OPT_GROUP(N_("Messaging")), { OPTION_CALLBACK, 0, "add-header", NULL, N_("header"), N_("add email header"), 0, header_callback }, { OPTION_CALLBACK, 0, "to", NULL, N_("email"), N_("add To: header"), 0, to_callback }, { OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"), 0, cc_callback }, { OPTION_CALLBACK, 0, "from", &from, N_("ident"), N_("set From address to <ident> (or committer ident if absent)"), PARSE_OPT_OPTARG, from_callback }, OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"), N_("make first mail a reply to <message-id>")), { OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"), N_("attach the patch"), PARSE_OPT_OPTARG, attach_callback }, { OPTION_CALLBACK, 0, "inline", &rev, N_("boundary"), N_("inline the patch"), PARSE_OPT_OPTARG | PARSE_OPT_NONEG, inline_callback }, { OPTION_CALLBACK, 0, "thread", &thread, N_("style"), N_("enable message threading, styles: shallow, deep"), PARSE_OPT_OPTARG, thread_callback }, OPT_STRING(0, "signature", &signature, N_("signature"), N_("add a signature")), OPT_FILENAME(0, "signature-file", &signature_file, N_("add a signature from a file")), OPT__QUIET(&quiet, N_("don't print the patch filenames")), OPT_END() }; extra_hdr.strdup_strings = 1; extra_to.strdup_strings = 1; extra_cc.strdup_strings = 1; init_grep_defaults(); git_config(git_format_config, NULL); init_revisions(&rev, prefix); rev.commit_format = CMIT_FMT_EMAIL; rev.verbose_header = 1; rev.diff = 1; rev.max_parents = 1; DIFF_OPT_SET(&rev.diffopt, RECURSIVE); rev.subject_prefix = fmt_patch_subject_prefix; memset(&s_r_opt, 0, sizeof(s_r_opt)); s_r_opt.def = "HEAD"; s_r_opt.revarg_opt = REVARG_COMMITTISH; if (default_attach) { rev.mime_boundary = default_attach; rev.no_inline = 1; } /* * Parse the arguments before setup_revisions(), or something * like "git format-patch -o a123 HEAD^.." may fail; a123 is * possibly a valid SHA1. */ argc = parse_options(argc, argv, prefix, builtin_format_patch_options, builtin_format_patch_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); if (0 < reroll_count) { struct strbuf sprefix = STRBUF_INIT; strbuf_addf(&sprefix, "%s v%d", rev.subject_prefix, reroll_count); rev.reroll_count = reroll_count; rev.subject_prefix = strbuf_detach(&sprefix, NULL); } for (i = 0; i < extra_hdr.nr; i++) { strbuf_addstr(&buf, extra_hdr.items[i].string); strbuf_addch(&buf, '\n'); } if (extra_to.nr) strbuf_addstr(&buf, "To: "); for (i = 0; i < extra_to.nr; i++) { if (i) strbuf_addstr(&buf, " "); strbuf_addstr(&buf, extra_to.items[i].string); if (i + 1 < extra_to.nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } if (extra_cc.nr) strbuf_addstr(&buf, "Cc: "); for (i = 0; i < extra_cc.nr; i++) { if (i) strbuf_addstr(&buf, " "); strbuf_addstr(&buf, extra_cc.items[i].string); if (i + 1 < extra_cc.nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } rev.extra_headers = strbuf_detach(&buf, NULL); if (from) { if (split_ident_line(&rev.from_ident, from, strlen(from))) die(_("invalid ident line: %s"), from); } if (start_number < 0) start_number = 1; /* * If numbered is set solely due to format.numbered in config, * and it would conflict with --keep-subject (-k) from the * command line, reset "numbered". */ if (numbered && keep_subject && !numbered_cmdline_opt) numbered = 0; if (numbered && keep_subject) die (_("-n and -k are mutually exclusive.")); if (keep_subject && subject_prefix) die (_("--subject-prefix and -k are mutually exclusive.")); rev.preserve_subject = keep_subject; argc = setup_revisions(argc, argv, &rev, &s_r_opt); if (argc > 1) die (_("unrecognized argument: %s"), argv[1]); if (rev.diffopt.output_format & DIFF_FORMAT_NAME) die(_("--name-only does not make sense")); if (rev.diffopt.output_format & DIFF_FORMAT_NAME_STATUS) die(_("--name-status does not make sense")); if (rev.diffopt.output_format & DIFF_FORMAT_CHECKDIFF) die(_("--check does not make sense")); if (!use_patch_format && (!rev.diffopt.output_format || rev.diffopt.output_format == DIFF_FORMAT_PATCH)) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY; /* Always generate a patch */ rev.diffopt.output_format |= DIFF_FORMAT_PATCH; if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff) DIFF_OPT_SET(&rev.diffopt, BINARY); if (rev.show_notes) init_display_notes(&rev.notes_opt); if (!use_stdout) output_directory = set_outdir(prefix, output_directory); else setup_pager(); if (output_directory) { if (use_stdout) die(_("standard output, or directory, which one?")); if (mkdir(output_directory, 0777) < 0 && errno != EEXIST) die_errno(_("Could not create directory '%s'"), output_directory); } if (rev.pending.nr == 1) { int check_head = 0; if (rev.max_count < 0 && !rev.show_root_diff) { /* * This is traditional behaviour of "git format-patch * origin" that prepares what the origin side still * does not have. */ rev.pending.objects[0].item->flags |= UNINTERESTING; add_head_to_pending(&rev); check_head = 1; } /* * Otherwise, it is "format-patch -22 HEAD", and/or * "format-patch --root HEAD". The user wants * get_revision() to do the usual traversal. */ if (!strcmp(rev.pending.objects[0].name, "HEAD")) check_head = 1; if (check_head) { unsigned char sha1[20]; const char *ref, *v; ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, sha1, NULL); if (ref && skip_prefix(ref, "refs/heads/", &v)) branch_name = xstrdup(v); else branch_name = xstrdup(""); /* no branch */ } } /* * We cannot move this anywhere earlier because we do want to * know if --root was given explicitly from the command line. */ rev.show_root_diff = 1; if (ignore_if_in_upstream) { /* Don't say anything if head and upstream are the same. */ if (rev.pending.nr == 2) { struct object_array_entry *o = rev.pending.objects; if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0) return 0; } get_patch_ids(&rev, &ids); } if (!use_stdout) realstdout = xfdopen(xdup(1), "w"); if (prepare_revision_walk(&rev)) die(_("revision walk setup failed")); rev.boundary = 1; while ((commit = get_revision(&rev)) != NULL) { if (commit->object.flags & BOUNDARY) { boundary_count++; origin = (boundary_count == 1) ? commit : NULL; continue; } if (ignore_if_in_upstream && has_commit_patch_id(commit, &ids)) continue; nr++; REALLOC_ARRAY(list, nr); list[nr - 1] = commit; } if (nr == 0) /* nothing to do */ return 0; total = nr; if (!keep_subject && auto_number && total > 1) numbered = 1; if (numbered) rev.total = total + start_number - 1; if (cover_letter == -1) { if (config_cover_letter == COVER_AUTO) cover_letter = (total > 1); else cover_letter = (config_cover_letter == COVER_ON); } if (!signature) { ; /* --no-signature inhibits all signatures */ } else if (signature && signature != git_version_string) { ; /* non-default signature already set */ } else if (signature_file) { struct strbuf buf = STRBUF_INIT; if (strbuf_read_file(&buf, signature_file, 128) < 0) die_errno(_("unable to read signature file '%s'"), signature_file); signature = strbuf_detach(&buf, NULL); } if (in_reply_to || thread || cover_letter) rev.ref_message_ids = xcalloc(1, sizeof(struct string_list)); if (in_reply_to) { const char *msgid = clean_message_id(in_reply_to); string_list_append(rev.ref_message_ids, msgid); } rev.numbered_files = just_numbers; rev.patch_suffix = fmt_patch_suffix; if (cover_letter) { if (thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, use_stdout, origin, nr, list, branch_name, quiet); total++; start_number--; } rev.add_signoff = do_signoff; while (0 <= --nr) { int shown; commit = list[nr]; rev.nr = total - nr + (start_number - 1); /* Make the second and subsequent mails replies to the first */ if (thread) { /* Have we already had a message ID? */ if (rev.message_id) { /* * For deep threading: make every mail * a reply to the previous one, no * matter what other options are set. * * For shallow threading: * * Without --cover-letter and * --in-reply-to, make every mail a * reply to the one before. * * With --in-reply-to but no * --cover-letter, make every mail a * reply to the <reply-to>. * * With --cover-letter, make every * mail but the cover letter a reply * to the cover letter. The cover * letter is a reply to the * --in-reply-to, if specified. */ if (thread == THREAD_SHALLOW && rev.ref_message_ids->nr > 0 && (!cover_letter || rev.nr > 1)) free(rev.message_id); else string_list_append(rev.ref_message_ids, rev.message_id); } gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } if (!use_stdout && reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet)) die(_("Failed to create output files")); shown = log_tree_commit(&rev, commit); free_commit_buffer(commit); /* We put one extra blank line between formatted * patches and this flag is used by log-tree code * to see if it needs to emit a LF before showing * the log; when using one file per patch, we do * not want the extra blank line. */ if (!use_stdout) rev.shown_one = 0; if (shown) { if (rev.mime_boundary) printf("\n--%s%s--\n\n\n", mime_boundary_leader, rev.mime_boundary); else print_signature(); } if (!use_stdout) fclose(stdout); } free(list); free(branch_name); string_list_clear(&extra_to, 0); string_list_clear(&extra_cc, 0); string_list_clear(&extra_hdr, 0); if (ignore_if_in_upstream) free_patch_ids(&ids); return 0; }
int cmd_format_patch(int argc, const char **argv, const char *prefix) { struct commit *commit; struct commit **list = NULL; struct rev_info rev; int nr = 0, total, i, j; int use_stdout = 0; int start_number = -1; int keep_subject = 0; int numbered_files = 0; /* _just_ numbers */ int subject_prefix = 0; int ignore_if_in_upstream = 0; int thread = 0; int cover_letter = 0; int boundary_count = 0; int no_binary_diff = 0; struct commit *origin = NULL, *head = NULL; const char *in_reply_to = NULL; struct patch_ids ids; char *add_signoff = NULL; struct strbuf buf = STRBUF_INIT; git_config(git_format_config, NULL); init_revisions(&rev, prefix); rev.commit_format = CMIT_FMT_EMAIL; rev.verbose_header = 1; rev.diff = 1; rev.combine_merges = 0; rev.ignore_merges = 1; DIFF_OPT_SET(&rev.diffopt, RECURSIVE); rev.subject_prefix = fmt_patch_subject_prefix; /* * Parse the arguments before setup_revisions(), or something * like "git format-patch -o a123 HEAD^.." may fail; a123 is * possibly a valid SHA1. */ for (i = 1, j = 1; i < argc; i++) { if (!strcmp(argv[i], "--stdout")) use_stdout = 1; else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--numbered")) numbered = 1; else if (!strcmp(argv[i], "-N") || !strcmp(argv[i], "--no-numbered")) { numbered = 0; auto_number = 0; } else if (!prefixcmp(argv[i], "--start-number=")) start_number = strtol(argv[i] + 15, NULL, 10); else if (!strcmp(argv[i], "--numbered-files")) numbered_files = 1; else if (!strcmp(argv[i], "--start-number")) { i++; if (i == argc) die("Need a number for --start-number"); start_number = strtol(argv[i], NULL, 10); } else if (!prefixcmp(argv[i], "--cc=")) { ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc); extra_cc[extra_cc_nr++] = xstrdup(argv[i] + 5); } else if (!strcmp(argv[i], "-k") || !strcmp(argv[i], "--keep-subject")) { keep_subject = 1; rev.total = -1; } else if (!strcmp(argv[i], "--output-directory") || !strcmp(argv[i], "-o")) { i++; if (argc <= i) die("Which directory?"); if (output_directory) die("Two output directories?"); output_directory = argv[i]; } else if (!strcmp(argv[i], "--signoff") || !strcmp(argv[i], "-s")) { const char *committer; const char *endpos; committer = git_committer_info(IDENT_ERROR_ON_NO_NAME); endpos = strchr(committer, '>'); if (!endpos) die("bogus committer info %s\n", committer); add_signoff = xmemdupz(committer, endpos - committer + 1); } else if (!strcmp(argv[i], "--attach")) { rev.mime_boundary = git_version_string; rev.no_inline = 1; } else if (!prefixcmp(argv[i], "--attach=")) { rev.mime_boundary = argv[i] + 9; rev.no_inline = 1; } else if (!strcmp(argv[i], "--inline")) { rev.mime_boundary = git_version_string; rev.no_inline = 0; } else if (!prefixcmp(argv[i], "--inline=")) { rev.mime_boundary = argv[i] + 9; rev.no_inline = 0; } else if (!strcmp(argv[i], "--ignore-if-in-upstream")) ignore_if_in_upstream = 1; else if (!strcmp(argv[i], "--thread")) thread = 1; else if (!prefixcmp(argv[i], "--in-reply-to=")) in_reply_to = argv[i] + 14; else if (!strcmp(argv[i], "--in-reply-to")) { i++; if (i == argc) die("Need a Message-Id for --in-reply-to"); in_reply_to = argv[i]; } else if (!prefixcmp(argv[i], "--subject-prefix=")) { subject_prefix = 1; rev.subject_prefix = argv[i] + 17; } else if (!prefixcmp(argv[i], "--suffix=")) fmt_patch_suffix = argv[i] + 9; else if (!strcmp(argv[i], "--cover-letter")) cover_letter = 1; else if (!strcmp(argv[i], "--no-binary")) no_binary_diff = 1; else argv[j++] = argv[i]; } argc = j; for (i = 0; i < extra_hdr_nr; i++) { strbuf_addstr(&buf, extra_hdr[i]); strbuf_addch(&buf, '\n'); } if (extra_to_nr) strbuf_addstr(&buf, "To: "); for (i = 0; i < extra_to_nr; i++) { if (i) strbuf_addstr(&buf, " "); strbuf_addstr(&buf, extra_to[i]); if (i + 1 < extra_to_nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } if (extra_cc_nr) strbuf_addstr(&buf, "Cc: "); for (i = 0; i < extra_cc_nr; i++) { if (i) strbuf_addstr(&buf, " "); strbuf_addstr(&buf, extra_cc[i]); if (i + 1 < extra_cc_nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } rev.extra_headers = strbuf_detach(&buf, 0); if (start_number < 0) start_number = 1; if (numbered && keep_subject) die ("-n and -k are mutually exclusive."); if (keep_subject && subject_prefix) die ("--subject-prefix and -k are mutually exclusive."); if (numbered_files && use_stdout) die ("--numbered-files and --stdout are mutually exclusive."); argc = setup_revisions(argc, argv, &rev, "HEAD"); if (argc > 1) die ("unrecognized argument: %s", argv[1]); if (!rev.diffopt.output_format || rev.diffopt.output_format == DIFF_FORMAT_PATCH) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH; if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff) DIFF_OPT_SET(&rev.diffopt, BINARY); if (!output_directory && !use_stdout) output_directory = prefix; if (output_directory) { if (use_stdout) die("standard output, or directory, which one?"); if (mkdir(output_directory, 0777) < 0 && errno != EEXIST) die("Could not create directory %s", output_directory); } if (rev.pending.nr == 1) { if (rev.max_count < 0 && !rev.show_root_diff) { /* * This is traditional behaviour of "git format-patch * origin" that prepares what the origin side still * does not have. */ rev.pending.objects[0].item->flags |= UNINTERESTING; add_head_to_pending(&rev); } /* * Otherwise, it is "format-patch -22 HEAD", and/or * "format-patch --root HEAD". The user wants * get_revision() to do the usual traversal. */ } if (cover_letter) { /* remember the range */ int i; for (i = 0; i < rev.pending.nr; i++) { struct object *o = rev.pending.objects[i].item; if (!(o->flags & UNINTERESTING)) head = (struct commit *)o; } /* We can't generate a cover letter without any patches */ if (!head) return 0; } if (ignore_if_in_upstream) get_patch_ids(&rev, &ids, prefix); if (!use_stdout) realstdout = xfdopen(xdup(1), "w"); if (prepare_revision_walk(&rev)) die("revision walk setup failed"); rev.boundary = 1; while ((commit = get_revision(&rev)) != NULL) { if (commit->object.flags & BOUNDARY) { boundary_count++; origin = (boundary_count == 1) ? commit : NULL; continue; } /* ignore merges */ if (commit->parents && commit->parents->next) continue; if (ignore_if_in_upstream && has_commit_patch_id(commit, &ids)) continue; nr++; list = xrealloc(list, nr * sizeof(list[0])); list[nr - 1] = commit; } total = nr; if (!keep_subject && auto_number && total > 1) numbered = 1; if (numbered) rev.total = total + start_number - 1; if (in_reply_to) rev.ref_message_id = clean_message_id(in_reply_to); if (cover_letter) { if (thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, use_stdout, numbered, numbered_files, origin, nr, list, head); total++; start_number--; } rev.add_signoff = add_signoff; while (0 <= --nr) { int shown; commit = list[nr]; rev.nr = total - nr + (start_number - 1); /* Make the second and subsequent mails replies to the first */ if (thread) { /* Have we already had a message ID? */ if (rev.message_id) { /* * If we've got the ID to be a reply * to, discard the current ID; * otherwise, make everything a reply * to that. */ if (rev.ref_message_id) free(rev.message_id); else rev.ref_message_id = rev.message_id; } gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } if (!use_stdout && reopen_stdout(numbered_files ? NULL : get_oneline_for_filename(commit, keep_subject), rev.nr, rev.total)) die("Failed to create output files"); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; /* We put one extra blank line between formatted * patches and this flag is used by log-tree code * to see if it needs to emit a LF before showing * the log; when using one file per patch, we do * not want the extra blank line. */ if (!use_stdout) rev.shown_one = 0; if (shown) { if (rev.mime_boundary) printf("\n--%s%s--\n\n\n", mime_boundary_leader, rev.mime_boundary); else printf("-- \n%s\n\n", git_version_string); } if (!use_stdout) fclose(stdout); } free(list); if (ignore_if_in_upstream) free_patch_ids(&ids); return 0; }
int cmd_format_patch(int argc, const char **argv, const char *prefix) { struct commit *commit; struct commit **list = NULL; struct rev_info rev; int nr = 0, total, i; int use_stdout = 0; int start_number = -1; int numbered_files = 0; /* _just_ numbers */ int ignore_if_in_upstream = 0; int cover_letter = 0; int boundary_count = 0; int no_binary_diff = 0; struct commit *origin = NULL, *head = NULL; const char *in_reply_to = NULL; struct patch_ids ids; char *add_signoff = NULL; struct strbuf buf = STRBUF_INIT; int use_patch_format = 0; const struct option builtin_format_patch_options[] = { { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL, "use [PATCH n/m] even with a single patch", PARSE_OPT_NOARG, numbered_callback }, { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL, "use [PATCH] even with multiple patches", PARSE_OPT_NOARG, no_numbered_callback }, OPT_BOOLEAN('s', "signoff", &do_signoff, "add Signed-off-by:"), OPT_BOOLEAN(0, "stdout", &use_stdout, "print patches to standard out"), OPT_BOOLEAN(0, "cover-letter", &cover_letter, "generate a cover letter"), OPT_BOOLEAN(0, "numbered-files", &numbered_files, "use simple number sequence for output file names"), OPT_STRING(0, "suffix", &fmt_patch_suffix, "sfx", "use <sfx> instead of '.patch'"), OPT_INTEGER(0, "start-number", &start_number, "start numbering patches at <n> instead of 1"), { OPTION_CALLBACK, 0, "subject-prefix", &rev, "prefix", "Use [<prefix>] instead of [PATCH]", PARSE_OPT_NONEG, subject_prefix_callback }, { OPTION_CALLBACK, 'o', "output-directory", &output_directory, "dir", "store resulting files in <dir>", PARSE_OPT_NONEG, output_directory_callback }, { OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL, "don't strip/add [PATCH]", PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback }, OPT_BOOLEAN(0, "no-binary", &no_binary_diff, "don't output binary diffs"), OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream, "don't include a patch matching a commit upstream"), { OPTION_BOOLEAN, 'p', "no-stat", &use_patch_format, NULL, "show patch format instead of default (patch + stat)", PARSE_OPT_NONEG | PARSE_OPT_NOARG }, OPT_GROUP("Messaging"), { OPTION_CALLBACK, 0, "add-header", NULL, "header", "add email header", PARSE_OPT_NONEG, header_callback }, { OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header", PARSE_OPT_NONEG, cc_callback }, OPT_STRING(0, "in-reply-to", &in_reply_to, "message-id", "make first mail a reply to <message-id>"), { OPTION_CALLBACK, 0, "attach", &rev, "boundary", "attach the patch", PARSE_OPT_OPTARG, attach_callback }, { OPTION_CALLBACK, 0, "inline", &rev, "boundary", "inline the patch", PARSE_OPT_OPTARG | PARSE_OPT_NONEG, inline_callback }, { OPTION_CALLBACK, 0, "thread", &thread, "style", "enable message threading, styles: shallow, deep", PARSE_OPT_OPTARG, thread_callback }, OPT_END() }; git_config(git_format_config, NULL); init_revisions(&rev, prefix); rev.commit_format = CMIT_FMT_EMAIL; rev.verbose_header = 1; rev.diff = 1; rev.combine_merges = 0; rev.ignore_merges = 1; DIFF_OPT_SET(&rev.diffopt, RECURSIVE); rev.subject_prefix = fmt_patch_subject_prefix; if (default_attach) { rev.mime_boundary = default_attach; rev.no_inline = 1; } /* * Parse the arguments before setup_revisions(), or something * like "git format-patch -o a123 HEAD^.." may fail; a123 is * possibly a valid SHA1. */ argc = parse_options(argc, argv, prefix, builtin_format_patch_options, builtin_format_patch_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH); if (do_signoff) { const char *committer; const char *endpos; committer = git_committer_info(IDENT_ERROR_ON_NO_NAME); endpos = strchr(committer, '>'); if (!endpos) die("bogus committer info %s", committer); add_signoff = xmemdupz(committer, endpos - committer + 1); } for (i = 0; i < extra_hdr_nr; i++) { strbuf_addstr(&buf, extra_hdr[i]); strbuf_addch(&buf, '\n'); } if (extra_to_nr) strbuf_addstr(&buf, "To: "); for (i = 0; i < extra_to_nr; i++) { if (i) strbuf_addstr(&buf, " "); strbuf_addstr(&buf, extra_to[i]); if (i + 1 < extra_to_nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } if (extra_cc_nr) strbuf_addstr(&buf, "Cc: "); for (i = 0; i < extra_cc_nr; i++) { if (i) strbuf_addstr(&buf, " "); strbuf_addstr(&buf, extra_cc[i]); if (i + 1 < extra_cc_nr) strbuf_addch(&buf, ','); strbuf_addch(&buf, '\n'); } rev.extra_headers = strbuf_detach(&buf, NULL); if (start_number < 0) start_number = 1; /* * If numbered is set solely due to format.numbered in config, * and it would conflict with --keep-subject (-k) from the * command line, reset "numbered". */ if (numbered && keep_subject && !numbered_cmdline_opt) numbered = 0; if (numbered && keep_subject) die ("-n and -k are mutually exclusive."); if (keep_subject && subject_prefix) die ("--subject-prefix and -k are mutually exclusive."); argc = setup_revisions(argc, argv, &rev, "HEAD"); if (argc > 1) die ("unrecognized argument: %s", argv[1]); if (rev.diffopt.output_format & DIFF_FORMAT_NAME) die("--name-only does not make sense"); if (rev.diffopt.output_format & DIFF_FORMAT_NAME_STATUS) die("--name-status does not make sense"); if (rev.diffopt.output_format & DIFF_FORMAT_CHECKDIFF) die("--check does not make sense"); if (!use_patch_format && (!rev.diffopt.output_format || rev.diffopt.output_format == DIFF_FORMAT_PATCH)) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY; /* Always generate a patch */ rev.diffopt.output_format |= DIFF_FORMAT_PATCH; if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff) DIFF_OPT_SET(&rev.diffopt, BINARY); if (!use_stdout) output_directory = set_outdir(prefix, output_directory); if (output_directory) { if (use_stdout) die("standard output, or directory, which one?"); if (mkdir(output_directory, 0777) < 0 && errno != EEXIST) die_errno("Could not create directory '%s'", output_directory); } if (rev.pending.nr == 1) { if (rev.max_count < 0 && !rev.show_root_diff) { /* * This is traditional behaviour of "git format-patch * origin" that prepares what the origin side still * does not have. */ rev.pending.objects[0].item->flags |= UNINTERESTING; add_head_to_pending(&rev); } /* * Otherwise, it is "format-patch -22 HEAD", and/or * "format-patch --root HEAD". The user wants * get_revision() to do the usual traversal. */ } /* * We cannot move this anywhere earlier because we do want to * know if --root was given explicitly from the comand line. */ rev.show_root_diff = 1; if (cover_letter) { /* remember the range */ int i; for (i = 0; i < rev.pending.nr; i++) { struct object *o = rev.pending.objects[i].item; if (!(o->flags & UNINTERESTING)) head = (struct commit *)o; } /* We can't generate a cover letter without any patches */ if (!head) return 0; } if (ignore_if_in_upstream) get_patch_ids(&rev, &ids, prefix); if (!use_stdout) realstdout = xfdopen(xdup(1), "w"); if (prepare_revision_walk(&rev)) die("revision walk setup failed"); rev.boundary = 1; while ((commit = get_revision(&rev)) != NULL) { if (commit->object.flags & BOUNDARY) { boundary_count++; origin = (boundary_count == 1) ? commit : NULL; continue; } /* ignore merges */ if (commit->parents && commit->parents->next) continue; if (ignore_if_in_upstream && has_commit_patch_id(commit, &ids)) continue; nr++; list = xrealloc(list, nr * sizeof(list[0])); list[nr - 1] = commit; } total = nr; if (!keep_subject && auto_number && total > 1) numbered = 1; if (numbered) rev.total = total + start_number - 1; if (in_reply_to || thread || cover_letter) rev.ref_message_ids = xcalloc(1, sizeof(struct string_list)); if (in_reply_to) { const char *msgid = clean_message_id(in_reply_to); string_list_append(msgid, rev.ref_message_ids); } rev.numbered_files = numbered_files; rev.patch_suffix = fmt_patch_suffix; if (cover_letter) { if (thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, use_stdout, numbered, numbered_files, origin, nr, list, head); total++; start_number--; } rev.add_signoff = add_signoff; while (0 <= --nr) { int shown; commit = list[nr]; rev.nr = total - nr + (start_number - 1); /* Make the second and subsequent mails replies to the first */ if (thread) { /* Have we already had a message ID? */ if (rev.message_id) { /* * For deep threading: make every mail * a reply to the previous one, no * matter what other options are set. * * For shallow threading: * * Without --cover-letter and * --in-reply-to, make every mail a * reply to the one before. * * With --in-reply-to but no * --cover-letter, make every mail a * reply to the <reply-to>. * * With --cover-letter, make every * mail but the cover letter a reply * to the cover letter. The cover * letter is a reply to the * --in-reply-to, if specified. */ if (thread == THREAD_SHALLOW && rev.ref_message_ids->nr > 0 && (!cover_letter || rev.nr > 1)) free(rev.message_id); else string_list_append(rev.message_id, rev.ref_message_ids); } gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit, &rev)) die("Failed to create output files"); shown = log_tree_commit(&rev, commit); free(commit->buffer); commit->buffer = NULL; /* We put one extra blank line between formatted * patches and this flag is used by log-tree code * to see if it needs to emit a LF before showing * the log; when using one file per patch, we do * not want the extra blank line. */ if (!use_stdout) rev.shown_one = 0; if (shown) { if (rev.mime_boundary) printf("\n--%s%s--\n\n\n", mime_boundary_leader, rev.mime_boundary); else printf("-- \n%s\n\n", git_version_string); } if (!use_stdout) fclose(stdout); } free(list); if (ignore_if_in_upstream) free_patch_ids(&ids); return 0; }
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); }
void cgit_print_patch(const char *new_rev, const char *old_rev, const char *prefix) { struct rev_info rev; struct commit *commit; unsigned char new_rev_sha1[20], old_rev_sha1[20]; char rev_range[2 * 40 + 3]; char *rev_argv[] = { NULL, "--reverse", "--format=email", rev_range }; char *patchname; if (!new_rev) new_rev = ctx.qry.head; if (get_sha1(new_rev, new_rev_sha1)) { cgit_print_error("Bad object id: %s", new_rev); return; } commit = lookup_commit_reference(new_rev_sha1); if (!commit) { cgit_print_error("Bad commit reference: %s", new_rev); return; } if (old_rev) { if (get_sha1(old_rev, old_rev_sha1)) { cgit_print_error("Bad object id: %s", old_rev); return; } if (!lookup_commit_reference(old_rev_sha1)) { cgit_print_error("Bad commit reference: %s", old_rev); return; } } else if (commit->parents && commit->parents->item) { hashcpy(old_rev_sha1, commit->parents->item->object.sha1); } else { hashclr(old_rev_sha1); } if (is_null_sha1(old_rev_sha1)) { memcpy(rev_range, sha1_to_hex(new_rev_sha1), 41); } else { sprintf(rev_range, "%s..%s", sha1_to_hex(old_rev_sha1), sha1_to_hex(new_rev_sha1)); } patchname = fmt("%s.patch", rev_range); ctx.page.mimetype = "text/plain"; ctx.page.filename = patchname; cgit_print_http_headers(&ctx); if (ctx.cfg.noplainemail) { rev_argv[2] = "--format=format:From %H Mon Sep 17 00:00:00 " "2001%nFrom: %an%nDate: %aD%n%w(78,0,1)Subject: " "%s%n%n%w(0)%b"; } init_revisions(&rev, NULL); rev.abbrev = DEFAULT_ABBREV; rev.verbose_header = 1; rev.diff = 1; rev.show_root_diff = 1; rev.max_parents = 1; rev.diffopt.output_format |= DIFF_FORMAT_PATCH; setup_revisions(ARRAY_SIZE(rev_argv), (const char **)rev_argv, &rev, NULL); prepare_revision_walk(&rev); while ((commit = get_revision(&rev)) != NULL) { log_tree_commit(&rev, commit); printf("--\ncgit %s\n", cgit_version); } }