parseopt: add PARSE_OPT_NO_INTERNAL_HELP
[gitweb.git] / builtin-diff.c
index 7c2a8412fa8cefa2f53a5f7c2035ea9f2536e1c4..d75d69bf5774ffd402bbeec47b9a0e0800554d63 100644 (file)
@@ -21,7 +21,7 @@ struct blobinfo {
 };
 
 static const char builtin_diff_usage[] =
-"git-diff <options> <rev>{0,2} -- <path>*";
+"git diff <options> <rev>{0,2} -- <path>*";
 
 static void stuff_change(struct diff_options *opt,
                         unsigned old_mode, unsigned new_mode,
@@ -74,6 +74,8 @@ static int builtin_diff_b_f(struct rev_info *revs,
        if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
                die("'%s': not a regular file or symlink", path);
 
+       diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/");
+
        if (blob[0].mode == S_IFINVALID)
                blob[0].mode = canon_mode(st.st_mode);
 
@@ -116,12 +118,14 @@ static int builtin_diff_index(struct rev_info *revs,
        int cached = 0;
        while (1 < argc) {
                const char *arg = argv[1];
-               if (!strcmp(arg, "--cached"))
+               if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged"))
                        cached = 1;
                else
                        usage(builtin_diff_usage);
                argv++; argc--;
        }
+       if (!cached)
+               setup_work_tree();
        /*
         * Make sure there is one revision (i.e. pending object),
         * and there is no revision filtering parameters.
@@ -130,8 +134,8 @@ static int builtin_diff_index(struct rev_info *revs,
            revs->max_count != -1 || revs->min_age != -1 ||
            revs->max_age != -1)
                usage(builtin_diff_usage);
-       if (read_cache() < 0) {
-               perror("read_cache");
+       if (read_cache_preload(revs->diffopt.paths) < 0) {
+               perror("read_cache_preload");
                return -1;
        }
        return run_diff_index(revs, cached);
@@ -173,10 +177,8 @@ static int builtin_diff_combined(struct rev_info *revs,
        if (!revs->dense_combined_merges && !revs->combine_merges)
                revs->dense_combined_merges = revs->combine_merges = 1;
        parent = xmalloc(ents * sizeof(*parent));
-       /* Again, the revs are all reverse */
        for (i = 0; i < ents; i++)
-               hashcpy((unsigned char *)(parent + i),
-                       ent[ents - 1 - i].item->sha1);
+               hashcpy((unsigned char *)(parent + i), ent[i].item->sha1);
        diff_tree_combined(parent[0], parent + 1, ents - 1,
                           revs->dense_combined_merges, revs);
        return 0;
@@ -202,6 +204,44 @@ static void refresh_index_quietly(void)
        rollback_lock_file(lock_file);
 }
 
+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
+                       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;
@@ -230,42 +270,51 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
         * 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);
-       git_config(git_diff_ui_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;
 
-       if (!setup_diff_no_index(&rev, argc, argv, nongit, prefix))
-               argc = 0;
-       else
-               argc = setup_revisions(argc, argv, &rev, NULL);
+       /* 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");
        }
-       if (rev.diffopt.prefix && nongit) {
-               rev.diffopt.prefix = NULL;
-               rev.diffopt.prefix_length = 0;
-       }
-       DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
+
        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))
+       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
+       /*
+        * Do we have --cached and not have a pending object, then
         * default to HEAD by hand.  Eek.
         */
        if (!rev.pending.nr) {
@@ -274,7 +323,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                        const char *arg = argv[i];
                        if (!strcmp(arg, "--"))
                                break;
-                       else if (!strcmp(arg, "--cached")) {
+                       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)");
@@ -333,7 +383,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        if (!ents) {
                switch (blobs) {
                case 0:
-                       result = run_diff_files_cmd(&rev, argc, argv);
+                       result = builtin_diff_files(&rev, argc, argv);
                        break;
                case 1:
                        if (paths != 1)