Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Wed, 16 Mar 2011 23:59:30 +0000 (16:59 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 16 Mar 2011 23:59:30 +0000 (16:59 -0700)
* maint:
Prepare draft release notes to 1.7.4.2
gitweb: highlight: replace tabs with spaces
make_absolute_path: return the input path if it points to our buffer
valgrind: ignore SSE-based strlen invalid reads
diff --submodule: split into bite-sized pieces
cherry: split off function to print output lines
branch: split off function that writes tracking info and commit subject
standardize brace placement in struct definitions
compat: make gcc bswap an inline function
enums: omit trailing comma for portability

Conflicts:
RelNotes

1  2 
builtin/add.c
builtin/grep.c
builtin/log.c
cache.h
config.c
fast-import.c
merge-recursive.c
transport-helper.c
diff --combined builtin/add.c
index f7a17e43f6816622ab4ff836a169221289756505,1d74763f58d893619501427bcee0cc04c2878a02..e127d5a68b5c28497a3e640760e4c597fbaa59d5
@@@ -21,8 -21,7 +21,7 @@@ static const char * const builtin_add_u
  static int patch_interactive, add_interactive, edit_interactive;
  static int take_worktree_changes;
  
- struct update_callback_data
- {
+ struct update_callback_data {
        int flags;
        int add_errors;
  };
@@@ -86,7 -85,7 +85,7 @@@ int add_files_to_cache(const char *pref
        struct rev_info rev;
        init_revisions(&rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
 -      rev.prune_data = pathspec;
 +      init_pathspec(&rev.prune_data, pathspec);
        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = update_callback;
        data.flags = flags;
@@@ -322,7 -321,7 +321,7 @@@ static struct option builtin_add_option
        OPT__FORCE(&ignored_too, "allow adding otherwise ignored files"),
        OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
        OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
 -      OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"),
 +      OPT_BOOLEAN('A', "all", &addremove, "add changes from all tracked and untracked files"),
        OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
        OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
        OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, "check if - even missing - files are ignored in dry run"),
diff --combined builtin/grep.c
index 5afee2f3a42c76c7e414a667e04c10dd87af1a6a,34f9ae03044f8f4c1bf36c41791020fe5b125b2c..eaf8560a520bd8aac4fa62394378b444116d6226
@@@ -40,8 -40,7 +40,7 @@@ enum work_type {WORK_SHA1, WORK_FILE}
   * threads. The producer adds struct work_items to 'todo' and the
   * consumers pick work items from the same array.
   */
- struct work_item
- {
+ struct work_item {
        enum work_type type;
        char *name;
  
@@@ -329,6 -328,106 +328,6 @@@ static int grep_config(const char *var
        return 0;
  }
  
 -/*
 - * Return non-zero if max_depth is negative or path has no more then max_depth
 - * slashes.
 - */
 -static int accept_subdir(const char *path, int max_depth)
 -{
 -      if (max_depth < 0)
 -              return 1;
 -
 -      while ((path = strchr(path, '/')) != NULL) {
 -              max_depth--;
 -              if (max_depth < 0)
 -                      return 0;
 -              path++;
 -      }
 -      return 1;
 -}
 -
 -/*
 - * Return non-zero if name is a subdirectory of match and is not too deep.
 - */
 -static int is_subdir(const char *name, int namelen,
 -              const char *match, int matchlen, int max_depth)
 -{
 -      if (matchlen > namelen || strncmp(name, match, matchlen))
 -              return 0;
 -
 -      if (name[matchlen] == '\0') /* exact match */
 -              return 1;
 -
 -      if (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/')
 -              return accept_subdir(name + matchlen + 1, max_depth);
 -
 -      return 0;
 -}
 -
 -/*
 - * git grep pathspecs are somewhat different from diff-tree pathspecs;
 - * pathname wildcards are allowed.
 - */
 -static int pathspec_matches(const char **paths, const char *name, int max_depth)
 -{
 -      int namelen, i;
 -      if (!paths || !*paths)
 -              return accept_subdir(name, max_depth);
 -      namelen = strlen(name);
 -      for (i = 0; paths[i]; i++) {
 -              const char *match = paths[i];
 -              int matchlen = strlen(match);
 -              const char *cp, *meta;
 -
 -              if (is_subdir(name, namelen, match, matchlen, max_depth))
 -                      return 1;
 -              if (!fnmatch(match, name, 0))
 -                      return 1;
 -              if (name[namelen-1] != '/')
 -                      continue;
 -
 -              /* We are being asked if the directory ("name") is worth
 -               * descending into.
 -               *
 -               * Find the longest leading directory name that does
 -               * not have metacharacter in the pathspec; the name
 -               * we are looking at must overlap with that directory.
 -               */
 -              for (cp = match, meta = NULL; cp - match < matchlen; cp++) {
 -                      char ch = *cp;
 -                      if (ch == '*' || ch == '[' || ch == '?') {
 -                              meta = cp;
 -                              break;
 -                      }
 -              }
 -              if (!meta)
 -                      meta = cp; /* fully literal */
 -
 -              if (namelen <= meta - match) {
 -                      /* Looking at "Documentation/" and
 -                       * the pattern says "Documentation/howto/", or
 -                       * "Documentation/diff*.txt".  The name we
 -                       * have should match prefix.
 -                       */
 -                      if (!memcmp(match, name, namelen))
 -                              return 1;
 -                      continue;
 -              }
 -
 -              if (meta - match < namelen) {
 -                      /* Looking at "Documentation/howto/" and
 -                       * the pattern says "Documentation/h*";
 -                       * match up to "Do.../h"; this avoids descending
 -                       * into "Documentation/technical/".
 -                       */
 -                      if (!memcmp(match, name, meta - match))
 -                              return 1;
 -                      continue;
 -              }
 -      }
 -      return 0;
 -}
 -
  static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
  {
        void *data;
@@@ -481,7 -580,7 +480,7 @@@ static void run_pager(struct grep_opt *
        free(argv);
  }
  
 -static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
 +static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached)
  {
        int hit = 0;
        int nr;
                struct cache_entry *ce = active_cache[nr];
                if (!S_ISREG(ce->ce_mode))
                        continue;
 -              if (!pathspec_matches(paths, ce->name, opt->max_depth))
 +              if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
                        continue;
                /*
                 * If CE_VALID is on, we assume worktree file and its cache entry
        return hit;
  }
  
 -static int grep_tree(struct grep_opt *opt, const char **paths,
 -                   struct tree_desc *tree,
 -                   const char *tree_name, const char *base)
 +static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
 +                   struct tree_desc *tree, struct strbuf *base, int tn_len)
  {
 -      int len;
 -      int hit = 0;
 +      int hit = 0, matched = 0;
        struct name_entry entry;
 -      char *down;
 -      int tn_len = strlen(tree_name);
 -      struct strbuf pathbuf;
 -
 -      strbuf_init(&pathbuf, PATH_MAX + tn_len);
 -
 -      if (tn_len) {
 -              strbuf_add(&pathbuf, tree_name, tn_len);
 -              strbuf_addch(&pathbuf, ':');
 -              tn_len = pathbuf.len;
 -      }
 -      strbuf_addstr(&pathbuf, base);
 -      len = pathbuf.len;
 +      int old_baselen = base->len;
  
        while (tree_entry(tree, &entry)) {
                int te_len = tree_entry_len(entry.path, entry.sha1);
 -              pathbuf.len = len;
 -              strbuf_add(&pathbuf, entry.path, te_len);
 -
 -              if (S_ISDIR(entry.mode))
 -                      /* Match "abc/" against pathspec to
 -                       * decide if we want to descend into "abc"
 -                       * directory.
 -                       */
 -                      strbuf_addch(&pathbuf, '/');
 -
 -              down = pathbuf.buf + tn_len;
 -              if (!pathspec_matches(paths, down, opt->max_depth))
 -                      ;
 -              else if (S_ISREG(entry.mode))
 -                      hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);
 +
 +              if (matched != 2) {
 +                      matched = tree_entry_interesting(&entry, base, tn_len, pathspec);
 +                      if (matched == -1)
 +                              break; /* no more matches */
 +                      if (!matched)
 +                              continue;
 +              }
 +
 +              strbuf_add(base, entry.path, te_len);
 +
 +              if (S_ISREG(entry.mode)) {
 +                      hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len);
 +              }
                else if (S_ISDIR(entry.mode)) {
                        enum object_type type;
                        struct tree_desc sub;
                        if (!data)
                                die("unable to read tree (%s)",
                                    sha1_to_hex(entry.sha1));
 +
 +                      strbuf_addch(base, '/');
                        init_tree_desc(&sub, data, size);
 -                      hit |= grep_tree(opt, paths, &sub, tree_name, down);
 +                      hit |= grep_tree(opt, pathspec, &sub, base, tn_len);
                        free(data);
                }
 +              strbuf_setlen(base, old_baselen);
 +
                if (hit && opt->status_only)
                        break;
        }
 -      strbuf_release(&pathbuf);
        return hit;
  }
  
 -static int grep_object(struct grep_opt *opt, const char **paths,
 +static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
                       struct object *obj, const char *name)
  {
        if (obj->type == OBJ_BLOB)
                struct tree_desc tree;
                void *data;
                unsigned long size;
 -              int hit;
 +              struct strbuf base;
 +              int hit, len;
 +
                data = read_object_with_reference(obj->sha1, tree_type,
                                                  &size, NULL);
                if (!data)
                        die("unable to read tree (%s)", sha1_to_hex(obj->sha1));
 +
 +              len = name ? strlen(name) : 0;
 +              strbuf_init(&base, PATH_MAX + len + 1);
 +              if (len) {
 +                      strbuf_add(&base, name, len);
 +                      strbuf_addch(&base, ':');
 +              }
                init_tree_desc(&tree, data, size);
 -              hit = grep_tree(opt, paths, &tree, name, "");
 +              hit = grep_tree(opt, pathspec, &tree, &base, base.len);
 +              strbuf_release(&base);
                free(data);
                return hit;
        }
        die("unable to grep from object of type %s", typename(obj->type));
  }
  
 -static int grep_objects(struct grep_opt *opt, const char **paths,
 +static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
                        const struct object_array *list)
  {
        unsigned int i;
        for (i = 0; i < nr; i++) {
                struct object *real_obj;
                real_obj = deref_tag(list->objects[i].item, NULL, 0);
 -              if (grep_object(opt, paths, real_obj, list->objects[i].name)) {
 +              if (grep_object(opt, pathspec, real_obj, list->objects[i].name)) {
                        hit = 1;
                        if (opt->status_only)
                                break;
        return hit;
  }
  
 -static int grep_directory(struct grep_opt *opt, const char **paths)
 +static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec)
  {
        struct dir_struct dir;
        int i, hit = 0;
        memset(&dir, 0, sizeof(dir));
        setup_standard_excludes(&dir);
  
 -      fill_directory(&dir, paths);
 +      fill_directory(&dir, pathspec->raw);
        for (i = 0; i < dir.nr; i++) {
 +              const char *name = dir.entries[i]->name;
 +              int namelen = strlen(name);
 +              if (!match_pathspec_depth(pathspec, name, namelen, 0, NULL))
 +                      continue;
                hit |= grep_file(opt, dir.entries[i]->name);
                if (hit && opt->status_only)
                        break;
@@@ -734,7 -831,6 +733,7 @@@ int cmd_grep(int argc, const char **arg
        struct grep_opt opt;
        struct object_array list = OBJECT_ARRAY_INIT;
        const char **paths = NULL;
 +      struct pathspec pathspec;
        struct string_list path_list = STRING_LIST_INIT_NODUP;
        int i;
        int dummy;
                paths[0] = prefix;
                paths[1] = NULL;
        }
 +      init_pathspec(&pathspec, paths);
 +      pathspec.max_depth = opt.max_depth;
 +      pathspec.recursive = 1;
  
        if (show_in_pager && (cached || list.nr))
                die("--open-files-in-pager only works on the worktree");
                        die("--cached cannot be used with --no-index.");
                if (list.nr)
                        die("--no-index cannot be used with revs.");
 -              hit = grep_directory(&opt, paths);
 +              hit = grep_directory(&opt, &pathspec);
        } else if (!list.nr) {
                if (!cached)
                        setup_work_tree();
  
 -              hit = grep_cache(&opt, paths, cached);
 +              hit = grep_cache(&opt, &pathspec, cached);
        } else {
                if (cached)
                        die("both --cached and trees are given.");
 -              hit = grep_objects(&opt, paths, &list);
 +              hit = grep_objects(&opt, &pathspec, &list);
        }
  
        if (use_threads)
diff --combined builtin/log.c
index f5ed690c435423662808fe0ff7f1e256374586db,0f43d2ec78555339cfb788bc173780acdaa2139d..99e33b3651a3c0d7e89144712d3754eab8ad30d6
@@@ -89,7 -89,7 +89,7 @@@ static void cmd_log_init(int argc, cons
                rev->always_show_header = 0;
        if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
                rev->always_show_header = 0;
 -              if (rev->diffopt.nr_paths != 1)
 +              if (rev->diffopt.pathspec.nr != 1)
                        usage("git logs can only follow renames on one pathname at a time");
        }
        for (i = 1; i < argc; i++) {
@@@ -1352,6 -1352,23 +1352,23 @@@ static const char * const cherry_usage[
        NULL
  };
  
+ static void print_commit(char sign, struct commit *commit, int verbose,
+                        int abbrev)
+ {
+       if (!verbose) {
+               printf("%c %s\n", sign,
+                      find_unique_abbrev(commit->object.sha1, abbrev));
+       } else {
+               struct strbuf buf = STRBUF_INIT;
+               struct pretty_print_context ctx = {0};
+               pretty_print_commit(CMIT_FMT_ONELINE, commit, &buf, &ctx);
+               printf("%c %s %s\n", sign,
+                      find_unique_abbrev(commit->object.sha1, abbrev),
+                      buf.buf);
+               strbuf_release(&buf);
+       }
+ }
  int cmd_cherry(int argc, const char **argv, const char *prefix)
  {
        struct rev_info revs;
                commit = list->item;
                if (has_commit_patch_id(commit, &ids))
                        sign = '-';
-               if (verbose) {
-                       struct strbuf buf = STRBUF_INIT;
-                       struct pretty_print_context ctx = {0};
-                       pretty_print_commit(CMIT_FMT_ONELINE, commit,
-                                           &buf, &ctx);
-                       printf("%c %s %s\n", sign,
-                              find_unique_abbrev(commit->object.sha1, abbrev),
-                              buf.buf);
-                       strbuf_release(&buf);
-               }
-               else {
-                       printf("%c %s\n", sign,
-                              find_unique_abbrev(commit->object.sha1, abbrev));
-               }
+               print_commit(sign, commit, verbose, abbrev);
                list = list->next;
        }
  
diff --combined cache.h
index 8d18a113cdb9080ef2d5176fe6da84fe76f106ff,c782495a8f097e45c4bedd0e18c1f1250b794728..cbdeaa1d0d23c7e82dd7bd9ea8f97917285b62ff
+++ b/cache.h
@@@ -500,23 -500,8 +500,23 @@@ extern int index_name_is_other(const st
  extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  
 -extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 -extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
 +struct pathspec {
 +      const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
 +      int nr;
 +      unsigned int has_wildcard:1;
 +      unsigned int recursive:1;
 +      int max_depth;
 +      struct pathspec_item {
 +              const char *match;
 +              int len;
 +              unsigned int has_wildcard:1;
 +      } *items;
 +};
 +
 +extern int init_pathspec(struct pathspec *, const char **);
 +extern void free_pathspec(struct pathspec *);
 +extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
 +extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path, int format_check);
  extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
  extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
  
  #define REFRESH_IGNORE_MISSING        0x0008  /* ignore non-existent */
  #define REFRESH_IGNORE_SUBMODULES     0x0010  /* ignore submodules */
  #define REFRESH_IN_PORCELAIN  0x0020  /* user friendly output, not "needs update" */
 -extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, char *header_msg);
 +extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg);
  
  struct lock_file {
        struct lock_file *next;
@@@ -585,7 -570,7 +585,7 @@@ extern enum safe_crlf safe_crlf
  enum auto_crlf {
        AUTO_CRLF_FALSE = 0,
        AUTO_CRLF_TRUE = 1,
-       AUTO_CRLF_INPUT = -1,
+       AUTO_CRLF_INPUT = -1
  };
  
  extern enum auto_crlf auto_crlf;
@@@ -913,8 -898,7 +913,8 @@@ extern struct packed_git 
        time_t mtime;
        int pack_fd;
        unsigned pack_local:1,
 -               pack_keep:1;
 +               pack_keep:1,
 +               do_not_close:1;
        unsigned char sha1[20];
        /* something like ".git/objects/pack/xxxxx.pack" */
        char pack_name[FLEX_ARRAY]; /* more */
@@@ -1014,7 -998,6 +1014,7 @@@ extern int git_config_maybe_bool(const 
  extern int git_config_string(const char **, const char *, const char *);
  extern int git_config_pathname(const char **, const char *, const char *);
  extern int git_config_set(const char *, const char *);
 +extern int git_config_parse_key(const char *, char **, int *);
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
  extern const char *git_etc_gitconfig(void);
diff --combined config.c
index b94de8f51c860b2804c8e16a33343d55e32c2733,822ef8365ce29c48e4b04fcccc47ba2e0b3a255f..fa740a6a60a49512b613f70add97d445f622afd2
+++ b/config.c
@@@ -20,8 -20,7 +20,7 @@@ static int zlib_compression_seen
  
  const char *config_exclusive_filename = NULL;
  
- struct config_item
- {
+ struct config_item {
        struct config_item *next;
        char *name;
        char *value;
@@@ -1093,75 -1092,6 +1092,75 @@@ int git_config_set(const char *key, con
        return git_config_set_multivar(key, value, NULL, 0);
  }
  
 +/*
 + * Auxiliary function to sanity-check and split the key into the section
 + * identifier and variable name.
 + *
 + * Returns 0 on success, -1 when there is an invalid character in the key and
 + * -2 if there is no section name in the key.
 + *
 + * store_key - pointer to char* which will hold a copy of the key with
 + *             lowercase section and variable name
 + * baselen - pointer to int which will hold the length of the
 + *           section + subsection part, can be NULL
 + */
 +int git_config_parse_key(const char *key, char **store_key, int *baselen_)
 +{
 +      int i, dot, baselen;
 +      const char *last_dot = strrchr(key, '.');
 +
 +      /*
 +       * Since "key" actually contains the section name and the real
 +       * key name separated by a dot, we have to know where the dot is.
 +       */
 +
 +      if (last_dot == NULL || last_dot == key) {
 +              error("key does not contain a section: %s", key);
 +              return -2;
 +      }
 +
 +      if (!last_dot[1]) {
 +              error("key does not contain variable name: %s", key);
 +              return -2;
 +      }
 +
 +      baselen = last_dot - key;
 +      if (baselen_)
 +              *baselen_ = baselen;
 +
 +      /*
 +       * Validate the key and while at it, lower case it for matching.
 +       */
 +      *store_key = xmalloc(strlen(key) + 1);
 +
 +      dot = 0;
 +      for (i = 0; key[i]; i++) {
 +              unsigned char c = key[i];
 +              if (c == '.')
 +                      dot = 1;
 +              /* Leave the extended basename untouched.. */
 +              if (!dot || i > baselen) {
 +                      if (!iskeychar(c) ||
 +                          (i == baselen + 1 && !isalpha(c))) {
 +                              error("invalid key: %s", key);
 +                              goto out_free_ret_1;
 +                      }
 +                      c = tolower(c);
 +              } else if (c == '\n') {
 +                      error("invalid key (newline): %s", key);
 +                      goto out_free_ret_1;
 +              }
 +              (*store_key)[i] = c;
 +      }
 +      (*store_key)[i] = 0;
 +
 +      return 0;
 +
 +out_free_ret_1:
 +      free(*store_key);
 +      return -1;
 +}
 +
  /*
   * If value==NULL, unset in (remove from) config,
   * if value_regex!=NULL, disregard key/value pairs where value does not match.
  int git_config_set_multivar(const char *key, const char *value,
        const char *value_regex, int multi_replace)
  {
 -      int i, dot;
        int fd = -1, in_fd;
        int ret;
        char *config_filename;
        struct lock_file *lock = NULL;
 -      const char *last_dot = strrchr(key, '.');
  
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);
        else
                config_filename = git_pathdup("config");
  
 -      /*
 -       * Since "key" actually contains the section name and the real
 -       * key name separated by a dot, we have to know where the dot is.
 -       */
 -
 -      if (last_dot == NULL) {
 -              error("key does not contain a section: %s", key);
 -              ret = 2;
 +      /* parse-key returns negative; flip the sign to feed exit(3) */
 +      ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
 +      if (ret)
                goto out_free;
 -      }
 -      store.baselen = last_dot - key;
  
        store.multi_replace = multi_replace;
  
 -      /*
 -       * Validate the key and while at it, lower case it for matching.
 -       */
 -      store.key = xmalloc(strlen(key) + 1);
 -      dot = 0;
 -      for (i = 0; key[i]; i++) {
 -              unsigned char c = key[i];
 -              if (c == '.')
 -                      dot = 1;
 -              /* Leave the extended basename untouched.. */
 -              if (!dot || i > store.baselen) {
 -                      if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
 -                              error("invalid key: %s", key);
 -                              free(store.key);
 -                              ret = 1;
 -                              goto out_free;
 -                      }
 -                      c = tolower(c);
 -              } else if (c == '\n') {
 -                      error("invalid key (newline): %s", key);
 -                      free(store.key);
 -                      ret = 1;
 -                      goto out_free;
 -              }
 -              store.key[i] = c;
 -      }
 -      store.key[i] = 0;
  
        /*
         * The lock serves a purpose in addition to locking: the new
diff --combined fast-import.c
index e24ee2c63dabcac250be38ae349c035ea0f7b1f7,87f36b36c3de8c90c7bea770208cd17f3fedab25..d9f9a3f524671877d01d0897141a753f8b9c766a
@@@ -24,12 -24,10 +24,12 @@@ Format of STDIN stream
      commit_msg
      ('from' sp committish lf)?
      ('merge' sp committish lf)*
 -    file_change*
 +    (file_change | ls)*
      lf?;
    commit_msg ::= data;
  
 +  ls ::= 'ls' sp '"' quoted(path) '"' lf;
 +
    file_change ::= file_clr
      | file_del
      | file_rnm
    ts    ::= # time since the epoch in seconds, ascii base10 notation;
    tz    ::= # GIT style timezone;
  
 -     # note: comments and cat requests may appear anywhere
 +     # note: comments, ls and cat requests may appear anywhere
       # in the input, except within a data command.  Any form
       # of the data command always escapes the related input
       # from comment processing.
       # must be the first character on that line (an lf
       # preceded it).
       #
 +
    cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf;
 +  ls_tree  ::= 'ls' sp (hexsha1 | idnum) sp path_str lf;
  
    comment ::= '#' not_lf* lf;
    not_lf  ::= # Any byte that is not ASCII newline (LF);
  #define DEPTH_BITS 13
  #define MAX_DEPTH ((1<<DEPTH_BITS)-1)
  
- struct object_entry
- {
+ struct object_entry {
        struct pack_idx_entry idx;
        struct object_entry *next;
        uint32_t type : TYPE_BITS,
                depth : DEPTH_BITS;
  };
  
- struct object_entry_pool
- {
+ struct object_entry_pool {
        struct object_entry_pool *next_pool;
        struct object_entry *next_free;
        struct object_entry *end;
        struct object_entry entries[FLEX_ARRAY]; /* more */
  };
  
- struct mark_set
- {
+ struct mark_set {
        union {
                struct object_entry *marked[1024];
                struct mark_set *sets[1024];
        unsigned int shift;
  };
  
- struct last_object
- {
+ struct last_object {
        struct strbuf data;
        off_t offset;
        unsigned int depth;
        unsigned no_swap : 1;
  };
  
- struct mem_pool
- {
+ struct mem_pool {
        struct mem_pool *next_pool;
        char *next_free;
        char *end;
        uintmax_t space[FLEX_ARRAY]; /* more */
  };
  
- struct atom_str
- {
+ struct atom_str {
        struct atom_str *next_atom;
        unsigned short str_len;
        char str_dat[FLEX_ARRAY]; /* more */
  };
  
  struct tree_content;
- struct tree_entry
- {
+ struct tree_entry {
        struct tree_content *tree;
        struct atom_str *name;
-       struct tree_entry_ms
-       {
+       struct tree_entry_ms {
                uint16_t mode;
                unsigned char sha1[20];
        } versions[2];
  };
  
- struct tree_content
- {
+ struct tree_content {
        unsigned int entry_capacity; /* must match avail_tree_content */
        unsigned int entry_count;
        unsigned int delta_depth;
        struct tree_entry *entries[FLEX_ARRAY]; /* more */
  };
  
- struct avail_tree_content
- {
+ struct avail_tree_content {
        unsigned int entry_capacity; /* must match tree_content */
        struct avail_tree_content *next_avail;
  };
  
- struct branch
- {
+ struct branch {
        struct branch *table_next_branch;
        struct branch *active_next_branch;
        const char *name;
        unsigned char sha1[20];
  };
  
- struct tag
- {
+ struct tag {
        struct tag *next_tag;
        const char *name;
        unsigned int pack_id;
        unsigned char sha1[20];
  };
  
- struct hash_list
- {
+ struct hash_list {
        struct hash_list *next;
        unsigned char sha1[20];
  };
@@@ -278,8 -261,7 +265,7 @@@ typedef enum 
        WHENSPEC_NOW
  } whenspec_type;
  
- struct recent_command
- {
+ struct recent_command {
        struct recent_command *prev;
        struct recent_command *next;
        char *buf;
@@@ -333,7 -315,6 +319,7 @@@ static struct mark_set *marks
  static const char *export_marks_file;
  static const char *import_marks_file;
  static int import_marks_file_from_stream;
 +static int import_marks_file_ignore_missing;
  static int relative_marks_paths;
  
  /* Our last blob */
@@@ -378,7 -359,6 +364,7 @@@ static int cat_blob_fd = STDOUT_FILENO
  
  static void parse_argv(void);
  static void parse_cat_blob(void);
 +static void parse_ls(struct branch *b);
  
  static void write_branch_report(FILE *rpt, struct branch *b)
  {
@@@ -877,7 -857,6 +863,7 @@@ static void start_packfile(void
        p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2);
        strcpy(p->pack_name, tmpfile);
        p->pack_fd = pack_fd;
 +      p->do_not_close = 1;
        pack_file = sha1fd(pack_fd, p->pack_name);
  
        hdr.hdr_signature = htonl(PACK_SIGNATURE);
@@@ -1802,11 -1781,7 +1788,11 @@@ static void read_marks(void
  {
        char line[512];
        FILE *f = fopen(import_marks_file, "r");
 -      if (!f)
 +      if (f)
 +              ;
 +      else if (import_marks_file_ignore_missing && errno == ENOENT)
 +              return; /* Marks file does not exist */
 +      else
                die_errno("cannot read '%s'", import_marks_file);
        while (fgets(line, sizeof(line), f)) {
                uintmax_t mark;
@@@ -2620,8 -2595,6 +2606,8 @@@ static void parse_new_commit(void
                        note_change_n(b, prev_fanout);
                else if (!strcmp("deleteall", command_buf.buf))
                        file_change_deleteall(b);
 +              else if (!prefixcmp(command_buf.buf, "ls "))
 +                      parse_ls(b);
                else {
                        unread_command_buf = 1;
                        break;
@@@ -2845,153 -2818,6 +2831,153 @@@ static void parse_cat_blob(void
        cat_blob(oe, sha1);
  }
  
 +static struct object_entry *dereference(struct object_entry *oe,
 +                                      unsigned char sha1[20])
 +{
 +      unsigned long size;
 +      char *buf = NULL;
 +      if (!oe) {
 +              enum object_type type = sha1_object_info(sha1, NULL);
 +              if (type < 0)
 +                      die("object not found: %s", sha1_to_hex(sha1));
 +              /* cache it! */
 +              oe = insert_object(sha1);
 +              oe->type = type;
 +              oe->pack_id = MAX_PACK_ID;
 +              oe->idx.offset = 1;
 +      }
 +      switch (oe->type) {
 +      case OBJ_TREE:  /* easy case. */
 +              return oe;
 +      case OBJ_COMMIT:
 +      case OBJ_TAG:
 +              break;
 +      default:
 +              die("Not a treeish: %s", command_buf.buf);
 +      }
 +
 +      if (oe->pack_id != MAX_PACK_ID) {       /* in a pack being written */
 +              buf = gfi_unpack_entry(oe, &size);
 +      } else {
 +              enum object_type unused;
 +              buf = read_sha1_file(sha1, &unused, &size);
 +      }
 +      if (!buf)
 +              die("Can't load object %s", sha1_to_hex(sha1));
 +
 +      /* Peel one layer. */
 +      switch (oe->type) {
 +      case OBJ_TAG:
 +              if (size < 40 + strlen("object ") ||
 +                  get_sha1_hex(buf + strlen("object "), sha1))
 +                      die("Invalid SHA1 in tag: %s", command_buf.buf);
 +              break;
 +      case OBJ_COMMIT:
 +              if (size < 40 + strlen("tree ") ||
 +                  get_sha1_hex(buf + strlen("tree "), sha1))
 +                      die("Invalid SHA1 in commit: %s", command_buf.buf);
 +      }
 +
 +      free(buf);
 +      return find_object(sha1);
 +}
 +
 +static struct object_entry *parse_treeish_dataref(const char **p)
 +{
 +      unsigned char sha1[20];
 +      struct object_entry *e;
 +
 +      if (**p == ':') {       /* <mark> */
 +              char *endptr;
 +              e = find_mark(strtoumax(*p + 1, &endptr, 10));
 +              if (endptr == *p + 1)
 +                      die("Invalid mark: %s", command_buf.buf);
 +              if (!e)
 +                      die("Unknown mark: %s", command_buf.buf);
 +              *p = endptr;
 +              hashcpy(sha1, e->idx.sha1);
 +      } else {        /* <sha1> */
 +              if (get_sha1_hex(*p, sha1))
 +                      die("Invalid SHA1: %s", command_buf.buf);
 +              e = find_object(sha1);
 +              *p += 40;
 +      }
 +
 +      while (!e || e->type != OBJ_TREE)
 +              e = dereference(e, sha1);
 +      return e;
 +}
 +
 +static void print_ls(int mode, const unsigned char *sha1, const char *path)
 +{
 +      static struct strbuf line = STRBUF_INIT;
 +
 +      /* See show_tree(). */
 +      const char *type =
 +              S_ISGITLINK(mode) ? commit_type :
 +              S_ISDIR(mode) ? tree_type :
 +              blob_type;
 +
 +      if (!mode) {
 +              /* missing SP path LF */
 +              strbuf_reset(&line);
 +              strbuf_addstr(&line, "missing ");
 +              quote_c_style(path, &line, NULL, 0);
 +              strbuf_addch(&line, '\n');
 +      } else {
 +              /* mode SP type SP object_name TAB path LF */
 +              strbuf_reset(&line);
 +              strbuf_addf(&line, "%06o %s %s\t",
 +                              mode, type, sha1_to_hex(sha1));
 +              quote_c_style(path, &line, NULL, 0);
 +              strbuf_addch(&line, '\n');
 +      }
 +      cat_blob_write(line.buf, line.len);
 +}
 +
 +static void parse_ls(struct branch *b)
 +{
 +      const char *p;
 +      struct tree_entry *root = NULL;
 +      struct tree_entry leaf = {0};
 +
 +      /* ls SP (<treeish> SP)? <path> */
 +      p = command_buf.buf + strlen("ls ");
 +      if (*p == '"') {
 +              if (!b)
 +                      die("Not in a commit: %s", command_buf.buf);
 +              root = &b->branch_tree;
 +      } else {
 +              struct object_entry *e = parse_treeish_dataref(&p);
 +              root = new_tree_entry();
 +              hashcpy(root->versions[1].sha1, e->idx.sha1);
 +              load_tree(root);
 +              if (*p++ != ' ')
 +                      die("Missing space after tree-ish: %s", command_buf.buf);
 +      }
 +      if (*p == '"') {
 +              static struct strbuf uq = STRBUF_INIT;
 +              const char *endp;
 +              strbuf_reset(&uq);
 +              if (unquote_c_style(&uq, p, &endp))
 +                      die("Invalid path: %s", command_buf.buf);
 +              if (*endp)
 +                      die("Garbage after path in: %s", command_buf.buf);
 +              p = uq.buf;
 +      }
 +      tree_content_get(root, p, &leaf);
 +      /*
 +       * A directory in preparation would have a sha1 of zero
 +       * until it is saved.  Save, for simplicity.
 +       */
 +      if (S_ISDIR(leaf.versions[1].mode))
 +              store_tree(&leaf);
 +
 +      print_ls(leaf.versions[1].mode, leaf.versions[1].sha1, p);
 +      if (!b || root != &b->branch_tree)
 +              release_tree_entry(root);
 +}
 +
  static void checkpoint(void)
  {
        checkpoint_requested = 0;
@@@ -3027,8 -2853,7 +3013,8 @@@ static char* make_fast_import_path(cons
        return strbuf_detach(&abs_path, NULL);
  }
  
 -static void option_import_marks(const char *marks, int from_stream)
 +static void option_import_marks(const char *marks,
 +                                      int from_stream, int ignore_missing)
  {
        if (import_marks_file) {
                if (from_stream)
        import_marks_file = make_fast_import_path(marks);
        safe_create_leading_directories_const(import_marks_file);
        import_marks_file_from_stream = from_stream;
 +      import_marks_file_ignore_missing = ignore_missing;
  }
  
  static void option_date_format(const char *fmt)
@@@ -3142,10 -2966,7 +3128,10 @@@ static int parse_one_feature(const cha
        if (!prefixcmp(feature, "date-format=")) {
                option_date_format(feature + 12);
        } else if (!prefixcmp(feature, "import-marks=")) {
 -              option_import_marks(feature + 13, from_stream);
 +              option_import_marks(feature + 13, from_stream, 0);
 +      } else if (!prefixcmp(feature, "import-marks-if-exists=")) {
 +              option_import_marks(feature + strlen("import-marks-if-exists="),
 +                                      from_stream, 1);
        } else if (!prefixcmp(feature, "export-marks=")) {
                option_export_marks(feature + 13);
        } else if (!strcmp(feature, "cat-blob")) {
                relative_marks_paths = 0;
        } else if (!prefixcmp(feature, "force")) {
                force_update = 1;
 -      } else if (!strcmp(feature, "notes")) {
 +      } else if (!strcmp(feature, "notes") || !strcmp(feature, "ls")) {
                ; /* do nothing; we have the feature */
        } else {
                return 0;
@@@ -3297,8 -3118,6 +3283,8 @@@ int main(int argc, const char **argv
        while (read_next_command() != EOF) {
                if (!strcmp("blob", command_buf.buf))
                        parse_new_blob();
 +              else if (!prefixcmp(command_buf.buf, "ls "))
 +                      parse_ls(NULL);
                else if (!prefixcmp(command_buf.buf, "commit "))
                        parse_new_commit();
                else if (!prefixcmp(command_buf.buf, "tag "))
diff --combined merge-recursive.c
index 2a4f739365e6ec72372b32ddb486aee6b21f4c22,42d52cb5bc17e9bb289a6b6c3e90c97d241660a2..3debbc44a05246c24ce431ff3384bf710a9fbb59
@@@ -83,10 -83,8 +83,8 @@@ struct rename_df_conflict_info 
   * Since we want to write the index eventually, we cannot reuse the index
   * for these (temporary) data.
   */
- struct stage_data
- {
-       struct
-       {
+ struct stage_data {
+       struct {
                unsigned mode;
                unsigned char sha[20];
        } stages[4];
@@@ -137,6 -135,7 +135,6 @@@ static void flush_output(struct merge_o
  __attribute__((format (printf, 3, 4)))
  static void output(struct merge_options *o, int v, const char *fmt, ...)
  {
 -      int len;
        va_list ap;
  
        if (!show(o, v))
        strbuf_setlen(&o->obuf, o->obuf.len + o->call_depth * 2);
  
        va_start(ap, fmt);
 -      len = vsnprintf(o->obuf.buf + o->obuf.len, strbuf_avail(&o->obuf), fmt, ap);
 +      strbuf_vaddf(&o->obuf, fmt, ap);
        va_end(ap);
  
 -      if (len < 0)
 -              len = 0;
 -      if (len >= strbuf_avail(&o->obuf)) {
 -              strbuf_grow(&o->obuf, len + 2);
 -              va_start(ap, fmt);
 -              len = vsnprintf(o->obuf.buf + o->obuf.len, strbuf_avail(&o->obuf), fmt, ap);
 -              va_end(ap);
 -              if (len >= strbuf_avail(&o->obuf)) {
 -                      die("this should not happen, your snprintf is broken");
 -              }
 -      }
 -      strbuf_setlen(&o->obuf, o->obuf.len + len);
        strbuf_add(&o->obuf, "\n", 1);
        if (!o->buffer_output)
                flush_output(o);
@@@ -390,8 -401,7 +388,7 @@@ static void make_room_for_directories_o
        }
  }
  
- struct rename
- {
+ struct rename {
        struct diff_filepair *pair;
        struct stage_data *src_entry;
        struct stage_data *dst_entry;
@@@ -704,8 -714,7 +701,7 @@@ static void update_file(struct merge_op
  
  /* Low level file merging, update and removal */
  
- struct merge_file_info
- {
+ struct merge_file_info {
        unsigned char sha[20];
        unsigned mode;
        unsigned clean:1,
diff --combined transport-helper.c
index ba06b70cce6ecc5822fd86d2e3579be76dbeaa33,8866adf08898f5855dd1dea1db4286021ffb880c..0c5b1bd994d79cd6441a5c956a2d851b6bea7d5f
@@@ -12,8 -12,7 +12,7 @@@
  
  static int debug;
  
- struct helper_data
- {
+ struct helper_data {
        const char *name;
        struct child_process *helper;
        FILE *out;
@@@ -973,7 -972,7 +972,7 @@@ static int udt_do_read(struct unidirect
   */
  static int udt_do_write(struct unidirectional_transfer *t)
  {
 -      size_t bytes;
 +      ssize_t bytes;
  
        if (t->bufuse == 0)
                return 0;       /* Nothing to write. */