int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv) { static struct lock_file lock; int bundle_fd = -1; int bundle_to_stdout; const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *)); const char **argv_pack = xmalloc(6 * sizeof(const char *)); int i, ref_count = 0; char buffer[1024]; struct rev_info revs; struct child_process rls; FILE *rls_fout; bundle_to_stdout = !strcmp(path, "-"); if (bundle_to_stdout) bundle_fd = 1; else bundle_fd = hold_lock_file_for_update(&lock, path, LOCK_DIE_ON_ERROR); /* write signature */ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); /* init revs to list objects for pack-objects later */ save_commit_buffer = 0; init_revisions(&revs, NULL); /* write prerequisites */ memcpy(argv_boundary + 3, argv + 1, argc * sizeof(const char *)); argv_boundary[0] = "rev-list"; argv_boundary[1] = "--boundary"; argv_boundary[2] = "--pretty=oneline"; argv_boundary[argc + 2] = NULL; memset(&rls, 0, sizeof(rls)); rls.argv = argv_boundary; rls.out = -1; rls.git_cmd = 1; if (start_command(&rls)) return -1; rls_fout = xfdopen(rls.out, "r"); while (fgets(buffer, sizeof(buffer), rls_fout)) { unsigned char sha1[20]; if (buffer[0] == '-') { write_or_die(bundle_fd, buffer, strlen(buffer)); if (!get_sha1_hex(buffer + 1, sha1)) { struct object *object = parse_object(sha1); object->flags |= UNINTERESTING; add_pending_object(&revs, object, buffer); } } else if (!get_sha1_hex(buffer, sha1)) { struct object *object = parse_object(sha1); object->flags |= SHOWN; } } fclose(rls_fout); if (finish_command(&rls)) return error("rev-list died"); /* write references */ argc = setup_revisions(argc, argv, &revs, NULL); if (argc > 1) return error("unrecognized argument: %s'", argv[1]); object_array_remove_duplicates(&revs.pending); for (i = 0; i < revs.pending.nr; i++) { struct object_array_entry *e = revs.pending.objects + i; unsigned char sha1[20]; char *ref; const char *display_ref; int flag; if (e->item->flags & UNINTERESTING) continue; if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1) continue; if (!resolve_ref(e->name, sha1, 1, &flag)) flag = 0; display_ref = (flag & REF_ISSYMREF) ? e->name : ref; if (e->item->type == OBJ_TAG && !is_tag_in_date_range(e->item, &revs)) { e->item->flags |= UNINTERESTING; continue; } /* * Make sure the refs we wrote out is correct; --max-count and * other limiting options could have prevented all the tips * from getting output. * * Non commit objects such as tags and blobs do not have * this issue as they are not affected by those extra * constraints. */ if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) { warning("ref '%s' is excluded by the rev-list options", e->name); free(ref); continue; } /* * If you run "git bundle create bndl v1.0..v2.0", the * name of the positive ref is "v2.0" but that is the * commit that is referenced by the tag, and not the tag * itself. */ if (hashcmp(sha1, e->item->sha1)) { /* * Is this the positive end of a range expressed * in terms of a tag (e.g. v2.0 from the range * "v1.0..v2.0")? */ struct commit *one = lookup_commit_reference(sha1); struct object *obj; if (e->item == &(one->object)) { /* * Need to include e->name as an * independent ref to the pack-objects * input, so that the tag is included * in the output; otherwise we would * end up triggering "empty bundle" * error. */ obj = parse_object(sha1); obj->flags |= SHOWN; add_pending_object(&revs, obj, e->name); } free(ref); continue; } ref_count++; write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40); write_or_die(bundle_fd, " ", 1); write_or_die(bundle_fd, display_ref, strlen(display_ref)); write_or_die(bundle_fd, "\n", 1); free(ref); } if (!ref_count) die ("Refusing to create empty bundle."); /* end header */ write_or_die(bundle_fd, "\n", 1); /* write pack */ argv_pack[0] = "pack-objects"; argv_pack[1] = "--all-progress-implied"; argv_pack[2] = "--stdout"; argv_pack[3] = "--thin"; argv_pack[4] = "--delta-base-offset"; argv_pack[5] = NULL; memset(&rls, 0, sizeof(rls)); rls.argv = argv_pack; rls.in = -1; rls.out = bundle_fd; rls.git_cmd = 1; if (start_command(&rls)) return error("Could not spawn pack-objects"); /* * start_command closed bundle_fd if it was > 1 * so set the lock fd to -1 so commit_lock_file() * won't fail trying to close it. */ lock.fd = -1; for (i = 0; i < revs.pending.nr; i++) { struct object *object = revs.pending.objects[i].item; if (object->flags & UNINTERESTING) write_or_die(rls.in, "^", 1); write_or_die(rls.in, sha1_to_hex(object->sha1), 40); write_or_die(rls.in, "\n", 1); } close(rls.in); if (finish_command(&rls)) return error ("pack-objects died"); if (!bundle_to_stdout) { if (commit_lock_file(&lock)) die_errno("cannot create '%s'", path); } return 0; }
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(); 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\n", cgit_version); } }
int cmd_prune(int argc, const char **argv, const char *prefix) { struct rev_info revs; struct progress *progress = NULL; int prune_repos = 0; const struct option options[] = { OPT__DRY_RUN(&show_only, N_("do not remove, show only")), OPT__VERBOSE(&verbose, N_("report pruned objects")), OPT_BOOL(0, "progress", &show_progress, N_("show progress")), OPT_BOOL(0, "repos", &prune_repos, N_("prune .git/repos/")), OPT_EXPIRY_DATE(0, "expire", &expire, N_("expire objects older than <time>")), OPT_END() }; char *s; expire = ULONG_MAX; save_commit_buffer = 0; check_replace_refs = 0; init_revisions(&revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); if (prune_repos) { if (argc) die(_("--repos does not take extra arguments")); prune_repos_dir(); return 0; } while (argc--) { unsigned char sha1[20]; const char *name = *argv++; if (!get_sha1(name, sha1)) { struct object *object = parse_object_or_die(sha1, name); add_pending_object(&revs, object, ""); } else die("unrecognized argument: %s", name); } if (show_progress == -1) show_progress = isatty(2); if (show_progress) progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2); mark_reachable_objects(&revs, 1, progress); stop_progress(&progress); prune_object_dir(get_object_directory()); prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0); remove_temporary_files(get_object_directory()); s = mkpathdup("%s/pack", get_object_directory()); remove_temporary_files(s); free(s); if (is_repository_shallow()) prune_shallow(show_only); return 0; }
void show_submodule_summary(FILE *f, const char *path, unsigned char one[20], unsigned char two[20], unsigned dirty_submodule, const char *del, const char *add, const char *reset) { struct rev_info rev; struct commit *commit, *left = left, *right = right; struct commit_list *merge_bases, *list; const char *message = NULL; struct strbuf sb = STRBUF_INIT; static const char *format = " %m %s"; int fast_forward = 0, fast_backward = 0; if (is_null_sha1(two)) message = "(submodule deleted)"; else if (add_submodule_odb(path)) message = "(not checked out)"; else if (is_null_sha1(one)) message = "(new submodule)"; else if (!(left = lookup_commit_reference(one)) || !(right = lookup_commit_reference(two))) message = "(commits not present)"; if (!message) { init_revisions(&rev, NULL); setup_revisions(0, NULL, &rev, NULL); rev.left_right = 1; rev.first_parent_only = 1; left->object.flags |= SYMMETRIC_LEFT; add_pending_object(&rev, &left->object, path); add_pending_object(&rev, &right->object, path); merge_bases = get_merge_bases(left, right, 1); if (merge_bases) { if (merge_bases->item == left) fast_forward = 1; else if (merge_bases->item == right) fast_backward = 1; } for (list = merge_bases; list; list = list->next) { list->item->object.flags |= UNINTERESTING; add_pending_object(&rev, &list->item->object, sha1_to_hex(list->item->object.sha1)); } if (prepare_revision_walk(&rev)) message = "(revision walker failed)"; } strbuf_addf(&sb, "Submodule %s %s..", path, find_unique_abbrev(one, DEFAULT_ABBREV)); if (!fast_backward && !fast_forward) strbuf_addch(&sb, '.'); strbuf_addf(&sb, "%s", find_unique_abbrev(two, DEFAULT_ABBREV)); if (dirty_submodule) strbuf_add(&sb, "-dirty", 6); if (message) strbuf_addf(&sb, " %s\n", message); else strbuf_addf(&sb, "%s:\n", fast_backward ? " (rewind)" : ""); fwrite(sb.buf, sb.len, 1, f); if (!message) { while ((commit = get_revision(&rev))) { struct pretty_print_context ctx = {0}; ctx.date_mode = rev.date_mode; strbuf_setlen(&sb, 0); if (commit->object.flags & SYMMETRIC_LEFT) { if (del) strbuf_addstr(&sb, del); } else if (add) strbuf_addstr(&sb, add); format_commit_message(commit, format, &sb, &ctx); if (reset) strbuf_addstr(&sb, reset); strbuf_addch(&sb, '\n'); fprintf(f, "%s", sb.buf); } clear_commit_marks(left, ~0); clear_commit_marks(right, ~0); } strbuf_release(&sb); }
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; 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("bogos 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; strbuf_init(&buf, 0); 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_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_cherry(int argc, const char **argv, const char *prefix) { struct rev_info revs; struct patch_ids ids; struct commit *commit; struct commit_list *list = NULL; struct branch *current_branch; const char *upstream; const char *head = "HEAD"; const char *limit = NULL; int verbose = 0, abbrev = 0; struct option options[] = { OPT__ABBREV(&abbrev), OPT__VERBOSE(&verbose, "be verbose"), OPT_END() }; argc = parse_options(argc, argv, prefix, options, cherry_usage, 0); switch (argc) { case 3: limit = argv[2]; /* FALLTHROUGH */ case 2: head = argv[1]; /* FALLTHROUGH */ case 1: upstream = argv[0]; break; default: current_branch = branch_get(NULL); if (!current_branch || !current_branch->merge || !current_branch->merge[0] || !current_branch->merge[0]->dst) { fprintf(stderr, _("Could not find a tracked" " remote branch, please" " specify <upstream> manually.\n")); usage_with_options(cherry_usage, options); } upstream = current_branch->merge[0]->dst; } init_revisions(&revs, prefix); revs.diff = 1; revs.combine_merges = 0; revs.ignore_merges = 1; DIFF_OPT_SET(&revs.diffopt, RECURSIVE); if (add_pending_commit(head, &revs, 0)) die(_("Unknown commit %s"), head); if (add_pending_commit(upstream, &revs, UNINTERESTING)) die(_("Unknown commit %s"), upstream); /* Don't say anything if head and upstream are the same. */ if (revs.pending.nr == 2) { struct object_array_entry *o = revs.pending.objects; if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0) return 0; } get_patch_ids(&revs, &ids, prefix); if (limit && add_pending_commit(limit, &revs, UNINTERESTING)) die(_("Unknown commit %s"), limit); /* reverse the list of commits */ if (prepare_revision_walk(&revs)) die(_("revision walk setup failed")); while ((commit = get_revision(&revs)) != NULL) { /* ignore merges */ if (commit->parents && commit->parents->next) continue; commit_list_insert(commit, &list); } while (list) { char sign = '+'; commit = list->item; if (has_commit_patch_id(commit, &ids)) sign = '-'; print_commit(sign, commit, verbose, abbrev); list = list->next; } free_patch_ids(&ids); return 0; }
int verify_bundle(struct bundle_header *header, int verbose) { /* * Do fast check, then if any prereqs are missing then go line by line * to be verbose about the errors */ struct ref_list *p = &header->prerequisites; struct rev_info revs; const char *argv[] = {NULL, "--all", NULL}; struct object_array refs; struct commit *commit; int i, ret = 0, req_nr; const char *message = _("Repository lacks these prerequisite commits:"); init_revisions(&revs, NULL); for (i = 0; i < p->nr; i++) { struct ref_list_entry *e = p->list + i; struct object *o = parse_object(e->sha1); if (o) { o->flags |= PREREQ_MARK; add_pending_object(&revs, o, e->name); continue; } if (++ret == 1) error("%s", message); error("%s %s", sha1_to_hex(e->sha1), e->name); } if (revs.pending.nr != p->nr) return ret; req_nr = revs.pending.nr; setup_revisions(2, argv, &revs, NULL); refs = revs.pending; revs.leak_pending = 1; if (prepare_revision_walk(&revs)) die(_("revision walk setup failed")); i = req_nr; while (i && (commit = get_revision(&revs))) if (commit->object.flags & PREREQ_MARK) i--; for (i = 0; i < req_nr; i++) if (!(refs.objects[i].item->flags & SHOWN)) { if (++ret == 1) error("%s", message); error("%s %s", sha1_to_hex(refs.objects[i].item->sha1), refs.objects[i].name); } clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS); free(refs.objects); if (verbose) { struct ref_list *r; r = &header->references; printf_ln(Q_("The bundle contains this ref:", "The bundle contains these %d refs:", r->nr), r->nr); list_refs(r, 0, NULL); r = &header->prerequisites; if (!r->nr) { printf_ln(_("The bundle records a complete history.")); } else { printf_ln(Q_("The bundle requires this ref:", "The bundle requires these %d refs:", r->nr), r->nr); list_refs(r, 0, NULL); } } return ret; }
void bitmap_writer_build(struct packing_data *to_pack) { static const double REUSE_BITMAP_THRESHOLD = 0.2; int i, reuse_after, need_reset; struct bitmap *base = bitmap_new(); struct rev_info revs; writer.bitmaps = kh_init_sha1(); writer.to_pack = to_pack; if (writer.show_progress) writer.progress = start_progress("Building bitmaps", writer.selected_nr); init_revisions(&revs, NULL); revs.tag_objects = 1; revs.tree_objects = 1; revs.blob_objects = 1; revs.no_walk = 0; revs.include_check = should_include; reset_revision_walk(); reuse_after = writer.selected_nr * REUSE_BITMAP_THRESHOLD; need_reset = 0; for (i = writer.selected_nr - 1; i >= 0; --i) { struct bitmapped_commit *stored; struct object *object; khiter_t hash_pos; int hash_ret; stored = &writer.selected[i]; object = (struct object *)stored->commit; if (stored->bitmap == NULL) { if (i < writer.selected_nr - 1 && (need_reset || !in_merge_bases(writer.selected[i + 1].commit, stored->commit))) { bitmap_reset(base); reset_all_seen(); } add_pending_object(&revs, object, ""); revs.include_check_data = base; if (prepare_revision_walk(&revs)) die("revision walk setup failed"); traverse_commit_list(&revs, show_commit, show_object, base); revs.pending.nr = 0; revs.pending.alloc = 0; revs.pending.objects = NULL; stored->bitmap = bitmap_to_ewah(base); need_reset = 0; } else need_reset = 1; if (i >= reuse_after) stored->flags |= BITMAP_FLAG_REUSE; hash_pos = kh_put_sha1(writer.bitmaps, object->sha1, &hash_ret); if (hash_ret == 0) die("Duplicate entry when writing index: %s", sha1_to_hex(object->sha1)); kh_value(writer.bitmaps, hash_pos) = stored; display_progress(writer.progress, writer.selected_nr - i); } bitmap_free(base); stop_progress(&writer.progress); compute_xor_offsets(); }
static void print_summary(const char *prefix, const unsigned char *sha1) { struct rev_info rev; struct commit *commit; struct strbuf format = STRBUF_INIT; unsigned char junk_sha1[20]; const char *head = resolve_ref("HEAD", junk_sha1, 0, NULL); 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 (!commit || 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 (!user_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.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.buf, &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 ", !prefixcmp(head, "refs/heads/") ? head + 11 : !strcmp(head, "HEAD") ? "detached HEAD" : head, initial_commit ? " (root-commit)" : ""); if (!log_tree_commit(&rev, commit)) { struct pretty_print_context ctx = {0}; struct strbuf buf = STRBUF_INIT; ctx.date_mode = DATE_NORMAL; format_commit_message(commit, format.buf + 7, &buf, &ctx); printf("%s\n", buf.buf); strbuf_release(&buf); } strbuf_release(&format); }
void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, char *path, int pager, int commit_graph, int commit_sort) { struct rev_info rev; struct commit *commit; struct vector vec = VECTOR_INIT(char *); int i, columns = 3; char *arg; /* First argv is NULL */ vector_push(&vec, NULL, 0); if (!tip) tip = ctx.qry.head; tip = disambiguate_ref(tip); vector_push(&vec, &tip, 0); if (grep && pattern && *pattern) { pattern = xstrdup(pattern); if (!strcmp(grep, "grep") || !strcmp(grep, "author") || !strcmp(grep, "committer")) { arg = fmt("--%s=%s", grep, pattern); vector_push(&vec, &arg, 0); } if (!strcmp(grep, "range")) { /* Split the pattern at whitespace and add each token * as a revision expression. Do not accept other * rev-list options. Also, replace the previously * pushed tip (it's no longer relevant). */ vec.count--; while ((arg = next_token(&pattern))) { if (*arg == '-') { fprintf(stderr, "Bad range expr: %s\n", arg); break; } vector_push(&vec, &arg, 0); } } } if (commit_graph) { static const char *graph_arg = "--graph"; static const char *color_arg = "--color"; vector_push(&vec, &graph_arg, 0); vector_push(&vec, &color_arg, 0); graph_set_column_colors(column_colors_html, COLUMN_COLORS_HTML_MAX); } if (commit_sort == 1) { static const char *date_order_arg = "--date-order"; vector_push(&vec, &date_order_arg, 0); } else if (commit_sort == 2) { static const char *topo_order_arg = "--topo-order"; vector_push(&vec, &topo_order_arg, 0); } if (path) { arg = "--"; vector_push(&vec, &arg, 0); vector_push(&vec, &path, 0); } /* Make sure the vector is NULL-terminated */ vector_push(&vec, NULL, 0); vec.count--; init_revisions(&rev, NULL); rev.abbrev = DEFAULT_ABBREV; rev.commit_format = CMIT_FMT_DEFAULT; rev.verbose_header = 1; rev.show_root_diff = 0; setup_revisions(vec.count, vec.data, &rev, NULL); load_ref_decorations(DECORATE_FULL_REFS); rev.show_decorations = 1; rev.grep_filter.regflags |= REG_ICASE; compile_grep_patterns(&rev.grep_filter); prepare_revision_walk(&rev); if (pager) html("<table class='list nowrap'>"); html("<tr class='nohover'>"); if (commit_graph) html("<th></th>"); else html("<th class='left'>Age</th>"); html("<th class='left'>Commit message"); if (pager) { html(" ("); cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL, NULL, ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath, ctx.qry.ofs, ctx.qry.grep, ctx.qry.search, ctx.qry.showmsg ? 0 : 1); html(")"); } html("</th><th class='left'>Author</th>"); if (commit_graph) html("<th class='left'>Age</th>"); if (ctx.repo->enable_log_filecount) { html("<th class='left'>Files</th>"); columns++; } if (ctx.repo->enable_log_linecount) { html("<th class='left'>Lines</th>"); columns++; } html("</tr>\n"); if (ofs<0) ofs = 0; for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) { free(commit->buffer); commit->buffer = NULL; free_commit_list(commit->parents); commit->parents = NULL; } for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) { print_commit(commit, &rev); free(commit->buffer); commit->buffer = NULL; free_commit_list(commit->parents); commit->parents = NULL; } if (pager) { html("</table><div class='pager'>"); if (ofs > 0) { cgit_log_link("[prev]", NULL, NULL, ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath, ofs - cnt, ctx.qry.grep, ctx.qry.search, ctx.qry.showmsg); html(" "); } if ((commit = get_revision(&rev)) != NULL) { cgit_log_link("[next]", NULL, NULL, ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath, ofs + cnt, ctx.qry.grep, ctx.qry.search, ctx.qry.showmsg); } html("</div>"); } else if ((commit = get_revision(&rev)) != NULL) { html("<tr class='nohover'><td colspan='3'>"); cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL, ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg); html("</td></tr>\n"); } }
int fmt_merge_msg(struct strbuf *in, struct strbuf *out, struct fmt_merge_msg_opts *opts) { int i = 0, pos = 0; unsigned char head_sha1[20]; const char *current_branch; void *current_branch_to_free; struct merge_parents merge_parents; memset(&merge_parents, 0, sizeof(merge_parents)); /* get current branch */ current_branch = current_branch_to_free = resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL); if (!current_branch) die("No current branch"); if (starts_with(current_branch, "refs/heads/")) current_branch += 11; find_merge_parents(&merge_parents, in, head_sha1); /* get a line */ while (pos < in->len) { int len; char *newline, *p = in->buf + pos; newline = strchr(p, '\n'); len = newline ? newline - p : strlen(p); pos += len + !!newline; i++; p[len] = 0; if (handle_line(p, &merge_parents)) die ("Error in line %d: %.*s", i, len, p); } if (opts->add_title && srcs.nr) fmt_merge_msg_title(out, current_branch); if (origins.nr) fmt_merge_msg_sigs(out); if (opts->shortlog_len) { struct commit *head; struct rev_info rev; head = lookup_commit_or_die(head_sha1, "HEAD"); init_revisions(&rev, NULL); rev.commit_format = CMIT_FMT_ONELINE; rev.merge_diff_mode = MERGE_DIFF_IGNORE; rev.limited = 1; strbuf_complete_line(out); for (i = 0; i < origins.nr; i++) shortlog(origins.items[i].string, origins.items[i].util, head, &rev, opts, out); } strbuf_complete_line(out); free(current_branch_to_free); free(merge_parents.item); return 0; }
int cmd_main(int argc, const char **argv) { struct transfer_request *request; struct transfer_request *next_request; int nr_refspec = 0; const char **refspec = NULL; struct remote_lock *ref_lock = NULL; struct remote_lock *info_ref_lock = NULL; struct rev_info revs; int delete_branch = 0; int force_delete = 0; int objects_to_send; int rc = 0; int i; int new_refs; struct ref *ref, *local_refs; repo = xcalloc(1, sizeof(*repo)); argv++; for (i = 1; i < argc; i++, argv++) { const char *arg = *argv; if (*arg == '-') { if (!strcmp(arg, "--all")) { push_all = MATCH_REFS_ALL; continue; } if (!strcmp(arg, "--force")) { force_all = 1; continue; } if (!strcmp(arg, "--dry-run")) { dry_run = 1; continue; } if (!strcmp(arg, "--helper-status")) { helper_status = 1; continue; } if (!strcmp(arg, "--verbose")) { push_verbosely = 1; http_is_verbose = 1; continue; } if (!strcmp(arg, "-d")) { delete_branch = 1; continue; } if (!strcmp(arg, "-D")) { delete_branch = 1; force_delete = 1; continue; } if (!strcmp(arg, "-h")) usage(http_push_usage); } if (!repo->url) { char *path = strstr(arg, "//"); str_end_url_with_slash(arg, &repo->url); repo->path_len = strlen(repo->url); if (path) { repo->path = strchr(path+2, '/'); if (repo->path) repo->path_len = strlen(repo->path); } continue; } refspec = argv; nr_refspec = argc - i; break; } #ifndef USE_CURL_MULTI die("git-push is not available for http/https repository when not compiled with USE_CURL_MULTI"); #endif if (!repo->url) usage(http_push_usage); if (delete_branch && nr_refspec != 1) die("You must specify only one branch name when deleting a remote branch"); setup_git_directory(); memset(remote_dir_exists, -1, 256); http_init(NULL, repo->url, 1); #ifdef USE_CURL_MULTI is_running_queue = 0; #endif /* Verify DAV compliance/lock support */ if (!locking_available()) { rc = 1; goto cleanup; } sigchain_push_common(remove_locks_on_signal); /* Check whether the remote has server info files */ repo->can_update_info_refs = 0; repo->has_info_refs = remote_exists("info/refs"); repo->has_info_packs = remote_exists("objects/info/packs"); if (repo->has_info_refs) { info_ref_lock = lock_remote("info/refs", LOCK_TIME); if (info_ref_lock) repo->can_update_info_refs = 1; else { error("cannot lock existing info/refs"); rc = 1; goto cleanup; } } if (repo->has_info_packs) fetch_indices(); /* Get a list of all local and remote heads to validate refspecs */ local_refs = get_local_heads(); fprintf(stderr, "Fetching remote heads...\n"); get_dav_remote_heads(); run_request_queue(); /* Remove a remote branch if -d or -D was specified */ if (delete_branch) { if (delete_remote_branch(refspec[0], force_delete) == -1) { fprintf(stderr, "Unable to delete remote branch %s\n", refspec[0]); if (helper_status) printf("error %s cannot remove\n", refspec[0]); } goto cleanup; } /* match them up */ if (match_push_refs(local_refs, &remote_refs, nr_refspec, (const char **) refspec, push_all)) { rc = -1; goto cleanup; } if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n"); if (helper_status) printf("error null no match\n"); rc = 0; goto cleanup; } new_refs = 0; for (ref = remote_refs; ref; ref = ref->next) { struct argv_array commit_argv = ARGV_ARRAY_INIT; if (!ref->peer_ref) continue; if (is_null_oid(&ref->peer_ref->new_oid)) { if (delete_remote_branch(ref->name, 1) == -1) { error("Could not remove %s", ref->name); if (helper_status) printf("error %s cannot remove\n", ref->name); rc = -4; } else if (helper_status) printf("ok %s\n", ref->name); new_refs++; continue; } if (!oidcmp(&ref->old_oid, &ref->peer_ref->new_oid)) { if (push_verbosely) fprintf(stderr, "'%s': up-to-date\n", ref->name); if (helper_status) printf("ok %s up to date\n", ref->name); continue; } if (!force_all && !is_null_oid(&ref->old_oid) && !ref->force) { if (!has_object_file(&ref->old_oid) || !ref_newer(&ref->peer_ref->new_oid, &ref->old_oid)) { /* * We do not have the remote ref, or * we know that the remote ref is not * an ancestor of what we are trying to * push. Either way this can be losing * commits at the remote end and likely * we were not up to date to begin with. */ error("remote '%s' is not an ancestor of\n" "local '%s'.\n" "Maybe you are not up-to-date and " "need to pull first?", ref->name, ref->peer_ref->name); if (helper_status) printf("error %s non-fast forward\n", ref->name); rc = -2; continue; } } oidcpy(&ref->new_oid, &ref->peer_ref->new_oid); new_refs++; fprintf(stderr, "updating '%s'", ref->name); if (strcmp(ref->name, ref->peer_ref->name)) fprintf(stderr, " using '%s'", ref->peer_ref->name); fprintf(stderr, "\n from %s\n to %s\n", oid_to_hex(&ref->old_oid), oid_to_hex(&ref->new_oid)); if (dry_run) { if (helper_status) printf("ok %s\n", ref->name); continue; } /* Lock remote branch ref */ ref_lock = lock_remote(ref->name, LOCK_TIME); if (ref_lock == NULL) { fprintf(stderr, "Unable to lock remote branch %s\n", ref->name); if (helper_status) printf("error %s lock error\n", ref->name); rc = 1; continue; } /* Set up revision info for this refspec */ argv_array_push(&commit_argv, ""); /* ignored */ argv_array_push(&commit_argv, "--objects"); argv_array_push(&commit_argv, oid_to_hex(&ref->new_oid)); if (!push_all && !is_null_oid(&ref->old_oid)) argv_array_pushf(&commit_argv, "^%s", oid_to_hex(&ref->old_oid)); init_revisions(&revs, setup_git_directory()); setup_revisions(commit_argv.argc, commit_argv.argv, &revs, NULL); revs.edge_hint = 0; /* just in case */ /* Generate a list of objects that need to be pushed */ pushing = 0; if (prepare_revision_walk(&revs)) die("revision walk setup failed"); mark_edges_uninteresting(&revs, NULL); objects_to_send = get_delta(&revs, ref_lock); finish_all_active_slots(); /* Push missing objects to remote, this would be a convenient time to pack them first if appropriate. */ pushing = 1; if (objects_to_send) fprintf(stderr, " sending %d objects\n", objects_to_send); run_request_queue(); /* Update the remote branch if all went well */ if (aborted || !update_remote(ref->new_oid.hash, ref_lock)) rc = 1; if (!rc) fprintf(stderr, " done\n"); if (helper_status) printf("%s %s\n", !rc ? "ok" : "error", ref->name); unlock_remote(ref_lock); check_locks(); argv_array_clear(&commit_argv); } /* Update remote server info if appropriate */ if (repo->has_info_refs && new_refs) { if (info_ref_lock && repo->can_update_info_refs) { fprintf(stderr, "Updating remote server info\n"); if (!dry_run) update_remote_info_refs(info_ref_lock); } else { fprintf(stderr, "Unable to update server info\n"); } } cleanup: if (info_ref_lock) unlock_remote(info_ref_lock); free(repo); http_cleanup(); request = request_queue_head; while (request != NULL) { next_request = request->next; release_request(request); request = next_request; } return rc; }
int cmd_rev_list(int argc, const char **argv, const char *prefix) { struct rev_info revs; struct rev_list_info info; int i; int bisect_list = 0; int bisect_show_vars = 0; int bisect_find_all = 0; git_config(git_default_config, NULL); init_revisions(&revs, prefix); revs.abbrev = DEFAULT_ABBREV; revs.commit_format = CMIT_FMT_UNSPECIFIED; argc = setup_revisions(argc, argv, &revs, NULL); memset(&info, 0, sizeof(info)); info.revs = &revs; if (revs.bisect) bisect_list = 1; if (DIFF_OPT_TST(&revs.diffopt, QUICK)) info.flags |= REV_LIST_QUIET; for (i = 1 ; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--header")) { revs.verbose_header = 1; continue; } if (!strcmp(arg, "--timestamp")) { info.show_timestamp = 1; continue; } if (!strcmp(arg, "--bisect")) { bisect_list = 1; continue; } if (!strcmp(arg, "--bisect-all")) { bisect_list = 1; bisect_find_all = 1; info.flags |= BISECT_SHOW_ALL; revs.show_decorations = 1; continue; } if (!strcmp(arg, "--bisect-vars")) { bisect_list = 1; bisect_show_vars = 1; continue; } usage(rev_list_usage); } if (revs.commit_format != CMIT_FMT_UNSPECIFIED) { /* The command line has a --pretty */ info.hdr_termination = '\n'; if (revs.commit_format == CMIT_FMT_ONELINE) info.header_prefix = ""; else info.header_prefix = "commit "; } else if (revs.verbose_header) /* Only --header was specified */ revs.commit_format = CMIT_FMT_RAW; if ((!revs.commits && (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending.nr)) || revs.diff) usage(rev_list_usage); save_commit_buffer = (revs.verbose_header || revs.grep_filter.pattern_list || revs.grep_filter.header_list); if (bisect_list) revs.limited = 1; if (prepare_revision_walk(&revs)) die("revision walk setup failed"); if (revs.tree_objects) mark_edges_uninteresting(&revs, show_edge); if (bisect_list) { int reaches = reaches, all = all; revs.commits = find_bisection(revs.commits, &reaches, &all, bisect_find_all); if (bisect_show_vars) return show_bisect_vars(&info, reaches, all); } traverse_commit_list(&revs, show_commit, show_object, &info); if (revs.count) { if (revs.left_right && revs.cherry_mark) printf("%d\t%d\t%d\n", revs.count_left, revs.count_right, revs.count_same); else if (revs.left_right) printf("%d\t%d\n", revs.count_left, revs.count_right); else if (revs.cherry_mark) printf("%d\t%d\n", revs.count_left + revs.count_right, revs.count_same); else printf("%d\n", revs.count_left + revs.count_right); } return 0; }
int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv) { static struct lock_file lock; int bundle_fd = -1; int bundle_to_stdout; int ref_count = 0; struct rev_info revs; bundle_to_stdout = !strcmp(path, "-"); if (bundle_to_stdout) bundle_fd = 1; else { bundle_fd = hold_lock_file_for_update(&lock, path, LOCK_DIE_ON_ERROR); /* * write_pack_data() will close the fd passed to it, * but commit_lock_file() will also try to close the * lockfile's fd. So make a copy of the file * descriptor to avoid trying to close it twice. */ bundle_fd = dup(bundle_fd); if (bundle_fd < 0) die_errno("unable to dup file descriptor"); } /* write signature */ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); /* init revs to list objects for pack-objects later */ save_commit_buffer = 0; init_revisions(&revs, NULL); /* write prerequisites */ if (compute_and_write_prerequisites(bundle_fd, &revs, argc, argv)) return -1; argc = setup_revisions(argc, argv, &revs, NULL); if (argc > 1) return error(_("unrecognized argument: %s"), argv[1]); object_array_remove_duplicates(&revs.pending); ref_count = write_bundle_refs(bundle_fd, &revs); if (!ref_count) die(_("Refusing to create empty bundle.")); else if (ref_count < 0) return -1; /* write pack */ if (write_pack_data(bundle_fd, &revs)) return -1; if (!bundle_to_stdout) { if (commit_lock_file(&lock)) die_errno(_("cannot create '%s'"), path); } return 0; }
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) die("No HEAD commit to compare with (yet)"); 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) { const char **pathspec = rev.prune_data; while (*pathspec) { if (!path) path = *pathspec; paths++; pathspec++; } } /* * 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; }
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(); git_config(git_diff_ui_config, NULL); 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, prefix); /* 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->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) { 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->sha1); 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; }
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 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; int quiet = 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", 0, header_callback }, { OPTION_CALLBACK, 0, "to", NULL, "email", "add To: header", 0, to_callback }, { OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header", 0, 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_STRING(0, "signature", &signature, "signature", "add a signature"), OPT_BOOLEAN(0, "quiet", &quiet, "don't print the patch filenames"), OPT_END() }; extra_hdr.strdup_strings = 1; extra_to.strdup_strings = 1; extra_cc.strdup_strings = 1; 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"; 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.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 (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) { 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 command 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) { /* 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, 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; } 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(rev.ref_message_ids, msgid); } 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, quiet); 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.ref_message_ids, rev.message_id); } gen_message_id(&rev, sha1_to_hex(commit->object.sha1)); } if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit, &rev, quiet)) 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 print_signature(); } if (!use_stdout) fclose(stdout); } free(list); 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_cherry(int argc, const char **argv, const char *prefix) { struct rev_info revs; struct patch_ids ids; struct commit *commit; struct commit_list *list = NULL; const char *upstream; const char *head = "HEAD"; const char *limit = NULL; int verbose = 0; if (argc > 1 && !strcmp(argv[1], "-v")) { verbose = 1; argc--; argv++; } switch (argc) { case 4: limit = argv[3]; /* FALLTHROUGH */ case 3: head = argv[2]; /* FALLTHROUGH */ case 2: upstream = argv[1]; break; default: usage(cherry_usage); } init_revisions(&revs, prefix); revs.diff = 1; revs.combine_merges = 0; revs.ignore_merges = 1; DIFF_OPT_SET(&revs.diffopt, RECURSIVE); if (add_pending_commit(head, &revs, 0)) die("Unknown commit %s", head); if (add_pending_commit(upstream, &revs, UNINTERESTING)) die("Unknown commit %s", upstream); /* Don't say anything if head and upstream are the same. */ if (revs.pending.nr == 2) { struct object_array_entry *o = revs.pending.objects; if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0) return 0; } get_patch_ids(&revs, &ids, prefix); if (limit && add_pending_commit(limit, &revs, UNINTERESTING)) die("Unknown commit %s", limit); /* reverse the list of commits */ if (prepare_revision_walk(&revs)) die("revision walk setup failed"); while ((commit = get_revision(&revs)) != NULL) { /* ignore merges */ if (commit->parents && commit->parents->next) continue; commit_list_insert(commit, &list); } while (list) { char sign = '+'; commit = list->item; if (has_commit_patch_id(commit, &ids)) sign = '-'; if (verbose) { struct strbuf buf; strbuf_init(&buf, 0); pretty_print_commit(CMIT_FMT_ONELINE, commit, &buf, 0, NULL, NULL, 0, 0); printf("%c %s %s\n", sign, sha1_to_hex(commit->object.sha1), buf.buf); strbuf_release(&buf); } else { printf("%c %s\n", sign, sha1_to_hex(commit->object.sha1)); } list = list->next; } free_patch_ids(&ids); return 0; }
int cmd_show(int argc, const char **argv, const char *prefix) { struct rev_info rev; struct object_array_entry *objects; struct setup_revision_opt opt; struct pathspec match_all; int i, count, ret = 0; git_config(git_log_config, NULL); init_pathspec(&match_all, NULL); init_revisions(&rev, prefix); rev.diff = 1; rev.always_show_header = 1; rev.no_walk = 1; memset(&opt, 0, sizeof(opt)); opt.def = "HEAD"; opt.tweak = show_rev_tweak_rev; cmd_log_init(argc, argv, prefix, &rev, &opt); count = rev.pending.nr; objects = rev.pending.objects; for (i = 0; i < count && !ret; i++) { struct object *o = objects[i].item; const char *name = objects[i].name; switch (o->type) { case OBJ_BLOB: ret = show_object(o->sha1, 0, NULL); break; case OBJ_TAG: { struct tag *t = (struct tag *)o; if (rev.shown_one) putchar('\n'); printf("%stag %s%s\n", diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), t->tag, diff_get_color_opt(&rev.diffopt, DIFF_RESET)); ret = show_object(o->sha1, 1, &rev); rev.shown_one = 1; if (ret) break; o = parse_object(t->tagged->sha1); if (!o) ret = error(_("Could not read object %s"), sha1_to_hex(t->tagged->sha1)); objects[i].item = o; i--; break; } case OBJ_TREE: if (rev.shown_one) putchar('\n'); printf("%stree %s%s\n\n", diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), name, diff_get_color_opt(&rev.diffopt, DIFF_RESET)); read_tree_recursive((struct tree *)o, "", 0, 0, &match_all, show_tree_object, NULL); rev.shown_one = 1; break; case OBJ_COMMIT: rev.pending.nr = rev.pending.alloc = 0; rev.pending.objects = NULL; add_object_array(o, name, &rev.pending); ret = cmd_log_walk(&rev); break; default: ret = error(_("Unknown type: %d"), o->type); } } free(objects); return ret; }
int cmd_show(int argc, const char **argv, const char *prefix) { struct rev_info rev; struct object_array_entry *objects; int i, count, ret = 0; git_config(git_log_config, NULL); if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; init_revisions(&rev, prefix); rev.diff = 1; rev.combine_merges = 1; rev.dense_combined_merges = 1; rev.always_show_header = 1; rev.ignore_merges = 0; rev.no_walk = 1; cmd_log_init(argc, argv, prefix, &rev); count = rev.pending.nr; objects = rev.pending.objects; for (i = 0; i < count && !ret; i++) { struct object *o = objects[i].item; const char *name = objects[i].name; switch (o->type) { case OBJ_BLOB: ret = show_object(o->sha1, 0, NULL); break; case OBJ_TAG: { struct tag *t = (struct tag *)o; printf("%stag %s%s\n", diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), t->tag, diff_get_color_opt(&rev.diffopt, DIFF_RESET)); ret = show_object(o->sha1, 1, &rev); objects[i].item = parse_object(t->tagged->sha1); i--; break; } case OBJ_TREE: printf("%stree %s%s\n\n", diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), name, diff_get_color_opt(&rev.diffopt, DIFF_RESET)); read_tree_recursive((struct tree *)o, "", 0, 0, NULL, show_tree_object); break; case OBJ_COMMIT: rev.pending.nr = rev.pending.alloc = 0; rev.pending.objects = NULL; add_object_array(o, name, &rev.pending); ret = cmd_log_walk(&rev); break; default: ret = error("Unknown type: %d", o->type); } } free(objects); return ret; }
int cmd_rev_list(int argc, const char **argv, const char *prefix) { struct commit_list *list; int i; int read_from_stdin = 0; int bisect_show_vars = 0; int bisect_find_all = 0; int quiet = 0; git_config(git_default_config, NULL); init_revisions(&revs, prefix); revs.abbrev = 0; revs.commit_format = CMIT_FMT_UNSPECIFIED; argc = setup_revisions(argc, argv, &revs, NULL); quiet = DIFF_OPT_TST(&revs.diffopt, QUIET); for (i = 1 ; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--header")) { revs.verbose_header = 1; continue; } if (!strcmp(arg, "--timestamp")) { show_timestamp = 1; continue; } if (!strcmp(arg, "--bisect")) { bisect_list = 1; continue; } if (!strcmp(arg, "--bisect-all")) { bisect_list = 1; bisect_find_all = 1; continue; } if (!strcmp(arg, "--bisect-vars")) { bisect_list = 1; bisect_show_vars = 1; continue; } if (!strcmp(arg, "--stdin")) { if (read_from_stdin++) die("--stdin given twice?"); read_revisions_from_stdin(&revs); continue; } usage(rev_list_usage); } if (revs.commit_format != CMIT_FMT_UNSPECIFIED) { /* The command line has a --pretty */ hdr_termination = '\n'; if (revs.commit_format == CMIT_FMT_ONELINE) header_prefix = ""; else header_prefix = "commit "; } else if (revs.verbose_header) /* Only --header was specified */ revs.commit_format = CMIT_FMT_RAW; list = revs.commits; if ((!list && (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending.nr)) || revs.diff) usage(rev_list_usage); save_commit_buffer = revs.verbose_header || revs.grep_filter.pattern_list; if (bisect_list) revs.limited = 1; if (prepare_revision_walk(&revs)) die("revision walk setup failed"); if (revs.tree_objects) mark_edges_uninteresting(revs.commits, &revs, show_edge); if (bisect_list) { int reaches = reaches, all = all; revs.commits = find_bisection(revs.commits, &reaches, &all, bisect_find_all); if (bisect_show_vars) { int cnt; char hex[41]; if (!revs.commits) return 1; /* * revs.commits can reach "reaches" commits among * "all" commits. If it is good, then there are * (all-reaches) commits left to be bisected. * On the other hand, if it is bad, then the set * to bisect is "reaches". * A bisect set of size N has (N-1) commits further * to test, as we already know one bad one. */ cnt = all - reaches; if (cnt < reaches) cnt = reaches; strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1)); if (bisect_find_all) { traverse_commit_list(&revs, show_commit, show_object); printf("------\n"); } printf("bisect_rev=%s\n" "bisect_nr=%d\n" "bisect_good=%d\n" "bisect_bad=%d\n" "bisect_all=%d\n", hex, cnt - 1, all - reaches - 1, reaches - 1, all); return 0; } } traverse_commit_list(&revs, quiet ? finish_commit : show_commit, quiet ? finish_object : show_object); return 0; }
void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, char *path, int pager, int commit_graph, int commit_sort) { struct rev_info rev; struct commit *commit; struct argv_array rev_argv = ARGV_ARRAY_INIT; int i, columns = commit_graph ? 4 : 3; int must_free_tip = 0; /* rev_argv.argv[0] will be ignored by setup_revisions */ argv_array_push(&rev_argv, "log_rev_setup"); if (!tip) tip = ctx.qry.head; tip = disambiguate_ref(tip, &must_free_tip); argv_array_push(&rev_argv, tip); if (grep && pattern && *pattern) { pattern = xstrdup(pattern); if (!strcmp(grep, "grep") || !strcmp(grep, "author") || !strcmp(grep, "committer")) { argv_array_pushf(&rev_argv, "--%s=%s", grep, pattern); } else if (!strcmp(grep, "range")) { char *arg; /* Split the pattern at whitespace and add each token * as a revision expression. Do not accept other * rev-list options. Also, replace the previously * pushed tip (it's no longer relevant). */ argv_array_pop(&rev_argv); while ((arg = next_token(&pattern))) { if (*arg == '-') { fprintf(stderr, "Bad range expr: %s\n", arg); break; } argv_array_push(&rev_argv, arg); } } } if (commit_graph) { argv_array_push(&rev_argv, "--graph"); argv_array_push(&rev_argv, "--color"); graph_set_column_colors(column_colors_html, COLUMN_COLORS_HTML_MAX); } if (commit_sort == 1) argv_array_push(&rev_argv, "--date-order"); else if (commit_sort == 2) argv_array_push(&rev_argv, "--topo-order"); if (path) { argv_array_push(&rev_argv, "--"); argv_array_push(&rev_argv, path); } init_revisions(&rev, NULL); rev.abbrev = DEFAULT_ABBREV; rev.commit_format = CMIT_FMT_DEFAULT; rev.verbose_header = 1; rev.show_root_diff = 0; setup_revisions(rev_argv.argc, rev_argv.argv, &rev, NULL); load_ref_decorations(DECORATE_FULL_REFS); rev.show_decorations = 1; rev.grep_filter.regflags |= REG_ICASE; compile_grep_patterns(&rev.grep_filter); prepare_revision_walk(&rev); if (pager) html("<table class='list nowrap'>"); html("<tr class='nohover'>"); if (commit_graph) html("<th></th>"); else html("<th class='left'>Age</th>"); html("<th class='left'>Commit message"); if (pager) { html(" ("); cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL, NULL, ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath, ctx.qry.ofs, ctx.qry.grep, ctx.qry.search, ctx.qry.showmsg ? 0 : 1); html(")"); } html("</th><th class='left'>Author</th>"); if (commit_graph) html("<th class='left'>Age</th>"); if (ctx.repo->enable_log_filecount) { html("<th class='left'>Files</th>"); columns++; } if (ctx.repo->enable_log_linecount) { html("<th class='left'>Lines</th>"); columns++; } html("</tr>\n"); if (ofs<0) ofs = 0; for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) { free(commit->buffer); commit->buffer = NULL; free_commit_list(commit->parents); commit->parents = NULL; } for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) { print_commit(commit, &rev); free(commit->buffer); commit->buffer = NULL; free_commit_list(commit->parents); commit->parents = NULL; } if (pager) { html("</table><ul class='pager'>"); if (ofs > 0) { html("<li>"); cgit_log_link("[prev]", NULL, NULL, ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath, ofs - cnt, ctx.qry.grep, ctx.qry.search, ctx.qry.showmsg); html("</li>"); } if ((commit = get_revision(&rev)) != NULL) { html("<li>"); cgit_log_link("[next]", NULL, NULL, ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath, ofs + cnt, ctx.qry.grep, ctx.qry.search, ctx.qry.showmsg); html("</li>"); } html("</ul>"); } else if ((commit = get_revision(&rev)) != NULL) { htmlf("<tr class='nohover'><td colspan='%d'>", columns); cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL, ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg); html("</td></tr>\n"); } /* If we allocated tip then it is safe to cast away const. */ if (must_free_tip) free((char*) tip); }