static int check_changes_tracked_files(struct pathspec ps) { int result; struct rev_info rev; struct object_id dummy; /* No initial commit. */ if (get_oid("HEAD", &dummy)) return -1; if (read_cache() < 0) return -1; init_revisions(&rev, NULL); rev.prune_data = ps; rev.diffopt.flags.quick = 1; rev.diffopt.flags.ignore_submodules = 1; rev.abbrev = 0; add_head_to_pending(&rev); diff_setup_done(&rev.diffopt); result = run_diff_index(&rev, 1); if (diff_result_code(&rev.diffopt, result)) return 1; object_array_clear(&rev.pending); result = run_diff_files(&rev, 0); if (diff_result_code(&rev.diffopt, result)) return 1; return 0; }
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); }
static int cmd_log_walk(struct rev_info *rev) { struct commit *commit; if (rev->early_output) setup_early_output(rev); if (prepare_revision_walk(rev)) die("revision walk setup failed"); if (rev->early_output) finish_early_output(rev); /* * For --check and --exit-code, the exit code is based on CHECK_FAILED * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to * retain that state information if replacing rev->diffopt in this loop */ while ((commit = get_revision(rev)) != NULL) { log_tree_commit(rev, commit); if (!rev->reflog_info) { /* we allow cycles in reflog ancestry */ free(commit->buffer); commit->buffer = NULL; } free_commit_list(commit->parents); commit->parents = NULL; } if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF && DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) { return 02; } return diff_result_code(&rev->diffopt, 0); }
void diff_no_index(struct rev_info *revs, int argc, const char **argv, const char *prefix) { int i, prefixlen; const char *paths[2]; diff_setup(&revs->diffopt); for (i = 1; i < argc - 2; ) { int j; if (!strcmp(argv[i], "--no-index")) i++; else if (!strcmp(argv[i], "--")) i++; else { j = diff_opt_parse(&revs->diffopt, argv + i, argc - i); if (j <= 0) die("invalid diff option/value: %s", argv[i]); i += j; } } prefixlen = prefix ? strlen(prefix) : 0; for (i = 0; i < 2; i++) { const char *p = argv[argc - 2 + i]; if (!strcmp(p, "-")) /* * stdin should be spelled as "-"; if you have * path that is "-", spell it as "./-". */ p = file_from_standard_input; else if (prefixlen) p = xstrdup(prefix_filename(prefix, prefixlen, p)); paths[i] = p; } revs->diffopt.skip_stat_unmatch = 1; if (!revs->diffopt.output_format) revs->diffopt.output_format = DIFF_FORMAT_PATCH; DIFF_OPT_SET(&revs->diffopt, NO_INDEX); revs->max_count = -2; diff_setup_done(&revs->diffopt); setup_diff_pager(&revs->diffopt); DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); if (queue_diff(&revs->diffopt, paths[0], paths[1])) exit(1); diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/"); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); /* * The return code for --no-index imitates diff(1): * 0 = no changes, 1 = changes, else error */ exit(diff_result_code(&revs->diffopt, 0)); }
int cmd_diff_files(int argc, const char **argv, const char *prefix) { struct rev_info rev; int result; unsigned options = 0; if (argc == 2 && !strcmp(argv[1], "-h")) usage(diff_files_usage); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ init_revisions(&rev, prefix); rev.abbrev = 0; precompose_argv(argc, argv); argc = setup_revisions(argc, argv, &rev, NULL); while (1 < argc && argv[1][0] == '-') { if (!strcmp(argv[1], "--base")) rev.max_count = 1; else if (!strcmp(argv[1], "--ours")) rev.max_count = 2; else if (!strcmp(argv[1], "--theirs")) rev.max_count = 3; else if (!strcmp(argv[1], "-q")) options |= DIFF_SILENT_ON_REMOVED; else usage(diff_files_usage); argv++; argc--; } if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; /* * Make sure there are NO revision (i.e. pending object) parameter, * rev.max_count is reasonable (0 <= n <= 3), and * there is no other revision filtering parameters. */ if (rev.pending.nr || rev.min_age != -1 || rev.max_age != -1 || 3 < rev.max_count) usage(diff_files_usage); /* * "diff-files --base -p" should not combine merges because it * was not asked to. "diff-files -c -p" should not densify * (the user should ask with "diff-files --cc" explicitly). */ if (rev.max_count == -1 && !rev.combine_merges && (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) rev.combine_merges = rev.dense_combined_merges = 1; if (read_cache_preload(&rev.diffopt.pathspec) < 0) { perror("read_cache_preload"); return -1; } result = run_diff_files(&rev, options); return diff_result_code(&rev.diffopt, result); }
/** * Returns 1 if there are unstaged changes, 0 otherwise. */ static int has_unstaged_changes(const char *prefix) { struct rev_info rev_info; int result; init_revisions(&rev_info, prefix); DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES); DIFF_OPT_SET(&rev_info.diffopt, QUICK); diff_setup_done(&rev_info.diffopt); result = run_diff_files(&rev_info, 0); return diff_result_code(&rev_info.diffopt, result); }
static int cmd_log_walk(struct rev_info *rev) { struct commit *commit; int saved_nrl = 0; int saved_dcctc = 0; if (rev->early_output) setup_early_output(rev); if (prepare_revision_walk(rev)) die(_("revision walk setup failed")); if (rev->early_output) finish_early_output(rev); /* * For --check and --exit-code, the exit code is based on CHECK_FAILED * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to * retain that state information if replacing rev->diffopt in this loop */ while ((commit = get_revision(rev)) != NULL) { if (!log_tree_commit(rev, commit) && rev->max_count >= 0) /* * We decremented max_count in get_revision, * but we didn't actually show the commit. */ rev->max_count++; if (!rev->reflog_info) { /* we allow cycles in reflog ancestry */ free(commit->buffer); commit->buffer = NULL; } free_commit_list(commit->parents); commit->parents = NULL; if (saved_nrl < rev->diffopt.needed_rename_limit) saved_nrl = rev->diffopt.needed_rename_limit; if (rev->diffopt.degraded_cc_to_c) saved_dcctc = 1; } rev->diffopt.degraded_cc_to_c = saved_dcctc; rev->diffopt.needed_rename_limit = saved_nrl; if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF && DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) { return 02; } return diff_result_code(&rev->diffopt, 0); }
int cmd_diff_index(int argc, const char **argv, const char *prefix) { struct rev_info rev; int cached = 0; int i; int result; if (argc == 2 && !strcmp(argv[1], "-h")) usage(diff_cache_usage); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ init_revisions(&rev, prefix); rev.abbrev = 0; precompose_argv(argc, argv); argc = setup_revisions(argc, argv, &rev, NULL); for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--cached")) cached = 1; else usage(diff_cache_usage); } if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; /* * Make sure there is one revision (i.e. pending object), * and there is no revision filtering parameters. */ if (rev.pending.nr != 1 || rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1) usage(diff_cache_usage); if (!cached) { setup_work_tree(); if (read_cache_preload(&rev.diffopt.pathspec) < 0) { perror("read_cache_preload"); return -1; } } else if (read_cache() < 0) { perror("read_cache"); return -1; } result = run_diff_index(&rev, cached); UNLEAK(rev); return diff_result_code(&rev.diffopt, result); }
/** * Returns 1 if there are uncommitted changes, 0 otherwise. */ static int has_uncommitted_changes(const char *prefix) { struct rev_info rev_info; int result; if (is_cache_unborn()) return 0; init_revisions(&rev_info, prefix); DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES); DIFF_OPT_SET(&rev_info.diffopt, QUICK); add_head_to_pending(&rev_info); diff_setup_done(&rev_info.diffopt); result = run_diff_index(&rev_info, 1); return diff_result_code(&rev_info.diffopt, result); }
int cmd_diff_files(int argc, const char **argv, const char *prefix) { struct rev_info rev; int nongit; int result; prefix = setup_git_directory_gently(&nongit); init_revisions(&rev, prefix); git_config(git_diff_basic_config); /* no "diff" UI options */ rev.abbrev = 0; if (!setup_diff_no_index(&rev, argc, argv, nongit, prefix)) argc = 0; else argc = setup_revisions(argc, argv, &rev, NULL); if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; result = run_diff_files_cmd(&rev, argc, argv); return diff_result_code(&rev.diffopt, result); }
static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv) { int result; unsigned int options = 0; while (1 < argc && argv[1][0] == '-') { if (!strcmp(argv[1], "--base")) revs->max_count = 1; else if (!strcmp(argv[1], "--ours")) revs->max_count = 2; else if (!strcmp(argv[1], "--theirs")) revs->max_count = 3; else if (!strcmp(argv[1], "-q")) options |= DIFF_SILENT_ON_REMOVED; else if (!strcmp(argv[1], "-h")) usage(builtin_diff_usage); else return error("invalid option: %s", argv[1]); argv++; argc--; } /* * "diff --base" should not combine merges because it was not * asked to. "diff -c" should not densify (if the user wants * dense one, --cc can be explicitly asked for, or just rely * on the default). */ if (revs->max_count == -1 && !revs->combine_merges && (revs->diffopt.output_format & DIFF_FORMAT_PATCH)) revs->combine_merges = revs->dense_combined_merges = 1; setup_work_tree(); if (read_cache_preload(revs->diffopt.paths) < 0) { perror("read_cache_preload"); return -1; } result = run_diff_files(revs, options); return diff_result_code(&revs->diffopt, 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(); init_diff_ui_defaults(); git_config(git_diff_ui_config, NULL); precompose_argv(argc, argv); init_revisions(&rev, prefix); if (no_index && argc != i + 2) { if (no_index == DIFF_NO_INDEX_IMPLICIT) { /* * There was no --no-index and there were not two * paths. It is possible that the user intended * to do an inside-repository operation. */ fprintf(stderr, "Not a git repository\n"); fprintf(stderr, "To compare two paths outside a working tree:\n"); } /* Give the usage message for non-repository usage and exit. */ usagef("git diff %s <path> <path>", no_index == DIFF_NO_INDEX_EXPLICIT ? "--no-index" : "[--no-index]"); } if (no_index) /* If this is a no-index diff, just run it and exit there. */ diff_no_index(&rev, argc, argv); /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; /* Scale to real terminal size and respect statGraphWidth config */ rev.diffopt.stat_width = -1; rev.diffopt.stat_graph_width = -1; /* Default to let external and textconv be used */ DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL); DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV); if (nongit) die(_("Not a git repository")); argc = setup_revisions(argc, argv, &rev, NULL); if (!rev.diffopt.output_format) { rev.diffopt.output_format = DIFF_FORMAT_PATCH; diff_setup_done(&rev.diffopt); } DIFF_OPT_SET(&rev.diffopt, RECURSIVE); setup_diff_pager(&rev.diffopt); /* * Do we have --cached and not have a pending object, then * default to HEAD by hand. Eek. */ if (!rev.pending.nr) { int i; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--")) break; else if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged")) { add_head_to_pending(&rev); if (!rev.pending.nr) { struct tree *tree; tree = lookup_tree(EMPTY_TREE_SHA1_BIN); add_pending_object(&rev, &tree->object, "HEAD"); } break; } } } for (i = 0; i < rev.pending.nr; i++) { struct object_array_entry *entry = &rev.pending.objects[i]; struct object *obj = entry->item; const char *name = entry->name; int flags = (obj->flags & UNINTERESTING); if (!obj->parsed) obj = parse_object(obj->oid.hash); obj = deref_tag(obj, NULL, 0); if (!obj) die(_("invalid object '%s' given."), name); if (obj->type == OBJ_COMMIT) obj = &((struct commit *)obj)->tree->object; if (obj->type == OBJ_TREE) { obj->flags |= flags; add_object_array(obj, name, &ent); } else if (obj->type == OBJ_BLOB) { if (2 <= blobs) die(_("more than two blobs given: '%s'"), name); hashcpy(blob[blobs].sha1, obj->oid.hash); blob[blobs].name = name; blob[blobs].mode = entry->mode; blobs++; } else { die(_("unhandled object '%s' given."), name); } } if (rev.prune_data.nr) paths += rev.prune_data.nr; /* * Now, do the arguments look reasonable? */ if (!ent.nr) { switch (blobs) { case 0: result = builtin_diff_files(&rev, argc, argv); break; case 1: if (paths != 1) usage(builtin_diff_usage); result = builtin_diff_b_f(&rev, argc, argv, blob); break; case 2: if (paths) usage(builtin_diff_usage); result = builtin_diff_blobs(&rev, argc, argv, blob); break; default: usage(builtin_diff_usage); } } else if (blobs) usage(builtin_diff_usage); else if (ent.nr == 1) result = builtin_diff_index(&rev, argc, argv); else if (ent.nr == 2) result = builtin_diff_tree(&rev, argc, argv, &ent.objects[0], &ent.objects[1]); else if (ent.objects[0].item->flags & UNINTERESTING) { /* * diff A...B where there is at least one merge base * between A and B. We have ent.objects[0] == * merge-base, ent.objects[ents-2] == A, and * ent.objects[ents-1] == B. Show diff between the * base and B. Note that we pick one merge base at * random if there are more than one. */ result = builtin_diff_tree(&rev, argc, argv, &ent.objects[0], &ent.objects[ent.nr-1]); } else result = builtin_diff_combined(&rev, argc, argv, ent.objects, ent.nr); result = diff_result_code(&rev.diffopt, result); if (1 < rev.diffopt.skip_stat_unmatch) refresh_index_quietly(); return result; }
int cmd_diff(int argc, const char **argv, const char *prefix) { int i; struct rev_info rev; struct object_array_entry ent[100]; int ents = 0, blobs = 0, paths = 0; const char *path = NULL; struct blobinfo blob[2]; int nongit; int result = 0; /* * We could get N tree-ish in the rev.pending_objects list. * Also there could be M blobs there, and P pathspecs. * * N=0, M=0: * cache vs files (diff-files) * N=0, M=2: * compare two random blobs. P must be zero. * N=0, M=1, P=1: * compare a blob with a working tree file. * * N=1, M=0: * tree vs cache (diff-index --cached) * * N=2, M=0: * tree vs tree (diff-tree) * * N=0, M=0, P=2: * compare two filesystem entities (aka --no-index). * * Other cases are errors. */ prefix = setup_git_directory_gently(&nongit); gitmodules_config(); git_config(git_diff_ui_config, NULL); if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; init_revisions(&rev, prefix); /* If this is a no-index diff, just run it and exit there. */ diff_no_index(&rev, argc, argv, nongit, prefix); /* Otherwise, we are doing the usual "git" diff */ rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index; /* Default to let external and textconv be used */ DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL); DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV); if (nongit) die("Not a git repository"); argc = setup_revisions(argc, argv, &rev, NULL); if (!rev.diffopt.output_format) { rev.diffopt.output_format = DIFF_FORMAT_PATCH; if (diff_setup_done(&rev.diffopt) < 0) die("diff_setup_done failed"); } DIFF_OPT_SET(&rev.diffopt, RECURSIVE); /* * If the user asked for our exit code then don't start a * pager or we would end up reporting its exit code instead. */ if (!DIFF_OPT_TST(&rev.diffopt, EXIT_WITH_STATUS) && check_pager_config("diff") != 0) setup_pager(); /* * Do we have --cached and not have a pending object, then * default to HEAD by hand. Eek. */ if (!rev.pending.nr) { int i; for (i = 1; i < argc; i++) { const char *arg = argv[i]; if (!strcmp(arg, "--")) break; else if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged")) { add_head_to_pending(&rev); if (!rev.pending.nr) { struct tree *tree; tree = lookup_tree((const unsigned char*)EMPTY_TREE_SHA1_BIN); add_pending_object(&rev, &tree->object, "HEAD"); } break; } } } for (i = 0; i < rev.pending.nr; i++) { struct object_array_entry *list = rev.pending.objects+i; struct object *obj = list->item; const char *name = list->name; int flags = (obj->flags & UNINTERESTING); if (!obj->parsed) obj = parse_object(obj->sha1); obj = deref_tag(obj, NULL, 0); if (!obj) die("invalid object '%s' given.", name); if (obj->type == OBJ_COMMIT) obj = &((struct commit *)obj)->tree->object; if (obj->type == OBJ_TREE) { if (ARRAY_SIZE(ent) <= ents) die("more than %d trees given: '%s'", (int) ARRAY_SIZE(ent), name); obj->flags |= flags; ent[ents].item = obj; ent[ents].name = name; ents++; continue; } if (obj->type == OBJ_BLOB) { if (2 <= blobs) die("more than two blobs given: '%s'", name); hashcpy(blob[blobs].sha1, obj->sha1); blob[blobs].name = name; blob[blobs].mode = list->mode; blobs++; continue; } die("unhandled object '%s' given.", name); } if (rev.prune_data.nr) { if (!path) path = rev.prune_data.items[0].match; paths += rev.prune_data.nr; } /* * Now, do the arguments look reasonable? */ if (!ents) { switch (blobs) { case 0: result = builtin_diff_files(&rev, argc, argv); break; case 1: if (paths != 1) usage(builtin_diff_usage); result = builtin_diff_b_f(&rev, argc, argv, blob, path); break; case 2: if (paths) usage(builtin_diff_usage); result = builtin_diff_blobs(&rev, argc, argv, blob); break; default: usage(builtin_diff_usage); } } else if (blobs) usage(builtin_diff_usage); else if (ents == 1) result = builtin_diff_index(&rev, argc, argv); else if (ents == 2) result = builtin_diff_tree(&rev, argc, argv, ent); else if (ent[0].item->flags & UNINTERESTING) { /* * diff A...B where there is at least one merge base * between A and B. We have ent[0] == merge-base, * ent[ents-2] == A, and ent[ents-1] == B. Show diff * between the base and B. Note that we pick one * merge base at random if there are more than one. */ ent[1] = ent[ents-1]; result = builtin_diff_tree(&rev, argc, argv, ent); } else result = builtin_diff_combined(&rev, argc, argv, ent, ents); result = diff_result_code(&rev.diffopt, result); if (1 < rev.diffopt.skip_stat_unmatch) refresh_index_quietly(); return result; }
void diff_no_index(struct rev_info *revs, int argc, const char **argv, int nongit, const char *prefix) { int i, prefixlen; int no_index = 0; unsigned options = 0; const char *paths[2]; /* 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 = 1; if (argv[i][0] != '-') break; } if (!no_index && !nongit) { /* * Inside a git repository, without --no-index. Only * when a path outside the repository is given, * e.g. "git diff /var/tmp/[12]", or "git diff * Makefile /var/tmp/Makefile", allow it to be used as * a colourful "diff" replacement. */ if ((argc != i + 2) || (path_inside_repo(prefix, argv[i]) && path_inside_repo(prefix, argv[i+1]))) return; } if (argc != i + 2) usagef("git diff %s <path> <path>", no_index ? "--no-index" : "[--no-index]"); diff_setup(&revs->diffopt); for (i = 1; i < argc - 2; ) { int j; if (!strcmp(argv[i], "--no-index")) i++; else if (!strcmp(argv[i], "-q")) { options |= DIFF_SILENT_ON_REMOVED; i++; } else if (!strcmp(argv[i], "--")) i++; else { j = diff_opt_parse(&revs->diffopt, argv + i, argc - i); if (!j) die("invalid diff option/value: %s", argv[i]); i += j; } } prefixlen = prefix ? strlen(prefix) : 0; for (i = 0; i < 2; i++) { const char *p = argv[argc - 2 + i]; if (!strcmp(p, "-")) /* * stdin should be spelled as "-"; if you have * path that is "-", spell it as "./-". */ p = file_from_standard_input; else if (prefixlen) p = xstrdup(prefix_filename(prefix, prefixlen, p)); paths[i] = p; } revs->diffopt.skip_stat_unmatch = 1; if (!revs->diffopt.output_format) revs->diffopt.output_format = DIFF_FORMAT_PATCH; DIFF_OPT_SET(&revs->diffopt, NO_INDEX); revs->max_count = -2; diff_setup_done(&revs->diffopt); setup_diff_pager(&revs->diffopt); DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); if (queue_diff(&revs->diffopt, paths[0], paths[1])) exit(1); diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/"); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); /* * The return code for --no-index imitates diff(1): * 0 = no changes, 1 = changes, else error */ exit(diff_result_code(&revs->diffopt, 0)); }
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); }
void diff_no_index(struct repository *r, struct rev_info *revs, int argc, const char **argv) { int i; const char *paths[2]; struct strbuf replacement = STRBUF_INIT; const char *prefix = revs->prefix; /* * FIXME: --no-index should not look at index and we should be * able to pass NULL repo. Maybe later. */ repo_diff_setup(r, &revs->diffopt); for (i = 1; i < argc - 2; ) { int j; if (!strcmp(argv[i], "--no-index")) i++; else if (!strcmp(argv[i], "--")) i++; else { j = diff_opt_parse(&revs->diffopt, argv + i, argc - i, revs->prefix); if (j <= 0) die("invalid diff option/value: %s", argv[i]); i += j; } } for (i = 0; i < 2; i++) { const char *p = argv[argc - 2 + i]; if (!strcmp(p, "-")) /* * stdin should be spelled as "-"; if you have * path that is "-", spell it as "./-". */ p = file_from_standard_input; else if (prefix) p = prefix_filename(prefix, p); paths[i] = p; } fixup_paths(paths, &replacement); revs->diffopt.skip_stat_unmatch = 1; if (!revs->diffopt.output_format) revs->diffopt.output_format = DIFF_FORMAT_PATCH; revs->diffopt.flags.no_index = 1; revs->diffopt.flags.relative_name = 1; revs->diffopt.prefix = prefix; revs->max_count = -2; diff_setup_done(&revs->diffopt); setup_diff_pager(&revs->diffopt); revs->diffopt.flags.exit_with_status = 1; if (queue_diff(&revs->diffopt, paths[0], paths[1])) exit(1); diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/"); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); strbuf_release(&replacement); /* * The return code for --no-index imitates diff(1): * 0 = no changes, 1 = changes, else error */ exit(diff_result_code(&revs->diffopt, 0)); }
int cmd_diff_tree(int argc, const char **argv, const char *prefix) { int nr_sha1; 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; init_revisions(opt, prefix); gitmodules_config(); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ 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; 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 be equal to "a..b", so we * reverse the order of the objects if the second one * is marked UNINTERESTING. */ nr_sha1 = opt->pending.nr; switch (nr_sha1) { case 0: if (!read_stdin) usage(diff_tree_usage); break; case 1: tree1 = opt->pending.objects[0].item; diff_tree_commit_sha1(tree1->sha1); break; case 2: tree1 = opt->pending.objects[0].item; tree2 = opt->pending.objects[1].item; if (tree2->flags & UNINTERESTING) { struct object *tmp = tree2; tree2 = tree1; tree1 = tmp; } diff_tree_sha1(tree1->sha1, tree2->sha1, "", &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)) { unsigned char sha1[20]; if (get_sha1_hex(line, sha1)) { 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); }
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; 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); 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; /* 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; }