Merge branch 'jc/traverse-commit-list'
authorJunio C Hamano <gitster@pobox.com>
Wed, 5 Oct 2011 19:36:19 +0000 (12:36 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 5 Oct 2011 19:36:19 +0000 (12:36 -0700)
* jc/traverse-commit-list:
revision.c: update show_object_with_name() without using malloc()
revision.c: add show_object_with_name() helper function
rev-list: fix finish_object() call

1  2 
revision.c
revision.h
upload-pack.c
diff --combined revision.c
index 102456b009429eeecdee3a5f8f9a1a0404f1cffa,072ddac4703058ff245b01488e36b190f8590f82..19a493bf609bf3ecbe9602de0f2ce54af2ae828a
@@@ -40,6 -40,47 +40,47 @@@ char *path_name(const struct name_path 
        return n;
  }
  
+ static int show_path_component_truncated(FILE *out, const char *name, int len)
+ {
+       int cnt;
+       for (cnt = 0; cnt < len; cnt++) {
+               int ch = name[cnt];
+               if (!ch || ch == '\n')
+                       return -1;
+               fputc(ch, out);
+       }
+       return len;
+ }
+ static int show_path_truncated(FILE *out, const struct name_path *path)
+ {
+       int emitted, ours;
+       if (!path)
+               return 0;
+       emitted = show_path_truncated(out, path->up);
+       if (emitted < 0)
+               return emitted;
+       if (emitted)
+               fputc('/', out);
+       ours = show_path_component_truncated(out, path->elem, path->elem_len);
+       if (ours < 0)
+               return ours;
+       return ours || emitted;
+ }
+ void show_object_with_name(FILE *out, struct object *obj, const struct name_path *path, const char *component)
+ {
+       struct name_path leaf;
+       leaf.up = (struct name_path *)path;
+       leaf.elem = component;
+       leaf.elem_len = strlen(component);
+       fprintf(out, "%s ", sha1_to_hex(obj->sha1));
+       show_path_truncated(out, &leaf);
+       fputc('\n', out);
+ }
  void add_object(struct object *obj,
                struct object_array *p,
                struct name_path *path,
@@@ -729,16 -770,12 +770,16 @@@ static void limit_to_ancestry(struct co
   * to filter the result of "A..B" further to the ones that can actually
   * reach A.
   */
 -static struct commit_list *collect_bottom_commits(struct commit_list *list)
 +static struct commit_list *collect_bottom_commits(struct rev_info *revs)
  {
 -      struct commit_list *elem, *bottom = NULL;
 -      for (elem = list; elem; elem = elem->next)
 -              if (elem->item->object.flags & UNINTERESTING)
 -                      commit_list_insert(elem->item, &bottom);
 +      struct commit_list *bottom = NULL;
 +      int i;
 +      for (i = 0; i < revs->cmdline.nr; i++) {
 +              struct rev_cmdline_entry *elem = &revs->cmdline.rev[i];
 +              if ((elem->flags & UNINTERESTING) &&
 +                  elem->item->type == OBJ_COMMIT)
 +                      commit_list_insert((struct commit *)elem->item, &bottom);
 +      }
        return bottom;
  }
  
@@@ -769,7 -806,7 +810,7 @@@ static int limit_list(struct rev_info *
        struct commit_list *bottom = NULL;
  
        if (revs->ancestry_path) {
 -              bottom = collect_bottom_commits(list);
 +              bottom = collect_bottom_commits(revs);
                if (!bottom)
                        die("--ancestry-path given but there are no bottom commits");
        }
        return 0;
  }
  
 +static void add_rev_cmdline(struct rev_info *revs,
 +                          struct object *item,
 +                          const char *name,
 +                          int whence,
 +                          unsigned flags)
 +{
 +      struct rev_cmdline_info *info = &revs->cmdline;
 +      int nr = info->nr;
 +
 +      ALLOC_GROW(info->rev, nr + 1, info->alloc);
 +      info->rev[nr].item = item;
 +      info->rev[nr].name = name;
 +      info->rev[nr].whence = whence;
 +      info->rev[nr].flags = flags;
 +      info->nr++;
 +}
 +
  struct all_refs_cb {
        int all_flags;
        int warned_bad_reflog;
@@@ -855,7 -875,6 +896,7 @@@ static int handle_one_ref(const char *p
        struct all_refs_cb *cb = cb_data;
        struct object *object = get_reference(cb->all_revs, path, sha1,
                                              cb->all_flags);
 +      add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
        add_pending_object(cb->all_revs, object, path);
        return 0;
  }
@@@ -882,7 -901,6 +923,7 @@@ static void handle_one_reflog_commit(un
                struct object *o = parse_object(sha1);
                if (o) {
                        o->flags |= cb->all_flags;
 +                      /* ??? CMDLINEFLAGS ??? */
                        add_pending_object(cb->all_revs, o, "");
                }
                else if (!cb->warned_bad_reflog) {
@@@ -919,13 -937,12 +960,13 @@@ static void handle_reflog(struct rev_in
        for_each_reflog(handle_one_reflog, &cb);
  }
  
 -static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
 +static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
  {
        unsigned char sha1[20];
        struct object *it;
        struct commit *commit;
        struct commit_list *parents;
 +      const char *arg = arg_;
  
        if (*arg == '^') {
                flags ^= UNINTERESTING;
        for (parents = commit->parents; parents; parents = parents->next) {
                it = &parents->item->object;
                it->flags |= flags;
 +              add_rev_cmdline(revs, it, arg_, REV_CMD_PARENTS_ONLY, flags);
                add_pending_object(revs, it, arg);
        }
        return 1;
@@@ -1043,7 -1059,7 +1084,7 @@@ static void prepare_show_merge(struct r
        revs->limited = 1;
  }
  
 -int handle_revision_arg(const char *arg, struct rev_info *revs,
 +int handle_revision_arg(const char *arg_, struct rev_info *revs,
                        int flags,
                        int cant_be_filename)
  {
        struct object *object;
        unsigned char sha1[20];
        int local_flags;
 +      const char *arg = arg_;
  
        dotdot = strstr(arg, "..");
        if (dotdot) {
                const char *this = arg;
                int symmetric = *next == '.';
                unsigned int flags_exclude = flags ^ UNINTERESTING;
 +              unsigned int a_flags;
  
                *dotdot = 0;
                next += symmetric;
                                add_pending_commit_list(revs, exclude,
                                                        flags_exclude);
                                free_commit_list(exclude);
 -                              a->object.flags |= flags | SYMMETRIC_LEFT;
 +                              a_flags = flags | SYMMETRIC_LEFT;
                        } else
 -                              a->object.flags |= flags_exclude;
 +                              a_flags = flags_exclude;
 +                      a->object.flags |= a_flags;
                        b->object.flags |= flags;
 +                      add_rev_cmdline(revs, &a->object, this,
 +                                      REV_CMD_LEFT, a_flags);
 +                      add_rev_cmdline(revs, &b->object, next,
 +                                      REV_CMD_RIGHT, flags);
                        add_pending_object(revs, &a->object, this);
                        add_pending_object(revs, &b->object, next);
                        return 0;
        if (!cant_be_filename)
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, sha1, flags ^ local_flags);
 +      add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
        add_pending_object_with_mode(revs, object, arg, mode);
        return 0;
  }
diff --combined revision.h
index 93d31556cb44d3e4c5162d21c8e92d572a36508e,da00a58fa5ff600e4c47f698d51db5bb7d4d5e5b..8eeb542dfbbfa70eb17cce7d0e2075f43d51c161
@@@ -24,23 -24,6 +24,23 @@@ struct rev_info
  struct log_info;
  struct string_list;
  
 +struct rev_cmdline_info {
 +      unsigned int nr;
 +      unsigned int alloc;
 +      struct rev_cmdline_entry {
 +              struct object *item;
 +              const char *name;
 +              enum {
 +                      REV_CMD_REF,
 +                      REV_CMD_PARENTS_ONLY,
 +                      REV_CMD_LEFT,
 +                      REV_CMD_RIGHT,
 +                      REV_CMD_REV
 +              } whence;
 +              unsigned flags;
 +      } *rev;
 +};
 +
  struct rev_info {
        /* Starting list */
        struct commit_list *commits;
@@@ -49,9 -32,6 +49,9 @@@
        /* Parents of shown commits */
        struct object_array boundary_commits;
  
 +      /* The end-points specified by the end user */
 +      struct rev_cmdline_info cmdline;
 +
        /* Basic information */
        const char *prefix;
        const char *def;
@@@ -205,6 -185,8 +205,8 @@@ struct name_path 
  
  char *path_name(const struct name_path *path, const char *name);
  
+ extern void show_object_with_name(FILE *, struct object *, const struct name_path *, const char *);
  extern void add_object(struct object *obj,
                       struct object_array *p,
                       struct name_path *path,
diff --combined upload-pack.c
index 8739bfacdfd4e01e16a614dd419388fa93a904b6,970a1eba137828511456c5dc67e6d6001ef609a4..31686712c77ba8229870a224fafcc8008c3af4ff
@@@ -10,7 -10,6 +10,7 @@@
  #include "revision.h"
  #include "list-objects.h"
  #include "run-command.h"
 +#include "sigchain.h"
  
  static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<n>] <dir>";
  
@@@ -86,20 -85,7 +86,7 @@@ static void show_commit(struct commit *
  
  static void show_object(struct object *obj, const struct name_path *path, const char *component)
  {
-       /* An object with name "foo\n0000000..." can be used to
-        * confuse downstream git-pack-objects very badly.
-        */
-       const char *name = path_name(path, component);
-       const char *ep = strchr(name, '\n');
-       if (ep) {
-               fprintf(pack_pipe, "%s %.*s\n", sha1_to_hex(obj->sha1),
-                      (int) (ep - name),
-                      name);
-       }
-       else
-               fprintf(pack_pipe, "%s %s\n",
-                               sha1_to_hex(obj->sha1), name);
-       free((char *)name);
+       show_object_with_name(pack_pipe, obj, path, component);
  }
  
  static void show_edge(struct commit *commit)
@@@ -499,97 -485,11 +486,97 @@@ static int get_common_commits(void
        }
  }
  
 +static void check_non_tip(void)
 +{
 +      static const char *argv[] = {
 +              "rev-list", "--stdin", NULL,
 +      };
 +      static struct child_process cmd;
 +      struct object *o;
 +      char namebuf[42]; /* ^ + SHA-1 + LF */
 +      int i;
 +
 +      /* In the normal in-process case non-tip request can never happen */
 +      if (!stateless_rpc)
 +              goto error;
 +
 +      cmd.argv = argv;
 +      cmd.git_cmd = 1;
 +      cmd.no_stderr = 1;
 +      cmd.in = -1;
 +      cmd.out = -1;
 +
 +      if (start_command(&cmd))
 +              goto error;
 +
 +      /*
 +       * If rev-list --stdin encounters an unknown commit, it
 +       * terminates, which will cause SIGPIPE in the write loop
 +       * below.
 +       */
 +      sigchain_push(SIGPIPE, SIG_IGN);
 +
 +      namebuf[0] = '^';
 +      namebuf[41] = '\n';
 +      for (i = get_max_object_index(); 0 < i; ) {
 +              o = get_indexed_object(--i);
 +              if (!o)
 +                      continue;
 +              if (!(o->flags & OUR_REF))
 +                      continue;
 +              memcpy(namebuf + 1, sha1_to_hex(o->sha1), 40);
 +              if (write_in_full(cmd.in, namebuf, 42) < 0)
 +                      goto error;
 +      }
 +      namebuf[40] = '\n';
 +      for (i = 0; i < want_obj.nr; i++) {
 +              o = want_obj.objects[i].item;
 +              if (o->flags & OUR_REF)
 +                      continue;
 +              memcpy(namebuf, sha1_to_hex(o->sha1), 40);
 +              if (write_in_full(cmd.in, namebuf, 41) < 0)
 +                      goto error;
 +      }
 +      close(cmd.in);
 +
 +      sigchain_pop(SIGPIPE);
 +
 +      /*
 +       * The commits out of the rev-list are not ancestors of
 +       * our ref.
 +       */
 +      i = read_in_full(cmd.out, namebuf, 1);
 +      if (i)
 +              goto error;
 +      close(cmd.out);
 +
 +      /*
 +       * rev-list may have died by encountering a bad commit
 +       * in the history, in which case we do want to bail out
 +       * even when it showed no commit.
 +       */
 +      if (finish_command(&cmd))
 +              goto error;
 +
 +      /* All the non-tip ones are ancestors of what we advertised */
 +      return;
 +
 +error:
 +      /* Pick one of them (we know there at least is one) */
 +      for (i = 0; i < want_obj.nr; i++) {
 +              o = want_obj.objects[i].item;
 +              if (!(o->flags & OUR_REF))
 +                      die("git upload-pack: not our ref %s",
 +                          sha1_to_hex(o->sha1));
 +      }
 +}
 +
  static void receive_needs(void)
  {
        struct object_array shallows = OBJECT_ARRAY_INIT;
        static char line[1000];
        int len, depth = 0;
 +      int has_non_tip = 0;
  
        shallow_nr = 0;
        if (debug_fd)
                if (strstr(line+45, "include-tag"))
                        use_include_tag = 1;
  
 -              /* We have sent all our refs already, and the other end
 -               * should have chosen out of them; otherwise they are
 -               * asking for nonsense.
 -               *
 -               * Hmph.  We may later want to allow "want" line that
 -               * asks for something like "master~10" (symbolic)...
 -               * would it make sense?  I don't know.
 -               */
                o = lookup_object(sha1_buf);
 -              if (!o || !(o->flags & OUR_REF))
 +              if (!o)
                        die("git upload-pack: not our ref %s",
                            sha1_to_hex(sha1_buf));
                if (!(o->flags & WANTED)) {
                        o->flags |= WANTED;
 +                      if (!(o->flags & OUR_REF))
 +                              has_non_tip = 1;
                        add_object_array(o, NULL, &want_obj);
                }
        }
        if (debug_fd)
                write_str_in_full(debug_fd, "#E\n");
  
 +      /*
 +       * We have sent all our refs already, and the other end
 +       * should have chosen out of them. When we are operating
 +       * in the stateless RPC mode, however, their choice may
 +       * have been based on the set of older refs advertised
 +       * by another process that handled the initial request.
 +       */
 +      if (has_non_tip)
 +              check_non_tip();
 +
        if (!use_sideband && daemon_mode)
                no_progress = 1;
  
@@@ -732,17 -628,16 +719,17 @@@ static int send_ref(const char *refname
                " side-band-64k ofs-delta shallow no-progress"
                " include-tag multi_ack_detailed";
        struct object *o = parse_object(sha1);
 +      const char *refname_nons = strip_namespace(refname);
  
        if (!o)
                die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
  
        if (capabilities)
 -              packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname,
 +              packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname_nons,
                             0, capabilities,
                             stateless_rpc ? " no-done" : "");
        else
 -              packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname);
 +              packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
        capabilities = NULL;
        if (!(o->flags & OUR_REF)) {
                o->flags |= OUR_REF;
        if (o->type == OBJ_TAG) {
                o = deref_tag(o, refname, 0);
                if (o)
 -                      packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
 +                      packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname_nons);
        }
        return 0;
  }
@@@ -772,12 -667,12 +759,12 @@@ static void upload_pack(void
  {
        if (advertise_refs || !stateless_rpc) {
                reset_timeout();
 -              head_ref(send_ref, NULL);
 -              for_each_ref(send_ref, NULL);
 +              head_ref_namespaced(send_ref, NULL);
 +              for_each_namespaced_ref(send_ref, NULL);
                packet_flush(1);
        } else {
 -              head_ref(mark_our_ref, NULL);
 -              for_each_ref(mark_our_ref, NULL);
 +              head_ref_namespaced(mark_our_ref, NULL);
 +              for_each_namespaced_ref(mark_our_ref, NULL);
        }
        if (advertise_refs)
                return;