Merge branch 'nd/magic-pathspec'
authorJunio C Hamano <gitster@pobox.com>
Wed, 30 Oct 2013 19:10:29 +0000 (12:10 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 30 Oct 2013 19:10:33 +0000 (12:10 -0700)
All callers to parse_pathspec() must choose between getting no
pathspec or one path that is limited to the current directory
when there is no paths given on the command line, but there were
two callers that violated this rule, triggering a BUG().

* nd/magic-pathspec:
Fix calling parse_pathspec with no paths nor PATHSPEC_PREFER_* flags

1  2 
line-log.c
revision.c
diff --combined line-log.c
index 8b6e497b3ff187ee328cba395f30d3db7f86c0b4,b29f981cc778f138bf5f1760391345b1ce221ee3..717638b333b680da7a3a5acefcb73db9d956a00f
@@@ -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,19 -107,16 +107,19 @@@ 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 = 1; /* output cursor */
 +      int o = 0; /* output cursor */
  
        qsort(rs->ranges, rs->nr, sizeof(struct range), range_cmp);
  
 -      for (i = 1; i < rs->nr; i++) {
 -              if (rs->ranges[i].start <= rs->ranges[o-1].end) {
 -                      rs->ranges[o-1].end = rs->ranges[i].end;
 +      for (i = 0; i < rs->nr; i++) {
 +              if (rs->ranges[i].start == rs->ranges[i].end)
 +                      continue;
 +              if (o > 0 && rs->ranges[i].start <= rs->ranges[o-1].end) {
 +                      if (rs->ranges[o-1].end < rs->ranges[i].end)
 +                              rs->ranges[o-1].end = rs->ranges[i].end;
                } else {
                        rs->ranges[o].start = rs->ranges[i].start;
                        rs->ranges[o].end = rs->ranges[i].end;
@@@ -291,6 -288,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;
        }
@@@ -564,14 -562,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,7 -747,8 +760,8 @@@ void line_log_init(struct rev_info *rev
                        r = r->next;
                }
                paths[count] = NULL;
-               parse_pathspec(&rev->diffopt.pathspec, 0, 0, "", paths);
+               parse_pathspec(&rev->diffopt.pathspec, 0,
+                              PATHSPEC_PREFER_FULL, "", paths);
                free(paths);
        }
  }
diff --combined revision.c
index 7e03e154f446d2c105150cecea8c50fdc8e5238a,e04fdd0cb1d462e1eae6552e6e14a22abbc2b801..3fdea51ffed4839c6c30f34b45d67f5e95ab6cc2
@@@ -15,7 -15,6 +15,7 @@@
  #include "string-list.h"
  #include "line-log.h"
  #include "mailmap.h"
 +#include "commit-slab.h"
  
  volatile show_early_output_fn_t show_early_output;
  
@@@ -139,7 -138,8 +139,7 @@@ void mark_tree_uninteresting(struct tre
         * We don't care about the tree any more
         * after it has been marked uninteresting.
         */
 -      free(tree->buffer);
 -      tree->buffer = NULL;
 +      free_tree_buffer(tree);
  }
  
  void mark_parents_uninteresting(struct commit *commit)
@@@ -200,7 -200,7 +200,7 @@@ static void add_pending_object_with_mod
                revs->no_walk = 0;
        if (revs->reflog_info && obj->type == OBJ_COMMIT) {
                struct strbuf buf = STRBUF_INIT;
 -              int len = interpret_branch_name(name, &buf);
 +              int len = interpret_branch_name(name, 0, &buf);
                int st;
  
                if (0 < len && name[len] && buf.len)
@@@ -1358,7 -1358,7 +1358,7 @@@ static void prepare_show_merge(struct r
        if (!active_nr)
                read_cache();
        for (i = 0; i < active_nr; i++) {
 -              struct cache_entry *ce = active_cache[i];
 +              const struct cache_entry *ce = active_cache[i];
                if (!ce_stage(ce))
                        continue;
                if (ce_path_match(ce, &revs->prune_data)) {
                        i++;
        }
        free_pathspec(&revs->prune_data);
-       parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC, 0, "", prune);
+       parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC,
+                      PATHSPEC_PREFER_FULL, "", prune);
        revs->limited = 1;
  }
  
@@@ -1419,40 -1420,26 +1420,40 @@@ int handle_revision_arg(const char *arg
                }
                if (!get_sha1_committish(this, from_sha1) &&
                    !get_sha1_committish(next, sha1)) {
 -                      struct commit *a, *b;
 -                      struct commit_list *exclude;
 -
 -                      a = lookup_commit_reference(from_sha1);
 -                      b = lookup_commit_reference(sha1);
 -                      if (!a || !b) {
 -                              if (revs->ignore_missing)
 -                                      return 0;
 -                              die(symmetric ?
 -                                  "Invalid symmetric difference expression %s...%s" :
 -                                  "Invalid revision range %s..%s",
 -                                  arg, next);
 -                      }
 +                      struct object *a_obj, *b_obj;
  
                        if (!cant_be_filename) {
                                *dotdot = '.';
                                verify_non_filename(revs->prefix, arg);
                        }
  
 -                      if (symmetric) {
 +                      a_obj = parse_object(from_sha1);
 +                      b_obj = parse_object(sha1);
 +                      if (!a_obj || !b_obj) {
 +                      missing:
 +                              if (revs->ignore_missing)
 +                                      return 0;
 +                              die(symmetric
 +                                  ? "Invalid symmetric difference expression %s"
 +                                  : "Invalid revision range %s", arg);
 +                      }
 +
 +                      if (!symmetric) {
 +                              /* just A..B */
 +                              a_flags = flags_exclude;
 +                      } else {
 +                              /* A...B -- find merge bases between the two */
 +                              struct commit *a, *b;
 +                              struct commit_list *exclude;
 +
 +                              a = (a_obj->type == OBJ_COMMIT
 +                                   ? (struct commit *)a_obj
 +                                   : lookup_commit_reference(a_obj->sha1));
 +                              b = (b_obj->type == OBJ_COMMIT
 +                                   ? (struct commit *)b_obj
 +                                   : lookup_commit_reference(b_obj->sha1));
 +                              if (!a || !b)
 +                                      goto missing;
                                exclude = get_merge_bases(a, b, 1);
                                add_rev_cmdline_list(revs, exclude,
                                                     REV_CMD_MERGE_BASE,
                                add_pending_commit_list(revs, exclude,
                                                        flags_exclude);
                                free_commit_list(exclude);
 +
                                a_flags = flags | SYMMETRIC_LEFT;
 -                      } else
 -                              a_flags = flags_exclude;
 -                      a->object.flags |= a_flags;
 -                      b->object.flags |= flags;
 -                      add_rev_cmdline(revs, &a->object, this,
 +                      }
 +
 +                      a_obj->flags |= a_flags;
 +                      b_obj->flags |= flags;
 +                      add_rev_cmdline(revs, a_obj, this,
                                        REV_CMD_LEFT, a_flags);
 -                      add_rev_cmdline(revs, &b->object, next,
 +                      add_rev_cmdline(revs, b_obj, next,
                                        REV_CMD_RIGHT, flags);
 -                      add_pending_object(revs, &a->object, this);
 -                      add_pending_object(revs, &b->object, next);
 +                      add_pending_object(revs, a_obj, this);
 +                      add_pending_object(revs, b_obj, next);
                        return 0;
                }
                *dotdot = '.';
@@@ -2779,7 -2765,7 +2780,7 @@@ static int commit_match(struct commit *
        return retval;
  }
  
 -static inline int want_ancestry(struct rev_info *revs)
 +static inline int want_ancestry(const struct rev_info *revs)
  {
        return (revs->rewrite_parents || revs->children.name);
  }
@@@ -2836,14 -2822,6 +2837,14 @@@ enum commit_action simplify_commit(stru
        if (action == commit_show &&
            !revs->show_all &&
            revs->prune && revs->dense && want_ancestry(revs)) {
 +              /*
 +               * --full-diff on simplified parents is no good: it
 +               * will show spurious changes from the commits that
 +               * were elided.  So we save the parents on the side
 +               * when --full-diff is in effect.
 +               */
 +              if (revs->full_diff)
 +                      save_parents(revs, commit);
                if (rewrite_parents(revs, commit, rewrite_one) < 0)
                        return commit_error;
        }
@@@ -2863,7 -2841,6 +2864,7 @@@ static struct commit *get_revision_1(st
                free(entry);
  
                if (revs->reflog_info) {
 +                      save_parents(revs, commit);
                        fake_reflog_parent(revs->reflog_info, commit);
                        commit->object.flags &= ~(ADDED | SEEN | SHOWN);
                }
@@@ -3063,8 -3040,6 +3064,8 @@@ struct commit *get_revision(struct rev_
        c = get_revision_internal(revs);
        if (c && revs->graph)
                graph_update(revs->graph, c);
 +      if (!c)
 +              free_saved_parents(revs);
        return c;
  }
  
@@@ -3096,54 -3071,3 +3097,54 @@@ void put_revision_mark(const struct rev
        fputs(mark, stdout);
        putchar(' ');
  }
 +
 +define_commit_slab(saved_parents, struct commit_list *);
 +
 +#define EMPTY_PARENT_LIST ((struct commit_list *)-1)
 +
 +void save_parents(struct rev_info *revs, struct commit *commit)
 +{
 +      struct commit_list **pp;
 +
 +      if (!revs->saved_parents_slab) {
 +              revs->saved_parents_slab = xmalloc(sizeof(struct saved_parents));
 +              init_saved_parents(revs->saved_parents_slab);
 +      }
 +
 +      pp = saved_parents_at(revs->saved_parents_slab, commit);
 +
 +      /*
 +       * When walking with reflogs, we may visit the same commit
 +       * several times: once for each appearance in the reflog.
 +       *
 +       * In this case, save_parents() will be called multiple times.
 +       * We want to keep only the first set of parents.  We need to
 +       * store a sentinel value for an empty (i.e., NULL) parent
 +       * list to distinguish it from a not-yet-saved list, however.
 +       */
 +      if (*pp)
 +              return;
 +      if (commit->parents)
 +              *pp = copy_commit_list(commit->parents);
 +      else
 +              *pp = EMPTY_PARENT_LIST;
 +}
 +
 +struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit)
 +{
 +      struct commit_list *parents;
 +
 +      if (!revs->saved_parents_slab)
 +              return commit->parents;
 +
 +      parents = *saved_parents_at(revs->saved_parents_slab, commit);
 +      if (parents == EMPTY_PARENT_LIST)
 +              return NULL;
 +      return parents;
 +}
 +
 +void free_saved_parents(struct rev_info *revs)
 +{
 +      if (revs->saved_parents_slab)
 +              clear_saved_parents(revs->saved_parents_slab);
 +}