static int show_stash(int argc, const char **argv, const char *prefix) { int i; int opts = 0; int ret = 0; struct stash_info info; struct rev_info rev; struct argv_array stash_args = ARGV_ARRAY_INIT; struct option options[] = { OPT_END() }; init_diff_ui_defaults(); git_config(git_diff_ui_config, NULL); init_revisions(&rev, prefix); for (i = 1; i < argc; i++) { if (argv[i][0] != '-') argv_array_push(&stash_args, argv[i]); else opts++; } ret = get_stash_info(&info, stash_args.argc, stash_args.argv); argv_array_clear(&stash_args); if (ret) return -1; /* * The config settings are applied only if there are not passed * any options. */ if (!opts) { git_config(git_stash_config, NULL); if (show_stat) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT; if (show_patch) rev.diffopt.output_format |= DIFF_FORMAT_PATCH; if (!show_stat && !show_patch) { free_stash_info(&info); return 0; } } argc = setup_revisions(argc, argv, &rev, NULL); if (argc > 1) { free_stash_info(&info); usage_with_options(git_stash_show_usage, options); } rev.diffopt.flags.recursive = 1; setup_diff_pager(&rev.diffopt); diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt); log_tree_diff_flush(&rev); free_stash_info(&info); return diff_result_code(&rev.diffopt, 0); }
int commit_patch_id(struct commit *commit, struct diff_options *options, struct object_id *oid, int diff_header_only) { if (!patch_id_defined(commit)) return -1; if (commit->parents) diff_tree_oid(&commit->parents->item->object.oid, &commit->object.oid, "", options); else diff_root_tree_oid(&commit->object.oid, "", options); diffcore_std(options); return diff_flush_patch_id(options, oid, diff_header_only); }
/* Diff two trees. */ static int stdin_diff_trees(struct tree *tree1, const char *p) { struct object_id oid; struct tree *tree2; if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p) return error("Need exactly two trees, separated by a space"); tree2 = lookup_tree(&oid); if (!tree2 || parse_tree(tree2)) return -1; printf("%s %s\n", oid_to_hex(&tree1->object.oid), oid_to_hex(&tree2->object.oid)); diff_tree_oid(&tree1->object.oid, &tree2->object.oid, "", &log_tree_opt.diffopt); log_tree_diff_flush(&log_tree_opt); return 0; }
static int builtin_diff_tree(struct rev_info *revs, int argc, const char **argv, struct object_array_entry *ent0, struct object_array_entry *ent1) { const struct object_id *(oid[2]); int swap = 0; if (argc > 1) usage(builtin_diff_usage); /* * We saw two trees, ent0 and ent1. If ent1 is uninteresting, * swap them. */ if (ent1->item->flags & UNINTERESTING) swap = 1; oid[swap] = &ent0->item->oid; oid[1 - swap] = &ent1->item->oid; diff_tree_oid(oid[0], oid[1], "", &revs->diffopt); log_tree_diff_flush(revs); return 0; }
/* * We have an origin -- find the path that corresponds to it in its * parent and return an origin structure to represent it. */ static struct blame_origin *find_rename(struct commit *parent, struct blame_origin *origin) { struct blame_origin *porigin = NULL; struct diff_options diff_opts; int i; diff_setup(&diff_opts); DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.detect_rename = DIFF_DETECT_RENAME; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_opts.single_follow = origin->path; diff_setup_done(&diff_opts); if (is_null_oid(&origin->commit->object.oid)) do_diff_cache(&parent->tree->object.oid, &diff_opts); else diff_tree_oid(&parent->tree->object.oid, &origin->commit->tree->object.oid, "", &diff_opts); diffcore_std(&diff_opts); for (i = 0; i < diff_queued_diff.nr; i++) { struct diff_filepair *p = diff_queued_diff.queue[i]; if ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, origin->path)) { porigin = get_origin(parent, p->one->path); oidcpy(&porigin->blob_oid, &p->one->oid); porigin->mode = p->one->mode; break; } } diff_flush(&diff_opts); clear_pathspec(&diff_opts.pathspec); return porigin; }
int git_do_diff(GIT_DIFF diff, const GIT_HASH hash1, const GIT_HASH hash2, GIT_FILE* file, int* count, int isstat) { struct rev_info *p_Rev; int ret; struct object_id oid1, oid2; struct diff_queue_struct *q = &diff_queued_diff; p_Rev = (struct rev_info *)diff; hashcpy(oid1.hash, hash1); hashcpy(oid2.hash, hash2); ret = diff_tree_oid(&oid1, &oid2, "", &p_Rev->diffopt); if( ret ) { free_all_pack(); return ret; } if(isstat) { diffcore_std(&p_Rev->diffopt); memset(&p_Rev->diffstat, 0, sizeof(struct diffstat_t)); for (int i = 0; i < q->nr; ++i) { struct diff_filepair *p = q->queue[i]; //if (check_pair_status(p)) diff_flush_stat(p, &p_Rev->diffopt, &p_Rev->diffstat); } } free_all_pack(); if(file) *file = q; if(count) *count = q->nr; return 0; }
int cmd_diff_tree(int argc, const char **argv, const char *prefix) { char line[1000]; struct object *tree1, *tree2; static struct rev_info *opt = &log_tree_opt; struct setup_revision_opt s_r_opt; int read_stdin = 0; if (argc == 2 && !strcmp(argv[1], "-h")) usage(diff_tree_usage); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ init_revisions(opt, prefix); gitmodules_config(); opt->abbrev = 0; opt->diff = 1; opt->disable_stdin = 1; memset(&s_r_opt, 0, sizeof(s_r_opt)); s_r_opt.tweak = diff_tree_tweak_rev; precompose_argv(argc, argv); argc = setup_revisions(argc, argv, opt, &s_r_opt); while (--argc > 0) { const char *arg = *++argv; if (!strcmp(arg, "--stdin")) { read_stdin = 1; continue; } usage(diff_tree_usage); } /* * NOTE! We expect "a..b" to expand to "^a b" but it is * perfectly valid for revision range parser to yield "b ^a", * which means the same thing. If we get the latter, i.e. the * second one is marked UNINTERESTING, we recover the original * order the user gave, i.e. "a..b", by swapping the trees. */ switch (opt->pending.nr) { case 0: if (!read_stdin) usage(diff_tree_usage); break; case 1: tree1 = opt->pending.objects[0].item; diff_tree_commit_oid(&tree1->oid); break; case 2: tree1 = opt->pending.objects[0].item; tree2 = opt->pending.objects[1].item; if (tree2->flags & UNINTERESTING) { SWAP(tree2, tree1); } diff_tree_oid(&tree1->oid, &tree2->oid, "", &opt->diffopt); log_tree_diff_flush(opt); break; } if (read_stdin) { int saved_nrl = 0; int saved_dcctc = 0; if (opt->diffopt.detect_rename) opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE | DIFF_SETUP_USE_CACHE); while (fgets(line, sizeof(line), stdin)) { struct object_id oid; if (get_oid_hex(line, &oid)) { fputs(line, stdout); fflush(stdout); } else { diff_tree_stdin(line); if (saved_nrl < opt->diffopt.needed_rename_limit) saved_nrl = opt->diffopt.needed_rename_limit; if (opt->diffopt.degraded_cc_to_c) saved_dcctc = 1; } } opt->diffopt.degraded_cc_to_c = saved_dcctc; opt->diffopt.needed_rename_limit = saved_nrl; } return diff_result_code(&opt->diffopt, 0); }
static void diff_tree_local(struct notes_merge_options *o, struct notes_merge_pair *changes, int len, const struct object_id *base, const struct object_id *local) { struct diff_options opt; int i; trace_printf("\tdiff_tree_local(len = %i, base = %.7s, local = %.7s)\n", len, oid_to_hex(base), oid_to_hex(local)); diff_setup(&opt); opt.flags.recursive = 1; opt.output_format = DIFF_FORMAT_NO_OUTPUT; diff_setup_done(&opt); diff_tree_oid(base, local, "", &opt); diffcore_std(&opt); for (i = 0; i < diff_queued_diff.nr; i++) { struct diff_filepair *p = diff_queued_diff.queue[i]; struct notes_merge_pair *mp; int match; struct object_id obj; if (verify_notes_filepair(p, &obj)) { trace_printf("\t\tCannot merge entry '%s' (%c): " "%.7s -> %.7s. Skipping!\n", p->one->path, p->status, oid_to_hex(&p->one->oid), oid_to_hex(&p->two->oid)); continue; } mp = find_notes_merge_pair_pos(changes, len, &obj, 0, &match); if (!match) { trace_printf("\t\tIgnoring local-only change for %s: " "%.7s -> %.7s\n", oid_to_hex(&obj), oid_to_hex(&p->one->oid), oid_to_hex(&p->two->oid)); continue; } assert(!oidcmp(&mp->obj, &obj)); if (is_null_oid(&p->two->oid)) { /* deletion */ /* * Either this is a true deletion (1), or it is part * of an A/D pair (2), or D/A pair (3): * * (1) mp->local is uninitialized; set it to null_sha1 * (2) mp->local is not uninitialized; don't touch it * (3) mp->local is uninitialized; set it to null_sha1 * (will be overwritten by following addition) */ if (!oidcmp(&mp->local, &uninitialized)) oidclr(&mp->local); } else if (is_null_oid(&p->one->oid)) { /* addition */ /* * Either this is a true addition (1), or it is part * of an A/D pair (2), or D/A pair (3): * * (1) mp->local is uninitialized; set to p->two->sha1 * (2) mp->local is uninitialized; set to p->two->sha1 * (3) mp->local is null_sha1; set to p->two->sha1 */ assert(is_null_oid(&mp->local) || !oidcmp(&mp->local, &uninitialized)); oidcpy(&mp->local, &p->two->oid); } else { /* modification */ /* * This is a true modification. p->one->sha1 shall * match mp->base, and mp->local shall be uninitialized. * Set mp->local to p->two->sha1. */ assert(!oidcmp(&p->one->oid, &mp->base)); assert(!oidcmp(&mp->local, &uninitialized)); oidcpy(&mp->local, &p->two->oid); } trace_printf("\t\tStored local change for %s: %.7s -> %.7s\n", oid_to_hex(&mp->obj), oid_to_hex(&mp->base), oid_to_hex(&mp->local)); } diff_flush(&opt); clear_pathspec(&opt.pathspec); }
static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, const struct object_id *base, const struct object_id *remote, int *num_changes) { struct diff_options opt; struct notes_merge_pair *changes; int i, len = 0; trace_printf("\tdiff_tree_remote(base = %.7s, remote = %.7s)\n", oid_to_hex(base), oid_to_hex(remote)); diff_setup(&opt); opt.flags.recursive = 1; opt.output_format = DIFF_FORMAT_NO_OUTPUT; diff_setup_done(&opt); diff_tree_oid(base, remote, "", &opt); diffcore_std(&opt); changes = xcalloc(diff_queued_diff.nr, sizeof(struct notes_merge_pair)); for (i = 0; i < diff_queued_diff.nr; i++) { struct diff_filepair *p = diff_queued_diff.queue[i]; struct notes_merge_pair *mp; int occupied; struct object_id obj; if (verify_notes_filepair(p, &obj)) { trace_printf("\t\tCannot merge entry '%s' (%c): " "%.7s -> %.7s. Skipping!\n", p->one->path, p->status, oid_to_hex(&p->one->oid), oid_to_hex(&p->two->oid)); continue; } mp = find_notes_merge_pair_pos(changes, len, &obj, 1, &occupied); if (occupied) { /* We've found an addition/deletion pair */ assert(!oidcmp(&mp->obj, &obj)); if (is_null_oid(&p->one->oid)) { /* addition */ assert(is_null_oid(&mp->remote)); oidcpy(&mp->remote, &p->two->oid); } else if (is_null_oid(&p->two->oid)) { /* deletion */ assert(is_null_oid(&mp->base)); oidcpy(&mp->base, &p->one->oid); } else assert(!"Invalid existing change recorded"); } else { oidcpy(&mp->obj, &obj); oidcpy(&mp->base, &p->one->oid); oidcpy(&mp->local, &uninitialized); oidcpy(&mp->remote, &p->two->oid); len++; } trace_printf("\t\tStored remote change for %s: %.7s -> %.7s\n", oid_to_hex(&mp->obj), oid_to_hex(&mp->base), oid_to_hex(&mp->remote)); } diff_flush(&opt); clear_pathspec(&opt.pathspec); *num_changes = len; return changes; }
/* * We have an origin -- check if the same path exists in the * parent and return an origin structure to represent it. */ static struct blame_origin *find_origin(struct commit *parent, struct blame_origin *origin) { struct blame_origin *porigin; struct diff_options diff_opts; const char *paths[2]; /* First check any existing origins */ for (porigin = parent->util; porigin; porigin = porigin->next) if (!strcmp(porigin->path, origin->path)) { /* * The same path between origin and its parent * without renaming -- the most common case. */ return blame_origin_incref (porigin); } /* See if the origin->path is different between parent * and origin first. Most of the time they are the * same and diff-tree is fairly efficient about this. */ diff_setup(&diff_opts); DIFF_OPT_SET(&diff_opts, RECURSIVE); diff_opts.detect_rename = 0; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; paths[0] = origin->path; paths[1] = NULL; parse_pathspec(&diff_opts.pathspec, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_LITERAL_PATH, "", paths); diff_setup_done(&diff_opts); if (is_null_oid(&origin->commit->object.oid)) do_diff_cache(&parent->tree->object.oid, &diff_opts); else diff_tree_oid(&parent->tree->object.oid, &origin->commit->tree->object.oid, "", &diff_opts); diffcore_std(&diff_opts); if (!diff_queued_diff.nr) { /* The path is the same as parent */ porigin = get_origin(parent, origin->path); oidcpy(&porigin->blob_oid, &origin->blob_oid); porigin->mode = origin->mode; } else { /* * Since origin->path is a pathspec, if the parent * commit had it as a directory, we will see a whole * bunch of deletion of files in the directory that we * do not care about. */ int i; struct diff_filepair *p = NULL; for (i = 0; i < diff_queued_diff.nr; i++) { const char *name; p = diff_queued_diff.queue[i]; name = p->one->path ? p->one->path : p->two->path; if (!strcmp(name, origin->path)) break; } if (!p) die("internal error in blame::find_origin"); switch (p->status) { default: die("internal error in blame::find_origin (%c)", p->status); case 'M': porigin = get_origin(parent, origin->path); oidcpy(&porigin->blob_oid, &p->one->oid); porigin->mode = p->one->mode; break; case 'A': case 'T': /* Did not exist in parent, or type changed */ break; } } diff_flush(&diff_opts); clear_pathspec(&diff_opts.pathspec); return porigin; }
/* * For lines target is suspected for, see if we can find code movement * across file boundary from the parent commit. porigin is the path * in the parent we already tried. */ static void find_copy_in_parent(struct blame_scoreboard *sb, struct blame_entry ***blamed, struct blame_entry **toosmall, struct blame_origin *target, struct commit *parent, struct blame_origin *porigin, int opt) { struct diff_options diff_opts; int i, j; struct blame_list *blame_list; int num_ents; struct blame_entry *unblamed = target->suspects; struct blame_entry *leftover = NULL; if (!unblamed) return; /* nothing remains for this target */ diff_setup(&diff_opts); diff_opts.flags.recursive = 1; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_setup_done(&diff_opts); /* Try "find copies harder" on new path if requested; * we do not want to use diffcore_rename() actually to * match things up; find_copies_harder is set only to * force diff_tree_oid() to feed all filepairs to diff_queue, * and this code needs to be after diff_setup_done(), which * usually makes find-copies-harder imply copy detection. */ if ((opt & PICKAXE_BLAME_COPY_HARDEST) || ((opt & PICKAXE_BLAME_COPY_HARDER) && (!porigin || strcmp(target->path, porigin->path)))) diff_opts.flags.find_copies_harder = 1; if (is_null_oid(&target->commit->object.oid)) do_diff_cache(get_commit_tree_oid(parent), &diff_opts); else diff_tree_oid(get_commit_tree_oid(parent), get_commit_tree_oid(target->commit), "", &diff_opts); if (!diff_opts.flags.find_copies_harder) diffcore_std(&diff_opts); do { struct blame_entry **unblamedtail = &unblamed; blame_list = setup_blame_list(unblamed, &num_ents); for (i = 0; i < diff_queued_diff.nr; i++) { struct diff_filepair *p = diff_queued_diff.queue[i]; struct blame_origin *norigin; mmfile_t file_p; struct blame_entry potential[3]; if (!DIFF_FILE_VALID(p->one)) continue; /* does not exist in parent */ if (S_ISGITLINK(p->one->mode)) continue; /* ignore git links */ if (porigin && !strcmp(p->one->path, porigin->path)) /* find_move already dealt with this path */ continue; norigin = get_origin(parent, p->one->path); oidcpy(&norigin->blob_oid, &p->one->oid); norigin->mode = p->one->mode; fill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob); if (!file_p.ptr) continue; for (j = 0; j < num_ents; j++) { find_copy_in_blob(sb, blame_list[j].ent, norigin, potential, &file_p); copy_split_if_better(sb, blame_list[j].split, potential); decref_split(potential); } blame_origin_decref(norigin); } for (j = 0; j < num_ents; j++) { struct blame_entry *split = blame_list[j].split; if (split[1].suspect && sb->copy_score < blame_entry_score(sb, &split[1])) { split_blame(blamed, &unblamedtail, split, blame_list[j].ent); } else { blame_list[j].ent->next = leftover; leftover = blame_list[j].ent; } decref_split(split); } free(blame_list); *unblamedtail = NULL; toosmall = filter_small(sb, toosmall, &unblamed, sb->copy_score); } while (unblamed); target->suspects = reverse_blame(leftover, NULL); diff_flush(&diff_opts); clear_pathspec(&diff_opts.pathspec); }
int diff_root_tree_oid(const struct object_id *new_oid, const char *base, struct diff_options *opt) { return diff_tree_oid(NULL, new_oid, base, opt); }
static void handle_commit(struct commit *commit, struct rev_info *rev, struct string_list *paths_of_changed_objects) { int saved_output_format = rev->diffopt.output_format; const char *commit_buffer; const char *author, *author_end, *committer, *committer_end; const char *encoding, *message; char *reencoded = NULL; struct commit_list *p; const char *refname; int i; rev->diffopt.output_format = DIFF_FORMAT_CALLBACK; parse_commit_or_die(commit); commit_buffer = get_commit_buffer(commit, NULL); author = strstr(commit_buffer, "\nauthor "); if (!author) die ("Could not find author in commit %s", oid_to_hex(&commit->object.oid)); author++; author_end = strchrnul(author, '\n'); committer = strstr(author_end, "\ncommitter "); if (!committer) die ("Could not find committer in commit %s", oid_to_hex(&commit->object.oid)); committer++; committer_end = strchrnul(committer, '\n'); message = strstr(committer_end, "\n\n"); encoding = find_encoding(committer_end, message); if (message) message += 2; if (commit->parents && get_object_mark(&commit->parents->item->object) != 0 && !full_tree) { parse_commit_or_die(commit->parents->item); diff_tree_oid(get_commit_tree_oid(commit->parents->item), get_commit_tree_oid(commit), "", &rev->diffopt); } else diff_root_tree_oid(get_commit_tree_oid(commit), "", &rev->diffopt); /* Export the referenced blobs, and remember the marks. */ for (i = 0; i < diff_queued_diff.nr; i++) if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode)) export_blob(&diff_queued_diff.queue[i]->two->oid); refname = *revision_sources_at(&revision_sources, commit); if (anonymize) { refname = anonymize_refname(refname); anonymize_ident_line(&committer, &committer_end); anonymize_ident_line(&author, &author_end); } mark_next_object(&commit->object); if (anonymize) reencoded = anonymize_commit_message(message); else if (!is_encoding_utf8(encoding)) reencoded = reencode_string(message, "UTF-8", encoding); if (!commit->parents) printf("reset %s\n", refname); printf("commit %s\nmark :%"PRIu32"\n%.*s\n%.*s\ndata %u\n%s", refname, last_idnum, (int)(author_end - author), author, (int)(committer_end - committer), committer, (unsigned)(reencoded ? strlen(reencoded) : message ? strlen(message) : 0), reencoded ? reencoded : message ? message : ""); free(reencoded); unuse_commit_buffer(commit, commit_buffer); for (i = 0, p = commit->parents; p; p = p->next) { int mark = get_object_mark(&p->item->object); if (!mark) continue; if (i == 0) printf("from :%d\n", mark); else printf("merge :%d\n", mark); i++; } if (full_tree) printf("deleteall\n"); log_tree_diff_flush(rev); string_list_clear(paths_of_changed_objects, 0); rev->diffopt.output_format = saved_output_format; printf("\n"); show_progress(); }