Merge branch 'tb/blame-force-read-cache-to-workaround-safe-crlf'
authorJunio C Hamano <gitster@pobox.com>
Wed, 13 Apr 2016 21:12:40 +0000 (14:12 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 13 Apr 2016 21:12:41 +0000 (14:12 -0700)
When running "git blame $path" with unnormalized data in the index
for the path, the data in the working tree was blamed, even though
"git add" would not have changed what is already in the index, due
to "safe crlf" that disables the line-end conversion. It has been
corrected.

* tb/blame-force-read-cache-to-workaround-safe-crlf:
correct blame for files commited with CRLF

1  2 
builtin/blame.c
t/t8003-blame-corner-cases.sh
diff --combined builtin/blame.c
index e982fb81379f57152e34eeda706a57fa1ea4c143,a5cabf79eaa7511495c19a4c53ac141344b0e888..21f42b0b62b81b637f1cc9589cd6c0306a93d05e
@@@ -6,7 -6,6 +6,7 @@@
   */
  
  #include "cache.h"
 +#include "refs.h"
  #include "builtin.h"
  #include "blob.h"
  #include "commit.h"
@@@ -28,7 -27,6 +28,7 @@@
  #include "line-range.h"
  #include "line-log.h"
  #include "dir.h"
 +#include "progress.h"
  
  static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
  
@@@ -51,9 -49,8 +51,9 @@@ static int incremental
  static int xdl_opts;
  static int abbrev = -1;
  static int no_whole_file_rename;
 +static int show_progress;
  
 -static enum date_mode blame_date_mode = DATE_ISO8601;
 +static struct date_mode blame_date_mode = { DATE_ISO8601 };
  static size_t blame_date_width;
  
  static struct string_list mailmap;
@@@ -129,11 -126,6 +129,11 @@@ struct origin 
        char path[FLEX_ARRAY];
  };
  
 +struct progress_info {
 +      struct progress *progress;
 +      int blamed_lines;
 +};
 +
  static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen,
                      xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)
  {
@@@ -466,11 -458,12 +466,11 @@@ static void queue_blames(struct scorebo
  static struct origin *make_origin(struct commit *commit, const char *path)
  {
        struct origin *o;
 -      o = xcalloc(1, sizeof(*o) + strlen(path) + 1);
 +      FLEX_ALLOC_STR(o, path, path);
        o->commit = commit;
        o->refcnt = 1;
        o->next = commit->util;
        commit->util = o;
 -      strcpy(o->path, path);
        return o;
  }
  
@@@ -511,7 -504,7 +511,7 @@@ static int fill_blob_sha1_and_mode(stru
  {
        if (!is_null_sha1(origin->blob_sha1))
                return 0;
 -      if (get_tree_entry(origin->commit->object.sha1,
 +      if (get_tree_entry(origin->commit->object.oid.hash,
                           origin->path,
                           origin->blob_sha1, &origin->mode))
                goto error_out;
@@@ -562,11 -555,11 +562,11 @@@ static struct origin *find_origin(struc
                       PATHSPEC_LITERAL_PATH, "", paths);
        diff_setup_done(&diff_opts);
  
 -      if (is_null_sha1(origin->commit->object.sha1))
 -              do_diff_cache(parent->tree->object.sha1, &diff_opts);
 +      if (is_null_oid(&origin->commit->object.oid))
 +              do_diff_cache(parent->tree->object.oid.hash, &diff_opts);
        else
 -              diff_tree_sha1(parent->tree->object.sha1,
 -                             origin->commit->tree->object.sha1,
 +              diff_tree_sha1(parent->tree->object.oid.hash,
 +                             origin->commit->tree->object.oid.hash,
                               "", &diff_opts);
        diffcore_std(&diff_opts);
  
@@@ -632,11 -625,11 +632,11 @@@ static struct origin *find_rename(struc
        diff_opts.single_follow = origin->path;
        diff_setup_done(&diff_opts);
  
 -      if (is_null_sha1(origin->commit->object.sha1))
 -              do_diff_cache(parent->tree->object.sha1, &diff_opts);
 +      if (is_null_oid(&origin->commit->object.oid))
 +              do_diff_cache(parent->tree->object.oid.hash, &diff_opts);
        else
 -              diff_tree_sha1(parent->tree->object.sha1,
 -                             origin->commit->tree->object.sha1,
 +              diff_tree_sha1(parent->tree->object.oid.hash,
 +                             origin->commit->tree->object.oid.hash,
                               "", &diff_opts);
        diffcore_std(&diff_opts);
  
@@@ -982,8 -975,8 +982,8 @@@ static void pass_blame_to_parent(struc
  
        if (diff_hunks(&file_p, &file_o, 0, blame_chunk_cb, &d))
                die("unable to generate diff (%s -> %s)",
 -                  sha1_to_hex(parent->commit->object.sha1),
 -                  sha1_to_hex(target->commit->object.sha1));
 +                  oid_to_hex(&parent->commit->object.oid),
 +                  oid_to_hex(&target->commit->object.oid));
        /* The rest are the same as the parent */
        blame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);
        *d.dstq = NULL;
@@@ -1131,7 -1124,7 +1131,7 @@@ static void find_copy_in_blob(struct sc
        memset(split, 0, sizeof(struct blame_entry [3]));
        if (diff_hunks(file_p, &file_o, 1, handle_split_cb, &d))
                die("unable to generate diff (%s)",
 -                  sha1_to_hex(parent->commit->object.sha1));
 +                  oid_to_hex(&parent->commit->object.oid));
        /* remainder, if any, all match the preimage */
        handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
  }
@@@ -1280,11 -1273,11 +1280,11 @@@ static void find_copy_in_parent(struct 
                && (!porigin || strcmp(target->path, porigin->path))))
                DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
  
 -      if (is_null_sha1(target->commit->object.sha1))
 -              do_diff_cache(parent->tree->object.sha1, &diff_opts);
 +      if (is_null_oid(&target->commit->object.oid))
 +              do_diff_cache(parent->tree->object.oid.hash, &diff_opts);
        else
 -              diff_tree_sha1(parent->tree->object.sha1,
 -                             target->commit->tree->object.sha1,
 +              diff_tree_sha1(parent->tree->object.oid.hash,
 +                             target->commit->tree->object.oid.hash,
                               "", &diff_opts);
  
        if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
@@@ -1377,15 -1370,8 +1377,15 @@@ static void pass_whole_blame(struct sco
   */
  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);
  }
  
@@@ -1695,7 -1681,7 +1695,7 @@@ static void get_commit_info(struct comm
        if (len)
                strbuf_add(&ret->summary, subject, len);
        else
 -              strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1));
 +              strbuf_addf(&ret->summary, "(%s)", oid_to_hex(&commit->object.oid));
  
        unuse_commit_buffer(commit, message);
  }
@@@ -1738,7 -1724,7 +1738,7 @@@ static int emit_one_suspect_detail(stru
                printf("boundary\n");
        if (suspect->previous) {
                struct origin *prev = suspect->previous;
 -              printf("previous %s ", sha1_to_hex(prev->commit->object.sha1));
 +              printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
                write_name_quoted(prev->path, stdout, '\n');
        }
  
   * The blame_entry is found to be guilty for the range.
   * Show it in incremental output.
   */
 -static void found_guilty_entry(struct blame_entry *ent)
 +static void found_guilty_entry(struct blame_entry *ent,
 +                         struct progress_info *pi)
  {
        if (incremental) {
                struct origin *suspect = ent->suspect;
  
                printf("%s %d %d %d\n",
 -                     sha1_to_hex(suspect->commit->object.sha1),
 +                     oid_to_hex(&suspect->commit->object.oid),
                       ent->s_lno + 1, ent->lno + 1, ent->num_lines);
                emit_one_suspect_detail(suspect, 0);
                write_filename_info(suspect->path);
                maybe_flush_or_die(stdout, "stdout");
        }
 +      pi->blamed_lines += ent->num_lines;
 +      display_progress(pi->progress, pi->blamed_lines);
  }
  
  /*
@@@ -1776,11 -1759,6 +1776,11 @@@ static void assign_blame(struct scorebo
  {
        struct rev_info *revs = sb->revs;
        struct commit *commit = prio_queue_get(&sb->commits);
 +      struct progress_info pi = { NULL, 0 };
 +
 +      if (show_progress)
 +              pi.progress = start_progress_delay(_("Blaming lines"),
 +                                                 sb->num_lines, 50, 1);
  
        while (commit) {
                struct blame_entry *ent;
                        suspect->guilty = 1;
                        for (;;) {
                                struct blame_entry *next = ent->next;
 -                              found_guilty_entry(ent);
 +                              found_guilty_entry(ent, &pi);
                                if (next) {
                                        ent = next;
                                        continue;
                if (DEBUG) /* sanity */
                        sanity_check_refcnt(sb);
        }
 +
 +      stop_progress(&pi.progress);
  }
  
  static const char *format_time(unsigned long time, const char *tz_str,
                size_t time_width;
                int tz;
                tz = atoi(tz_str);
 -              time_str = show_date(time, tz, blame_date_mode);
 +              time_str = show_date(time, tz, &blame_date_mode);
                strbuf_addstr(&time_buf, time_str);
                /*
                 * Add space paddings to time_buf to display a fixed width
@@@ -1895,9 -1871,9 +1895,9 @@@ static void emit_porcelain(struct score
        int cnt;
        const char *cp;
        struct origin *suspect = ent->suspect;
 -      char hex[41];
 +      char hex[GIT_SHA1_HEXSZ + 1];
  
 -      strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
 +      sha1_to_hex_r(hex, suspect->commit->object.oid.hash);
        printf("%s %d %d %d\n",
               hex,
               ent->s_lno + 1,
@@@ -1933,11 -1909,11 +1933,11 @@@ static void emit_other(struct scoreboar
        const char *cp;
        struct origin *suspect = ent->suspect;
        struct commit_info ci;
 -      char hex[41];
 +      char hex[GIT_SHA1_HEXSZ + 1];
        int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
  
        get_commit_info(suspect->commit, &ci, 1);
 -      strcpy(hex, sha1_to_hex(suspect->commit->object.sha1));
 +      sha1_to_hex_r(hex, suspect->commit->object.oid.hash);
  
        cp = nth_line(sb, ent->lno);
        for (cnt = 0; cnt < ent->num_lines; cnt++) {
@@@ -2057,8 -2033,7 +2057,8 @@@ static int prepare_lines(struct scorebo
        for (p = buf; p < end; p = get_next_line(p, end))
                num++;
  
 -      sb->lineno = lineno = xmalloc(sizeof(*sb->lineno) * (num + 1));
 +      ALLOC_ARRAY(sb->lineno, num + 1);
 +      lineno = sb->lineno;
  
        for (p = buf; p < end; p = get_next_line(p, end))
                *lineno++ = p - buf;
@@@ -2093,7 -2068,7 +2093,7 @@@ static int read_ancestry(const char *gr
  
  static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)
  {
 -      const char *uniq = find_unique_abbrev(suspect->commit->object.sha1,
 +      const char *uniq = find_unique_abbrev(suspect->commit->object.oid.hash,
                                              auto_abbrev);
        int len = strlen(uniq);
        if (auto_abbrev < len)
@@@ -2169,7 -2144,7 +2169,7 @@@ static void sanity_check_refcnt(struct 
                if (ent->suspect->refcnt <= 0) {
                        fprintf(stderr, "%s in %s has negative refcnt %d\n",
                                ent->suspect->path,
 -                              sha1_to_hex(ent->suspect->commit->object.sha1),
 +                              oid_to_hex(&ent->suspect->commit->object.oid),
                                ent->suspect->refcnt);
                        baa = 1;
                }
@@@ -2206,18 -2181,10 +2206,18 @@@ static int git_blame_config(const char 
                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);
 -              blame_date_mode = parse_date_format(value);
 +              parse_date_format(value, &blame_date_mode);
                return 0;
        }
  
@@@ -2232,7 -2199,7 +2232,7 @@@ static void verify_working_tree_path(st
        struct commit_list *parents;
  
        for (parents = work_tree->parents; parents; parents = parents->next) {
 -              const unsigned char *commit_sha1 = parents->item->object.sha1;
 +              const unsigned char *commit_sha1 = parents->item->object.oid.hash;
                unsigned char blob_sha1[20];
                unsigned mode;
  
@@@ -2256,19 -2223,20 +2256,19 @@@ static struct commit_list **append_pare
  static void append_merge_parents(struct commit_list **tail)
  {
        int merge_head;
 -      const char *merge_head_file = git_path("MERGE_HEAD");
        struct strbuf line = STRBUF_INIT;
  
 -      merge_head = open(merge_head_file, O_RDONLY);
 +      merge_head = open(git_path_merge_head(), O_RDONLY);
        if (merge_head < 0) {
                if (errno == ENOENT)
                        return;
 -              die("cannot open '%s' for reading", merge_head_file);
 +              die("cannot open '%s' for reading", git_path_merge_head());
        }
  
        while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
                unsigned char sha1[20];
                if (line.len < 40 || get_sha1_hex(line.buf, sha1))
 -                      die("unknown line in '%s': %s", merge_head_file, line.buf);
 +                      die("unknown line in '%s': %s", git_path_merge_head(), line.buf);
                tail = append_parent(tail, sha1);
        }
        close(merge_head);
@@@ -2307,6 -2275,7 +2307,7 @@@ static struct commit *fake_working_tree
        unsigned mode;
        struct strbuf msg = STRBUF_INIT;
  
+       read_cache();
        time(&now);
        commit = alloc_commit_node();
        commit->object.parsed = 1;
        strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
        for (parent = commit->parents; parent; parent = parent->next)
                strbuf_addf(&msg, "parent %s\n",
 -                          sha1_to_hex(parent->item->object.sha1));
 +                          oid_to_hex(&parent->item->object.oid));
        strbuf_addf(&msg,
                    "author %s\n"
                    "committer %s\n\n"
        ce->ce_mode = create_ce_mode(mode);
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
  
 -      /*
 -       * We are not going to write this out, so this does not matter
 -       * right now, but someday we might optimize diff-index --cached
 -       * with cache-tree information.
 -       */
        cache_tree_invalidate_path(&the_index, path);
  
        return commit;
  }
  
 -static char *prepare_final(struct scoreboard *sb)
 +static struct commit *find_single_final(struct rev_info *revs,
 +                                      const char **name_p)
  {
        int i;
 -      const char *final_commit_name = NULL;
 -      struct rev_info *revs = sb->revs;
 +      struct commit *found = NULL;
 +      const char *name = 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)
                        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;
 +                          revs->pending.objects[i].name, name);
 +              found = (struct commit *)obj;
 +              name = revs->pending.objects[i].name;
        }
 -      return xstrdup_or_null(final_commit_name);
 +      if (name_p)
 +              *name_p = name;
 +      return found;
 +}
 +
 +static char *prepare_final(struct scoreboard *sb)
 +{
 +      const char *name;
 +      sb->final = find_single_final(sb->revs, &name);
 +      return xstrdup_or_null(name);
  }
  
  static char *prepare_initial(struct scoreboard *sb)
@@@ -2519,7 -2488,6 +2520,7 @@@ int cmd_blame(int argc, const char **ar
        long dashdash_pos, lno;
        char *final_commit_name = NULL;
        enum object_type type;
 +      struct commit *final_commit = NULL;
  
        static struct string_list range_list;
        static int output_option = 0, opt = 0;
                OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
                OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
                OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost statistics")),
 +              OPT_BOOL(0, "progress", &show_progress, N_("Force progress reporting")),
                OPT_BIT(0, "score-debug", &output_option, N_("Show output score for blame entries"), OUTPUT_SHOW_SCORE),
                OPT_BIT('f', "show-name", &output_option, N_("Show original filename (Default: auto)"), OUTPUT_SHOW_NAME),
                OPT_BIT('n', "show-number", &output_option, N_("Show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER),
        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);
  
        save_commit_buffer = 0;
        dashdash_pos = 0;
 +      show_progress = -1;
  
        parse_options_start(&ctx, argc, argv, prefix, options,
                            PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
@@@ -2592,13 -2558,6 +2593,13 @@@ parse_done
        DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES);
        argc = parse_options_end(&ctx);
  
 +      if (incremental || (output_option & OUTPUT_PORCELAIN)) {
 +              if (show_progress > 0)
 +                      die("--progress can't be used with --incremental or porcelain formats");
 +              show_progress = 0;
 +      } else if (show_progress < 0)
 +              show_progress = isatty(2);
 +
        if (0 < abbrev)
                /* one more abbrev length is needed for the boundary commit */
                abbrev++;
  
        if (cmd_is_annotate) {
                output_option |= OUTPUT_ANNOTATE_COMPAT;
 -              blame_date_mode = DATE_ISO8601;
 +              blame_date_mode.type = DATE_ISO8601;
        } else {
                blame_date_mode = revs.date_mode;
        }
  
        /* The maximum width used to show the dates */
 -      switch (blame_date_mode) {
 +      switch (blame_date_mode.type) {
        case DATE_RFC2822:
                blame_date_width = sizeof("Thu, 19 Oct 2006 16:00:04 -0700");
                break;
                   fewer display columns. */
                blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
                break;
 -      case DATE_LOCAL:
        case DATE_NORMAL:
                blame_date_width = sizeof("Thu Oct 19 16:00:04 2006 -0700");
                break;
 +      case DATE_STRFTIME:
 +              blame_date_width = strlen(show_date(0, 0, &blame_date_mode)) + 1; /* add the null */
 +              break;
        }
        blame_date_width -= 1; /* strip the null */
  
                sb.commits.compare = compare_commits_by_commit_date;
        }
        else if (contents_from)
 -              die("--contents and --children do not blend well.");
 +              die("--contents and --reverse do not blend well.");
        else {
                final_commit_name = prepare_initial(&sb);
                sb.commits.compare = compare_commits_by_reverse_commit_date;
 +              if (revs.first_parent_only)
 +                      revs.children.name = NULL;
        }
  
        if (!sb.final) {
        else if (contents_from)
                die("Cannot use --contents with final commit object name");
  
 +      if (reverse && revs.first_parent_only) {
 +              final_commit = find_single_final(sb.revs, NULL);
 +              if (!final_commit)
 +                      die("--reverse and --first-parent together require specified latest commit");
 +      }
 +
        /*
         * If we have bottom, this will mark the ancestors of the
         * bottom commits we would reach while traversing as
        if (prepare_revision_walk(&revs))
                die(_("revision walk setup failed"));
  
 -      if (is_null_sha1(sb.final->object.sha1)) {
 +      if (reverse && revs.first_parent_only) {
 +              struct commit *c = final_commit;
 +
 +              sb.revs->children.name = "children";
 +              while (c->parents &&
 +                     oidcmp(&c->object.oid, &sb.final->object.oid)) {
 +                      struct commit_list *l = xcalloc(1, sizeof(*l));
 +
 +                      l->item = c;
 +                      if (add_decoration(&sb.revs->children,
 +                                         &c->parents->item->object, l))
 +                              die("BUG: not unique item in first-parent chain");
 +                      c = c->parents->item;
 +              }
 +
 +              if (oidcmp(&c->object.oid, &sb.final->object.oid))
 +                      die("--reverse --first-parent together require range along first-parent chain");
 +      }
 +
 +      if (is_null_oid(&sb.final->object.oid)) {
                o = sb.final->util;
                sb.final_buf = xmemdupz(o->file.ptr, o->file.size);
                sb.final_buf_size = o->file.size;
  
        read_mailmap(&mailmap, NULL);
  
 +      assign_blame(&sb, opt);
 +
        if (!incremental)
                setup_pager();
  
 -      assign_blame(&sb, opt);
 -
        free(final_commit_name);
  
        if (incremental)
index 6568429753d5f90426ad6acc0703b79e9f7a3284,b8128b633480cce75dfe6fb48ef24034e69f595f..a9b266f0d3d029229a346fb21c703acb84e4c37d
@@@ -153,7 -153,7 +153,7 @@@ test_expect_success 'blame path that us
  '
  
  test_expect_success 'blame to a commit with no author name' '
 -  TREE=`git rev-parse HEAD:` &&
 +  TREE=$(git rev-parse HEAD:) &&
    cat >badcommit <<EOF &&
  tree $TREE
  author <noname> 1234567890 +0000
@@@ -161,7 -161,7 +161,7 @@@ committer David Reiss <dreiss@facebook.
  
  some message
  EOF
 -  COMMIT=`git hash-object -t commit -w badcommit` &&
 +  COMMIT=$(git hash-object -t commit -w badcommit) &&
    git --no-pager blame $COMMIT -- uno >/dev/null
  '
  
@@@ -212,4 -212,18 +212,18 @@@ test_expect_success 'blame file with CR
        grep "A U Thor" actual
  '
  
+ test_expect_success 'blame file with CRLF core.autocrlf=true' '
+       git config core.autocrlf false &&
+       printf "testcase\r\n" >crlfinrepo &&
+       >.gitattributes &&
+       git add crlfinrepo &&
+       git commit -m "add crlfinrepo" &&
+       git config core.autocrlf true &&
+       mv crlfinrepo tmp &&
+       git checkout crlfinrepo &&
+       rm tmp &&
+       git blame crlfinrepo >actual &&
+       grep "A U Thor" actual
+ '
  test_done