Merge branch 'tm/line-log-first-parent'
authorJunio C Hamano <gitster@pobox.com>
Thu, 6 Nov 2014 18:52:36 +0000 (10:52 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 6 Nov 2014 18:52:37 +0000 (10:52 -0800)
"git log --first-parent -L..." used to crash.

* tm/line-log-first-parent:
line-log: fix crash when --first-parent is used

1  2 
line-log.c
t/t4211-line-log.sh
diff --combined line-log.c
index 038c58a395c6804e5f719d4d82a3d4edbd6d8901,fe04c9d109e682145e6b761f7f2de27779cf76a2..b7864ad5869eb076e778c352e2f64666dc77a525
@@@ -23,7 -23,7 +23,7 @@@ static void range_set_grow(struct range
  /* Either initialization would be fine */
  #define RANGE_SET_INIT {0}
  
 -static void range_set_init(struct range_set *rs, size_t prealloc)
 +void range_set_init(struct range_set *rs, size_t prealloc)
  {
        rs->alloc = rs->nr = 0;
        rs->ranges = NULL;
@@@ -31,7 -31,7 +31,7 @@@
                range_set_grow(rs, prealloc);
  }
  
 -static void range_set_release(struct range_set *rs)
 +void range_set_release(struct range_set *rs)
  {
        free(rs->ranges);
        rs->alloc = rs->nr = 0;
@@@ -56,7 -56,7 +56,7 @@@ static void range_set_move(struct range
  }
  
  /* tack on a _new_ range _at the end_ */
 -static void range_set_append_unsafe(struct range_set *rs, long a, long b)
 +void range_set_append_unsafe(struct range_set *rs, long a, long b)
  {
        assert(a <= b);
        range_set_grow(rs, 1);
@@@ -65,7 -65,7 +65,7 @@@
        rs->nr++;
  }
  
 -static void range_set_append(struct range_set *rs, long a, long b)
 +void range_set_append(struct range_set *rs, long a, long b)
  {
        assert(rs->nr == 0 || rs->ranges[rs->nr-1].end <= a);
        range_set_append_unsafe(rs, a, b);
@@@ -107,7 -107,7 +107,7 @@@ static void range_set_check_invariants(
   * In-place pass of sorting and merging the ranges in the range set,
   * to establish the invariants when we get the ranges from the user
   */
 -static void sort_and_merge_range_set(struct range_set *rs)
 +void sort_and_merge_range_set(struct range_set *rs)
  {
        int i;
        int o = 0; /* output cursor */
@@@ -291,6 -291,7 +291,6 @@@ static void line_log_data_insert(struc
  
        if (p) {
                range_set_append_unsafe(&p->ranges, begin, end);
 -              sort_and_merge_range_set(&p->ranges);
                free(path);
                return;
        }
        p = xcalloc(1, sizeof(struct line_log_data));
        p->path = path;
        range_set_append(&p->ranges, begin, end);
 -      sort_and_merge_range_set(&p->ranges);
        if (ip) {
                p->next = ip->next;
                ip->next = p;
@@@ -533,7 -535,7 +533,7 @@@ static void fill_line_ends(struct diff_
        }
  
        /* shrink the array to fit the elements */
 -      ends = xrealloc(ends, cur * sizeof(*ends));
 +      REALLOC_ARRAY(ends, cur);
        *lines = cur-1;
        *line_ends = ends;
  }
@@@ -564,14 -566,12 +564,14 @@@ parse_lines(struct commit *commit, cons
        struct nth_line_cb cb_data;
        struct string_list_item *item;
        struct line_log_data *ranges = NULL;
 +      struct line_log_data *p;
  
        for_each_string_list_item(item, args) {
                const char *name_part, *range_part;
                char *full_name;
                struct diff_filespec *spec;
                long begin = 0, end = 0;
 +              long anchor;
  
                name_part = skip_range_arg(item->string);
                if (!name_part || *name_part != ':' || !name_part[1])
                cb_data.lines = lines;
                cb_data.line_ends = ends;
  
 +              p = search_line_log_data(ranges, full_name, NULL);
 +              if (p && p->ranges.nr)
 +                      anchor = p->ranges.ranges[p->ranges.nr - 1].end + 1;
 +              else
 +                      anchor = 1;
 +
                if (parse_range_arg(range_part, nth_line, &cb_data,
 -                                  lines, &begin, &end,
 +                                  lines, anchor, &begin, &end,
                                    full_name))
                        die("malformed -L argument '%s'", range_part);
 +              if (lines < end || ((lines || begin) && lines < begin))
 +                      die("file %s has only %lu lines", name_part, lines);
                if (begin < 1)
                        begin = 1;
                if (end < 1)
                        end = lines;
                begin--;
 -              if (lines < end || lines < begin)
 -                      die("file %s has only %ld lines", name_part, lines);
                line_log_data_insert(&ranges, full_name, begin, end);
  
                free_filespec(spec);
                ends = NULL;
        }
  
 +      for (p = ranges; p; p = p->next)
 +              sort_and_merge_range_set(&p->ranges);
 +
        return ranges;
  }
  
@@@ -760,12 -751,32 +760,12 @@@ void line_log_init(struct rev_info *rev
                        r = r->next;
                }
                paths[count] = NULL;
 -              init_pathspec(&rev->diffopt.pathspec, paths);
 +              parse_pathspec(&rev->diffopt.pathspec, 0,
 +                             PATHSPEC_PREFER_FULL, "", paths);
                free(paths);
        }
  }
  
 -static void load_tree_desc(struct tree_desc *desc, void **tree,
 -                         const unsigned char *sha1)
 -{
 -      unsigned long size;
 -      *tree = read_object_with_reference(sha1, tree_type, &size, NULL);
 -      if (!*tree)
 -              die("Unable to read tree (%s)", sha1_to_hex(sha1));
 -      init_tree_desc(desc, *tree, size);
 -}
 -
 -static int count_parents(struct commit *commit)
 -{
 -      struct commit_list *parents = commit->parents;
 -      int count = 0;
 -      while (parents) {
 -              count++;
 -              parents = parents->next;
 -      }
 -      return count;
 -}
 -
  static void move_diff_queue(struct diff_queue_struct *dst,
                            struct diff_queue_struct *src)
  {
@@@ -821,11 -832,18 +821,11 @@@ static void queue_diffs(struct line_log
                        struct diff_queue_struct *queue,
                        struct commit *commit, struct commit *parent)
  {
 -      void *tree1 = NULL, *tree2 = NULL;
 -      struct tree_desc desc1, desc2;
 -
        assert(commit);
 -      load_tree_desc(&desc2, &tree2, commit->tree->object.sha1);
 -      if (parent)
 -              load_tree_desc(&desc1, &tree1, parent->tree->object.sha1);
 -      else
 -              init_tree_desc(&desc1, "", 0);
  
        DIFF_QUEUE_CLEAR(&diff_queued_diff);
 -      diff_tree(&desc1, &desc2, "", opt);
 +      diff_tree_sha1(parent ? parent->tree->object.sha1 : NULL,
 +                      commit->tree->object.sha1, "", opt);
        if (opt->detect_rename) {
                filter_diffs_for_paths(range, 1);
                if (diff_might_be_rename())
                filter_diffs_for_paths(range, 0);
        }
        move_diff_queue(queue, &diff_queued_diff);
 -
 -      if (tree1)
 -              free(tree1);
 -      if (tree2)
 -              free(tree2);
  }
  
  static char *get_nth_line(long line, unsigned long *ends, void *data)
@@@ -1139,8 -1162,11 +1139,11 @@@ static int process_ranges_merge_commit(
        struct commit **parents;
        struct commit_list *p;
        int i;
 -      int nparents = count_parents(commit);
 +      int nparents = commit_list_count(commit->parents);
  
+       if (nparents > 1 && rev->first_parent_only)
+               nparents = 1;
        diffqueues = xmalloc(nparents * sizeof(*diffqueues));
        cand = xmalloc(nparents * sizeof(*cand));
        parents = xmalloc(nparents * sizeof(*parents));
                         */
                        add_line_range(rev, parents[i], cand[i]);
                        clear_commit_line_range(rev, commit);
 -                      commit->parents = xmalloc(sizeof(struct commit_list));
 -                      commit->parents->item = parents[i];
 -                      commit->parents->next = NULL;
 +                      commit_list_append(parents[i], &commit->parents);
                        free(parents);
                        free(cand);
                        free_diffqueues(nparents, diffqueues);
diff --combined t/t4211-line-log.sh
index 7369d3c517294feda894eba85f7b02ac9a57ccae,3be25a3a7f0cb39cad496417c5e718bcfd33cfb4..0901b3098239863df983faa61d2b00ce2c4d670d
@@@ -48,7 -48,7 +48,7 @@@ canned_test "-M -L '/long f/,/^}/:b.c' 
  canned_test "-M -L ':f:b.c' parallel-change" parallel-change-f-to-main
  
  canned_test "-L 4,12:a.c -L :main:a.c simple" multiple
 -canned_test "-L 4,18:a.c -L :main:a.c simple" multiple-overlapping
 +canned_test "-L 4,18:a.c -L ^:main:a.c simple" multiple-overlapping
  canned_test "-L :main:a.c -L 4,18:a.c simple" multiple-overlapping
  canned_test "-L 4:a.c -L 8,12:a.c simple" multiple-superset
  canned_test "-L 8,12:a.c -L 4:a.c simple" multiple-superset
@@@ -64,34 -64,22 +64,39 @@@ test_bad_opts "-L 1,1000:b.c" "has only
  test_bad_opts "-L :b.c" "argument.*not of the form"
  test_bad_opts "-L :foo:b.c" "no match"
  
 -# There is a separate bug when an empty -L range is the first -L encountered,
 -# thus to demonstrate this particular bug, the empty -L range must follow a
 -# non-empty -L range.
 -test_expect_success '-L {empty-range} (any -L)' '
 +test_expect_success '-L X (X == nlines)' '
 +      n=$(wc -l <b.c) &&
 +      git log -L $n:b.c
 +'
 +
 +test_expect_success '-L X (X == nlines + 1)' '
        n=$(expr $(wc -l <b.c) + 1) &&
 -      git log -L1,1:b.c -L$n:b.c
 +      test_must_fail git log -L $n:b.c
 +'
 +
 +test_expect_success '-L X (X == nlines + 2)' '
 +      n=$(expr $(wc -l <b.c) + 2) &&
 +      test_must_fail git log -L $n:b.c
  '
  
 -test_expect_success '-L {empty-range} (first -L)' '
 +test_expect_success '-L ,Y (Y == nlines)' '
 +      n=$(printf "%d" $(wc -l <b.c)) &&
 +      git log -L ,$n:b.c
 +'
 +
 +test_expect_success '-L ,Y (Y == nlines + 1)' '
        n=$(expr $(wc -l <b.c) + 1) &&
 -      git log -L$n:b.c
 +      test_must_fail git log -L ,$n:b.c
 +'
 +
 +test_expect_success '-L ,Y (Y == nlines + 2)' '
 +      n=$(expr $(wc -l <b.c) + 2) &&
 +      test_must_fail git log -L ,$n:b.c
  '
  
+ test_expect_success '-L with --first-parent and a merge' '
+       git checkout parallel-change &&
+       git log --first-parent -L 1,1:b.c
+ '
  test_done