Merge branch 'cb/log-follow-with-combined' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 3 Jul 2013 22:30:59 +0000 (15:30 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 3 Jul 2013 22:30:59 +0000 (15:30 -0700)
"git log -c --follow $path" segfaulted upon hitting the commit that
renamed the $path being followed.

* cb/log-follow-with-combined:
fix segfault with git log -c --follow

1  2 
combine-diff.c
t/t4202-log.sh
diff --combined combine-diff.c
index 3e8bb17831aa5f2f39ee3a07eeebdda90ae39556,21fd00ff6156f7eb7af6b74dd569325af3f37820..6dc06093d3c1eb6f3ab7a9cc79f0f192e16dc617
@@@ -5,7 -5,6 +5,7 @@@
  #include "diffcore.h"
  #include "quote.h"
  #include "xdiff-interface.h"
 +#include "xdiff/xmacros.h"
  #include "log-tree.h"
  #include "refs.h"
  #include "userdiff.h"
@@@ -74,24 -73,16 +74,24 @@@ static struct combine_diff_path *inters
  
  /* Lines lost from parent */
  struct lline {
 -      struct lline *next;
 +      struct lline *next, *prev;
        int len;
        unsigned long parent_map;
        char line[FLEX_ARRAY];
  };
  
 +/* Lines lost from current parent (before coalescing) */
 +struct plost {
 +      struct lline *lost_head, *lost_tail;
 +      int len;
 +};
 +
  /* Lines surviving in the merge result */
  struct sline {
 -      struct lline *lost_head, **lost_tail;
 -      struct lline *next_lost;
 +      /* Accumulated and coalesced lost lines */
 +      struct lline *lost;
 +      int lenlost;
 +      struct plost plost;
        char *bol;
        int len;
        /* bit 0 up to (N-1) are on if the parent has this line (i.e.
        unsigned long *p_lno;
  };
  
 +static int match_string_spaces(const char *line1, int len1,
 +                             const char *line2, int len2,
 +                             long flags)
 +{
 +      if (flags & XDF_WHITESPACE_FLAGS) {
 +              for (; len1 > 0 && XDL_ISSPACE(line1[len1 - 1]); len1--);
 +              for (; len2 > 0 && XDL_ISSPACE(line2[len2 - 1]); len2--);
 +      }
 +
 +      if (!(flags & (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE)))
 +              return (len1 == len2 && !memcmp(line1, line2, len1));
 +
 +      while (len1 > 0 && len2 > 0) {
 +              len1--;
 +              len2--;
 +              if (XDL_ISSPACE(line1[len1]) || XDL_ISSPACE(line2[len2])) {
 +                      if ((flags & XDF_IGNORE_WHITESPACE_CHANGE) &&
 +                          (!XDL_ISSPACE(line1[len1]) || !XDL_ISSPACE(line2[len2])))
 +                              return 0;
 +
 +                      for (; len1 > 0 && XDL_ISSPACE(line1[len1]); len1--);
 +                      for (; len2 > 0 && XDL_ISSPACE(line2[len2]); len2--);
 +              }
 +              if (line1[len1] != line2[len2])
 +                      return 0;
 +      }
 +
 +      if (flags & XDF_IGNORE_WHITESPACE) {
 +              /* Consume remaining spaces */
 +              for (; len1 > 0 && XDL_ISSPACE(line1[len1 - 1]); len1--);
 +              for (; len2 > 0 && XDL_ISSPACE(line2[len2 - 1]); len2--);
 +      }
 +
 +      /* We matched full line1 and line2 */
 +      if (!len1 && !len2)
 +              return 1;
 +
 +      return 0;
 +}
 +
 +enum coalesce_direction { MATCH, BASE, NEW };
 +
 +/* Coalesce new lines into base by finding LCS */
 +static struct lline *coalesce_lines(struct lline *base, int *lenbase,
 +                                  struct lline *new, int lennew,
 +                                  unsigned long parent, long flags)
 +{
 +      int **lcs;
 +      enum coalesce_direction **directions;
 +      struct lline *baseend, *newend = NULL;
 +      int i, j, origbaselen = *lenbase;
 +
 +      if (new == NULL)
 +              return base;
 +
 +      if (base == NULL) {
 +              *lenbase = lennew;
 +              return new;
 +      }
 +
 +      /*
 +       * Coalesce new lines into base by finding the LCS
 +       * - Create the table to run dynamic programing
 +       * - Compute the LCS
 +       * - Then reverse read the direction structure:
 +       *   - If we have MATCH, assign parent to base flag, and consume
 +       *   both baseend and newend
 +       *   - Else if we have BASE, consume baseend
 +       *   - Else if we have NEW, insert newend lline into base and
 +       *   consume newend
 +       */
 +      lcs = xcalloc(origbaselen + 1, sizeof(int*));
 +      directions = xcalloc(origbaselen + 1, sizeof(enum coalesce_direction*));
 +      for (i = 0; i < origbaselen + 1; i++) {
 +              lcs[i] = xcalloc(lennew + 1, sizeof(int));
 +              directions[i] = xcalloc(lennew + 1, sizeof(enum coalesce_direction));
 +              directions[i][0] = BASE;
 +      }
 +      for (j = 1; j < lennew + 1; j++)
 +              directions[0][j] = NEW;
 +
 +      for (i = 1, baseend = base; i < origbaselen + 1; i++) {
 +              for (j = 1, newend = new; j < lennew + 1; j++) {
 +                      if (match_string_spaces(baseend->line, baseend->len,
 +                                              newend->line, newend->len, flags)) {
 +                              lcs[i][j] = lcs[i - 1][j - 1] + 1;
 +                              directions[i][j] = MATCH;
 +                      } else if (lcs[i][j - 1] >= lcs[i - 1][j]) {
 +                              lcs[i][j] = lcs[i][j - 1];
 +                              directions[i][j] = NEW;
 +                      } else {
 +                              lcs[i][j] = lcs[i - 1][j];
 +                              directions[i][j] = BASE;
 +                      }
 +                      if (newend->next)
 +                              newend = newend->next;
 +              }
 +              if (baseend->next)
 +                      baseend = baseend->next;
 +      }
 +
 +      for (i = 0; i < origbaselen + 1; i++)
 +              free(lcs[i]);
 +      free(lcs);
 +
 +      /* At this point, baseend and newend point to the end of each lists */
 +      i--;
 +      j--;
 +      while (i != 0 || j != 0) {
 +              if (directions[i][j] == MATCH) {
 +                      baseend->parent_map |= 1<<parent;
 +                      baseend = baseend->prev;
 +                      newend = newend->prev;
 +                      i--;
 +                      j--;
 +              } else if (directions[i][j] == NEW) {
 +                      struct lline *lline;
 +
 +                      lline = newend;
 +                      /* Remove lline from new list and update newend */
 +                      if (lline->prev)
 +                              lline->prev->next = lline->next;
 +                      else
 +                              new = lline->next;
 +                      if (lline->next)
 +                              lline->next->prev = lline->prev;
 +
 +                      newend = lline->prev;
 +                      j--;
 +
 +                      /* Add lline to base list */
 +                      if (baseend) {
 +                              lline->next = baseend->next;
 +                              lline->prev = baseend;
 +                              if (lline->prev)
 +                                      lline->prev->next = lline;
 +                      }
 +                      else {
 +                              lline->next = base;
 +                              base = lline;
 +                      }
 +                      (*lenbase)++;
 +
 +                      if (lline->next)
 +                              lline->next->prev = lline;
 +
 +              } else {
 +                      baseend = baseend->prev;
 +                      i--;
 +              }
 +      }
 +
 +      newend = new;
 +      while (newend) {
 +              struct lline *lline = newend;
 +              newend = newend->next;
 +              free(lline);
 +      }
 +
 +      for (i = 0; i < origbaselen + 1; i++)
 +              free(directions[i]);
 +      free(directions);
 +
 +      return base;
 +}
 +
  static char *grab_blob(const unsigned char *sha1, unsigned int mode,
                       unsigned long *size, struct userdiff_driver *textconv,
                       const char *path)
@@@ -304,19 -129,29 +304,19 @@@ static void append_lost(struct sline *s
        if (line[len-1] == '\n')
                len--;
  
 -      /* Check to see if we can squash things */
 -      if (sline->lost_head) {
 -              lline = sline->next_lost;
 -              while (lline) {
 -                      if (lline->len == len &&
 -                          !memcmp(lline->line, line, len)) {
 -                              lline->parent_map |= this_mask;
 -                              sline->next_lost = lline->next;
 -                              return;
 -                      }
 -                      lline = lline->next;
 -              }
 -      }
 -
        lline = xmalloc(sizeof(*lline) + len + 1);
        lline->len = len;
        lline->next = NULL;
 +      lline->prev = sline->plost.lost_tail;
 +      if (lline->prev)
 +              lline->prev->next = lline;
 +      else
 +              sline->plost.lost_head = lline;
 +      sline->plost.lost_tail = lline;
 +      sline->plost.len++;
        lline->parent_map = this_mask;
        memcpy(lline->line, line, len);
        lline->line[len] = 0;
 -      *sline->lost_tail = lline;
 -      sline->lost_tail = &lline->next;
 -      sline->next_lost = NULL;
  }
  
  struct combine_diff_state {
@@@ -359,6 -194,7 +359,6 @@@ static void consume_line(void *state_, 
                                xcalloc(state->num_parent,
                                        sizeof(unsigned long));
                state->sline[state->nb-1].p_lno[state->n] = state->ob;
 -              state->lost_bucket->next_lost = state->lost_bucket->lost_head;
                return;
        }
        if (!state->lost_bucket)
@@@ -379,7 -215,7 +379,7 @@@ static void combine_diff(const unsigne
                         struct sline *sline, unsigned int cnt, int n,
                         int num_parent, int result_deleted,
                         struct userdiff_driver *textconv,
 -                       const char *path)
 +                       const char *path, long flags)
  {
        unsigned int p_lno, lno;
        unsigned long nmask = (1UL << n);
        parent_file.ptr = grab_blob(parent, mode, &sz, textconv, path);
        parent_file.size = sz;
        memset(&xpp, 0, sizeof(xpp));
 -      xpp.flags = 0;
 +      xpp.flags = flags;
        memset(&xecfg, 0, sizeof(xecfg));
        memset(&state, 0, sizeof(state));
        state.nmask = nmask;
                struct lline *ll;
                sline[lno].p_lno[n] = p_lno;
  
 +              /* Coalesce new lines */
 +              if (sline[lno].plost.lost_head) {
 +                      struct sline *sl = &sline[lno];
 +                      sl->lost = coalesce_lines(sl->lost, &sl->lenlost,
 +                                                sl->plost.lost_head,
 +                                                sl->plost.len, n, flags);
 +                      sl->plost.lost_head = sl->plost.lost_tail = NULL;
 +                      sl->plost.len = 0;
 +              }
 +
                /* How many lines would this sline advance the p_lno? */
 -              ll = sline[lno].lost_head;
 +              ll = sline[lno].lost;
                while (ll) {
                        if (ll->parent_map & nmask)
                                p_lno++; /* '-' means parent had it */
@@@ -450,7 -276,7 +450,7 @@@ static int interesting(struct sline *sl
        /* If some parents lost lines here, or if we have added to
         * some parent, it is interesting.
         */
 -      return ((sline->flag & all_mask) || sline->lost_head);
 +      return ((sline->flag & all_mask) || sline->lost);
  }
  
  static unsigned long adjust_hunk_tail(struct sline *sline,
@@@ -518,11 -344,8 +518,11 @@@ static int give_context(struct sline *s
                unsigned long k;
  
                /* Paint a few lines before the first interesting line. */
 -              while (j < i)
 -                      sline[j++].flag |= mark | no_pre_delete;
 +              while (j < i) {
 +                      if (!(sline[j].flag & mark))
 +                              sline[j].flag |= no_pre_delete;
 +                      sline[j++].flag |= mark;
 +              }
  
        again:
                /* we know up to i is to be included.  where does the
@@@ -636,7 -459,7 +636,7 @@@ static int make_hunks(struct sline *sli
                has_interesting = 0;
                for (j = i; j < hunk_end && !has_interesting; j++) {
                        unsigned long this_diff = sline[j].flag & all_mask;
 -                      struct lline *ll = sline[j].lost_head;
 +                      struct lline *ll = sline[j].lost;
                        if (this_diff) {
                                /* This has some changes.  Is it the
                                 * same as others?
@@@ -703,8 -526,7 +703,8 @@@ static void show_line_to_eol(const cha
               saw_cr_at_eol ? "\r" : "");
  }
  
 -static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
 +static void dump_sline(struct sline *sline, const char *line_prefix,
 +                     unsigned long cnt, int num_parent,
                       int use_color, int result_deleted)
  {
        unsigned long mark = (1UL<<num_parent);
                        rlines -= null_context;
                }
  
 -              fputs(c_frag, stdout);
 +              printf("%s%s", line_prefix, c_frag);
                for (i = 0; i <= num_parent; i++) putchar(combine_marker);
                for (i = 0; i < num_parent; i++)
                        show_parent_lno(sline, lno, hunk_end, i, null_context);
                        int j;
                        unsigned long p_mask;
                        struct sline *sl = &sline[lno++];
 -                      ll = (sl->flag & no_pre_delete) ? NULL : sl->lost_head;
 +                      ll = (sl->flag & no_pre_delete) ? NULL : sl->lost;
                        while (ll) {
 -                              fputs(c_old, stdout);
 +                              printf("%s%s", line_prefix, c_old);
                                for (j = 0; j < num_parent; j++) {
                                        if (ll->parent_map & (1UL<<j))
                                                putchar('-');
                        if (cnt < lno)
                                break;
                        p_mask = 1;
 +                      fputs(line_prefix, stdout);
                        if (!(sl->flag & (mark-1))) {
                                /*
                                 * This sline was here to hang the
@@@ -841,7 -662,7 +841,7 @@@ static void reuse_combine_diff(struct s
        jmask = (1UL<<j);
  
        for (lno = 0; lno <= cnt; lno++) {
 -              struct lline *ll = sline->lost_head;
 +              struct lline *ll = sline->lost;
                sline->p_lno[i] = sline->p_lno[j];
                while (ll) {
                        if (ll->parent_map & jmask)
  static void dump_quoted_path(const char *head,
                             const char *prefix,
                             const char *path,
 +                           const char *line_prefix,
                             const char *c_meta, const char *c_reset)
  {
        static struct strbuf buf = STRBUF_INIT;
  
        strbuf_reset(&buf);
 +      strbuf_addstr(&buf, line_prefix);
        strbuf_addstr(&buf, c_meta);
        strbuf_addstr(&buf, head);
        quote_two_c_style(&buf, prefix, path, 0);
@@@ -877,7 -696,6 +877,7 @@@ static void show_combined_header(struc
                                 int num_parent,
                                 int dense,
                                 struct rev_info *rev,
 +                               const char *line_prefix,
                                 int mode_differs,
                                 int show_file_header)
  {
                show_log(rev);
  
        dump_quoted_path(dense ? "diff --cc " : "diff --combined ",
 -                       "", elem->path, c_meta, c_reset);
 -      printf("%sindex ", c_meta);
 +                       "", elem->path, line_prefix, c_meta, c_reset);
 +      printf("%s%sindex ", line_prefix, c_meta);
        for (i = 0; i < num_parent; i++) {
                abb = find_unique_abbrev(elem->parent[i].sha1,
                                         abbrev);
                            DIFF_STATUS_ADDED)
                                added = 0;
                if (added)
 -                      printf("%snew file mode %06o",
 -                             c_meta, elem->mode);
 +                      printf("%s%snew file mode %06o",
 +                             line_prefix, c_meta, elem->mode);
                else {
                        if (deleted)
 -                              printf("%sdeleted file ", c_meta);
 +                              printf("%s%sdeleted file ",
 +                                     line_prefix, c_meta);
                        printf("mode ");
                        for (i = 0; i < num_parent; i++) {
                                printf("%s%06o", i ? "," : "",
  
        if (added)
                dump_quoted_path("--- ", "", "/dev/null",
 -                               c_meta, c_reset);
 +                               line_prefix, c_meta, c_reset);
        else
                dump_quoted_path("--- ", a_prefix, elem->path,
 -                               c_meta, c_reset);
 +                               line_prefix, c_meta, c_reset);
        if (deleted)
                dump_quoted_path("+++ ", "", "/dev/null",
 -                               c_meta, c_reset);
 +                               line_prefix, c_meta, c_reset);
        else
                dump_quoted_path("+++ ", b_prefix, elem->path,
 -                               c_meta, c_reset);
 +                               line_prefix, c_meta, c_reset);
  }
  
  static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
        struct userdiff_driver *userdiff;
        struct userdiff_driver *textconv = NULL;
        int is_binary;
 +      const char *line_prefix = diff_line_prefix(opt);
  
        context = opt->context;
        userdiff = userdiff_find_by_path(elem->path);
        }
        if (is_binary) {
                show_combined_header(elem, num_parent, dense, rev,
 -                                   mode_differs, 0);
 +                                   line_prefix, mode_differs, 0);
                printf("Binary files differ\n");
                free(result);
                return;
  
        sline = xcalloc(cnt+2, sizeof(*sline));
        sline[0].bol = result;
 -      for (lno = 0; lno <= cnt + 1; lno++) {
 -              sline[lno].lost_tail = &sline[lno].lost_head;
 -              sline[lno].flag = 0;
 -      }
        for (lno = 0, cp = result; cp < result + result_size; cp++) {
                if (*cp == '\n') {
                        sline[lno].len = cp - sline[lno].bol;
                                     elem->parent[i].mode,
                                     &result_file, sline,
                                     cnt, i, num_parent, result_deleted,
 -                                   textconv, elem->path);
 +                                   textconv, elem->path, opt->xdl_opts);
        }
  
        show_hunks = make_hunks(sline, cnt, num_parent, dense);
  
        if (show_hunks || mode_differs || working_tree_file) {
                show_combined_header(elem, num_parent, dense, rev,
 -                                   mode_differs, 1);
 -              dump_sline(sline, cnt, num_parent,
 +                                   line_prefix, mode_differs, 1);
 +              dump_sline(sline, line_prefix, cnt, num_parent,
                           opt->use_color, result_deleted);
        }
        free(result);
  
        for (lno = 0; lno < cnt; lno++) {
 -              if (sline[lno].lost_head) {
 -                      struct lline *ll = sline[lno].lost_head;
 +              if (sline[lno].lost) {
 +                      struct lline *ll = sline[lno].lost;
                        while (ll) {
                                struct lline *tmp = ll;
                                ll = ll->next;
@@@ -1166,7 -986,6 +1166,7 @@@ static void show_raw_diff(struct combin
  {
        struct diff_options *opt = &rev->diffopt;
        int line_termination, inter_name_termination, i;
 +      const char *line_prefix = diff_line_prefix(opt);
  
        line_termination = opt->line_termination;
        inter_name_termination = '\t';
        if (rev->loginfo && !rev->no_commit_id)
                show_log(rev);
  
 +
        if (opt->output_format & DIFF_FORMAT_RAW) {
 +              printf("%s", line_prefix);
 +
                /* As many colons as there are parents */
                for (i = 0; i < num_parent; i++)
                        putchar(':');
@@@ -1217,7 -1033,6 +1217,7 @@@ void show_combined_diff(struct combine_
                       struct rev_info *rev)
  {
        struct diff_options *opt = &rev->diffopt;
 +
        if (!p->len)
                return;
        if (opt->output_format & (DIFF_FORMAT_RAW |
@@@ -1305,6 -1120,7 +1305,7 @@@ void diff_tree_combined(const unsigned 
        int i, num_paths, needsep, show_log_first, num_parent = parents->nr;
  
        diffopts = *opt;
+       diff_tree_setup_paths(diffopts.pathspec.raw, &diffopts);
        diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
        DIFF_OPT_SET(&diffopts, RECURSIVE);
        DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL);
  
                if (show_log_first && i == 0) {
                        show_log(rev);
 +
                        if (rev->verbose_header && opt->output_format)
 -                              putchar(opt->line_termination);
 +                              printf("%s%c", diff_line_prefix(opt),
 +                                     opt->line_termination);
                }
                diff_flush(&diffopts);
        }
  
                if (opt->output_format & DIFF_FORMAT_PATCH) {
                        if (needsep)
 -                              putchar(opt->line_termination);
 +                              printf("%s%c", diff_line_prefix(opt),
 +                                     opt->line_termination);
                        for (p = paths; p; p = p->next) {
                                if (p->len)
                                        show_patch_diff(p, num_parent, dense,
                paths = paths->next;
                free(tmp);
        }
+       diff_tree_release_paths(&diffopts);
  }
  
  void diff_tree_combined_merge(const struct commit *commit, int dense,
diff --combined t/t4202-log.sh
index 9243a979933997286bb6004bd7b224bad1bbc5cc,d9e587a96ed095cf29b393e380e1697d753a367a..cb03d287698f110b66e69636348caccdbe6a94f8
@@@ -419,6 -419,8 +419,6 @@@ test_expect_success 'log --graph with m
  '
  
  test_expect_success 'log.decorate configuration' '
 -      test_might_fail git config --unset-all log.decorate &&
 -
        git log --oneline >expect.none &&
        git log --oneline --decorate >expect.short &&
        git log --oneline --decorate=full >expect.full &&
        git log --oneline >actual &&
        test_cmp expect.short actual &&
  
 -      git config --unset-all log.decorate &&
 -      git config log.decorate true &&
 +      test_config log.decorate true &&
        git log --oneline >actual &&
        test_cmp expect.short actual &&
        git log --oneline --decorate=full >actual &&
        git log --oneline --decorate=no >actual &&
        test_cmp expect.none actual &&
  
 -      git config --unset-all log.decorate &&
 -      git config log.decorate no &&
 +      test_config log.decorate no &&
        git log --oneline >actual &&
        test_cmp expect.none actual &&
        git log --oneline --decorate >actual &&
        git log --oneline --decorate=full >actual &&
        test_cmp expect.full actual &&
  
 -      git config --unset-all log.decorate &&
 -      git config log.decorate 1 &&
 +      test_config log.decorate 1 &&
        git log --oneline >actual &&
        test_cmp expect.short actual &&
        git log --oneline --decorate=full >actual &&
        git log --oneline --decorate=no >actual &&
        test_cmp expect.none actual &&
  
 -      git config --unset-all log.decorate &&
 -      git config log.decorate short &&
 +      test_config log.decorate short &&
        git log --oneline >actual &&
        test_cmp expect.short actual &&
        git log --oneline --no-decorate >actual &&
        git log --oneline --decorate=full >actual &&
        test_cmp expect.full actual &&
  
 -      git config --unset-all log.decorate &&
 -      git config log.decorate full &&
 +      test_config log.decorate full &&
        git log --oneline >actual &&
        test_cmp expect.full actual &&
        git log --oneline --no-decorate >actual &&
        git log --oneline --decorate >actual &&
        test_cmp expect.short actual
  
 -      git config --unset-all log.decorate &&
 +      test_unconfig log.decorate &&
        git log --pretty=raw >expect.raw &&
 -      git config log.decorate full &&
 +      test_config log.decorate full &&
        git log --pretty=raw >actual &&
        test_cmp expect.raw actual
  
  '
  
  test_expect_success 'reflog is expected format' '
 -      test_might_fail git config --remove-section log &&
        git log -g --abbrev-commit --pretty=oneline >expect &&
        git reflog >actual &&
        test_cmp expect actual
@@@ -488,6 -496,10 +488,6 @@@ test_expect_success 'whatchanged is exp
  '
  
  test_expect_success 'log.abbrevCommit configuration' '
 -      test_when_finished "git config --unset log.abbrevCommit" &&
 -
 -      test_might_fail git config --unset log.abbrevCommit &&
 -
        git log --abbrev-commit >expect.log.abbrev &&
        git log --no-abbrev-commit >expect.log.full &&
        git log --pretty=raw >expect.log.raw &&
        git whatchanged --abbrev-commit >expect.whatchanged.abbrev &&
        git whatchanged --no-abbrev-commit >expect.whatchanged.full &&
  
 -      git config log.abbrevCommit true &&
 +      test_config log.abbrevCommit true &&
  
        git log >actual &&
        test_cmp expect.log.abbrev actual &&
@@@ -530,6 -542,20 +530,20 @@@ test_expect_success 'show added path un
        )
  '
  
+ test_expect_success 'git log -c --follow' '
+       test_create_repo follow-c &&
+       (
+               cd follow-c &&
+               test_commit initial file original &&
+               git rm file &&
+               test_commit rename file2 original &&
+               git reset --hard initial &&
+               test_commit modify file foo &&
+               git merge -m merge rename &&
+               git log -c --follow file2
+       )
+ '
  cat >expect <<\EOF
  *   commit COMMIT_OBJECT_NAME
  |\  Merge: MERGE_PARENTS