blame: extract find_single_final
[gitweb.git] / builtin / blame.c
index 662339fe63eafe8b554d5189480f37557df4c5cb..7b075583cf504bc121a9de327f5600ec4b63ac96 100644 (file)
@@ -26,6 +26,7 @@
 #include "userdiff.h"
 #include "line-range.h"
 #include "line-log.h"
+#include "dir.h"
 
 static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
 
@@ -1364,8 +1365,15 @@ static void pass_whole_blame(struct scoreboard *sb,
  */
 static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)
 {
-       if (!reverse)
+       if (!reverse) {
+               if (revs->first_parent_only &&
+                   commit->parents &&
+                   commit->parents->next) {
+                       free_commit_list(commit->parents->next);
+                       commit->parents->next = NULL;
+               }
                return commit->parents;
+       }
        return lookup_decoration(&revs->children, &commit->object);
 }
 
@@ -2151,16 +2159,6 @@ static void sanity_check_refcnt(struct scoreboard *sb)
        }
 }
 
-/*
- * Used for the command line parsing; check if the path exists
- * in the working tree.
- */
-static int has_string_in_work_tree(const char *path)
-{
-       struct stat st;
-       return !lstat(path, &st);
-}
-
 static unsigned parse_score(const char *arg)
 {
        char *end;
@@ -2185,6 +2183,14 @@ static int git_blame_config(const char *var, const char *value, void *cb)
                blank_boundary = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "blame.showemail")) {
+               int *output_option = cb;
+               if (git_config_bool(var, value))
+                       *output_option |= OUTPUT_SHOW_EMAIL;
+               else
+                       *output_option &= ~OUTPUT_SHOW_EMAIL;
+               return 0;
+       }
        if (!strcmp(var, "blame.date")) {
                if (!value)
                        return config_error_nonbool(var);
@@ -2390,16 +2396,11 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
        return commit;
 }
 
-static char *prepare_final(struct scoreboard *sb)
+static struct object_array_entry *find_single_final(struct rev_info *revs)
 {
        int i;
-       const char *final_commit_name = NULL;
-       struct rev_info *revs = sb->revs;
+       struct object_array_entry *found = NULL;
 
-       /*
-        * There must be one and only one positive commit in the
-        * revs->pending array.
-        */
        for (i = 0; i < revs->pending.nr; i++) {
                struct object *obj = revs->pending.objects[i].item;
                if (obj->flags & UNINTERESTING)
@@ -2408,14 +2409,24 @@ static char *prepare_final(struct scoreboard *sb)
                        obj = deref_tag(obj, NULL, 0);
                if (obj->type != OBJ_COMMIT)
                        die("Non commit %s?", revs->pending.objects[i].name);
-               if (sb->final)
+               if (found)
                        die("More than one commit to dig from %s and %s?",
                            revs->pending.objects[i].name,
-                           final_commit_name);
-               sb->final = (struct commit *) obj;
-               final_commit_name = revs->pending.objects[i].name;
+                           found->name);
+               found = &(revs->pending.objects[i]);
+       }
+       return found;
+}
+
+static char *prepare_final(struct scoreboard *sb)
+{
+       struct object_array_entry *found = find_single_final(sb->revs);
+       if (found) {
+               sb->final = (struct commit *) found->item;
+               return xstrdup(found->name);
+       } else {
+               return NULL;
        }
-       return xstrdup_or_null(final_commit_name);
 }
 
 static char *prepare_initial(struct scoreboard *sb)
@@ -2529,7 +2540,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        unsigned int range_i;
        long anchor;
 
-       git_config(git_blame_config, NULL);
+       git_config(git_blame_config, &output_option);
        init_revisions(&revs, NULL);
        revs.date_mode = blame_date_mode;
        DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
@@ -2656,14 +2667,14 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                if (argc < 2)
                        usage_with_options(blame_opt_usage, options);
                path = add_prefix(prefix, argv[argc - 1]);
-               if (argc == 3 && !has_string_in_work_tree(path)) { /* (2b) */
+               if (argc == 3 && !file_exists(path)) { /* (2b) */
                        path = add_prefix(prefix, argv[1]);
                        argv[1] = argv[2];
                }
                argv[argc - 1] = "--";
 
                setup_work_tree();
-               if (!has_string_in_work_tree(path))
+               if (!file_exists(path))
                        die_errno("cannot stat path '%s'", path);
        }
 
@@ -2678,6 +2689,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        }
        else if (contents_from)
                die("--contents and --children do not blend well.");
+       else if (revs.first_parent_only)
+               die("combining --first-parent and --reverse is not supported");
        else {
                final_commit_name = prepare_initial(&sb);
                sb.commits.compare = compare_commits_by_reverse_commit_date;