Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Mon, 30 May 2011 07:09:55 +0000 (00:09 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 30 May 2011 07:09:55 +0000 (00:09 -0700)
* maint:
git-submodule.sh: separate parens by a space to avoid confusing some shells
Documentation/technical/api-diff.txt: correct name of diff_unmerge()
read_gitfile_gently: use ssize_t to hold read result
remove tests of always-false condition
rerere.c: diagnose a corrupt MERGE_RR when hitting EOF between TAB and '\0'

1  2 
git-submodule.sh
rerere.c
setup.c
transport.c
diff --combined git-submodule.sh
index bf110e9cb77a0e9930c427408e18d323db2c8916,4361ae418cfc032f793d14af057e2b178922ca1f..d189a24c71c44806a9c1381e2a8e5993269e568a
@@@ -8,7 -8,7 +8,7 @@@ dashless=$(basename "$0" | sed -e 's/-
  USAGE="[--quiet] add [-b branch] [-f|--force] [--reference <repository>] [--] <repository> [<path>]
     or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
     or: $dashless [--quiet] init [--] [<path>...]
 -   or: $dashless [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
 +   or: $dashless [--quiet] update [--init] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
     or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
     or: $dashless [--quiet] foreach [--recursive] <command>
     or: $dashless [--quiet] sync [--] [<path>...]"
@@@ -402,9 -402,6 +402,9 @@@ cmd_update(
                -N|--no-fetch)
                        nofetch=1
                        ;;
 +              -f|--force)
 +                      force=$1
 +                      ;;
                -r|--rebase)
                        update="rebase"
                        ;;
  
                if test "$subsha1" != "$sha1"
                then
 -                      force=
 -                      if test -z "$subsha1"
 +                      subforce=$force
 +                      # If we don't already have a -f flag and the submodule has never been checked out
 +                      if test -z "$subsha1" -a -z "$force"
                        then
 -                              force="-f"
 +                              subforce="-f"
                        fi
  
                        if test -z "$nofetch"
                                # Run fetch only if $sha1 isn't present or it
                                # is not reachable from a ref.
                                (clear_local_git_env; cd "$path" &&
-                                       ((rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
+                                       ( (rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
                                         test -z "$rev") || git-fetch)) ||
                                die "Unable to fetch in submodule path '$path'"
                        fi
                                msg="merged in"
                                ;;
                        *)
 -                              command="git checkout $force -q"
 +                              command="git checkout $subforce -q"
                                action="checkout"
                                msg="checked out"
                                ;;
diff --combined rerere.c
index e3407cf33417385c89aa9a1a9a9d5ad2809f57fb,6ec452f7bc689c02ff663ab2cd1f6f1a068fe08c..dcb525a4d03faeba53d108d2e175a6d04f99d160
+++ b/rerere.c
@@@ -47,8 -47,14 +47,14 @@@ static void read_rr(struct string_list 
                name = xstrdup(buf);
                if (fgetc(in) != '\t')
                        die("corrupt MERGE_RR");
-               for (i = 0; i < sizeof(buf) && (buf[i] = fgetc(in)); i++)
-                       ; /* do nothing */
+               for (i = 0; i < sizeof(buf); i++) {
+                       int c = fgetc(in);
+                       if (c < 0)
+                               die("corrupt MERGE_RR");
+                       buf[i] = c;
+                       if (c == 0)
+                                break;
+               }
                if (i == sizeof(buf))
                        die("filename too long");
                string_list_insert(rr, buf)->util = name;
@@@ -671,88 -677,3 +677,88 @@@ int rerere_forget(const char **pathspec
        }
        return write_rr(&merge_rr, fd);
  }
 +
 +static time_t rerere_created_at(const char *name)
 +{
 +      struct stat st;
 +      return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
 +}
 +
 +static time_t rerere_last_used_at(const char *name)
 +{
 +      struct stat st;
 +      return stat(rerere_path(name, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
 +}
 +
 +static void unlink_rr_item(const char *name)
 +{
 +      unlink(rerere_path(name, "thisimage"));
 +      unlink(rerere_path(name, "preimage"));
 +      unlink(rerere_path(name, "postimage"));
 +      rmdir(git_path("rr-cache/%s", name));
 +}
 +
 +struct rerere_gc_config_cb {
 +      int cutoff_noresolve;
 +      int cutoff_resolve;
 +};
 +
 +static int git_rerere_gc_config(const char *var, const char *value, void *cb)
 +{
 +      struct rerere_gc_config_cb *cf = cb;
 +
 +      if (!strcmp(var, "gc.rerereresolved"))
 +              cf->cutoff_resolve = git_config_int(var, value);
 +      else if (!strcmp(var, "gc.rerereunresolved"))
 +              cf->cutoff_noresolve = git_config_int(var, value);
 +      else
 +              return git_default_config(var, value, cb);
 +      return 0;
 +}
 +
 +void rerere_gc(struct string_list *rr)
 +{
 +      struct string_list to_remove = STRING_LIST_INIT_DUP;
 +      DIR *dir;
 +      struct dirent *e;
 +      int i, cutoff;
 +      time_t now = time(NULL), then;
 +      struct rerere_gc_config_cb cf = { 15, 60 };
 +
 +      git_config(git_rerere_gc_config, &cf);
 +      dir = opendir(git_path("rr-cache"));
 +      if (!dir)
 +              die_errno("unable to open rr-cache directory");
 +      while ((e = readdir(dir))) {
 +              if (is_dot_or_dotdot(e->d_name))
 +                      continue;
 +
 +              then = rerere_last_used_at(e->d_name);
 +              if (then) {
 +                      cutoff = cf.cutoff_resolve;
 +              } else {
 +                      then = rerere_created_at(e->d_name);
 +                      if (!then)
 +                              continue;
 +                      cutoff = cf.cutoff_noresolve;
 +              }
 +              if (then < now - cutoff * 86400)
 +                      string_list_append(&to_remove, e->d_name);
 +      }
 +      closedir(dir);
 +      for (i = 0; i < to_remove.nr; i++)
 +              unlink_rr_item(to_remove.items[i].string);
 +      string_list_clear(&to_remove, 0);
 +}
 +
 +void rerere_clear(struct string_list *merge_rr)
 +{
 +      int i;
 +
 +      for (i = 0; i < merge_rr->nr; i++) {
 +              const char *name = (const char *)merge_rr->items[i].util;
 +              if (!has_rerere_resolution(name))
 +                      unlink_rr_item(name);
 +      }
 +      unlink_or_warn(git_path("MERGE_RR"));
 +}
diff --combined setup.c
index 013ad1127534367e64afeec3749e617ceccfb5d4,e7a37863ccdfe8c38e079f4d971f76a523bfd83b..ce87900ce3c68ace0f231827bdda0e9a65ef15b3
+++ b/setup.c
@@@ -85,17 -85,8 +85,17 @@@ static void NORETURN die_verify_filenam
  {
        unsigned char sha1[20];
        unsigned mode;
 -      /* try a detailed diagnostic ... */
 -      get_sha1_with_mode_1(arg, sha1, &mode, 0, prefix);
 +
 +      /*
 +       * Saying "'(icase)foo' does not exist in the index" when the
 +       * user gave us ":(icase)foo" is just stupid.  A magic pathspec
 +       * begins with a colon and is followed by a non-alnum; do not
 +       * let get_sha1_with_mode_1(only_to_die=1) to even trigger.
 +       */
 +      if (!(arg[0] == ':' && !isalnum(arg[1])))
 +              /* try a detailed diagnostic ... */
 +              get_sha1_with_mode_1(arg, sha1, &mode, 1, prefix);
 +
        /* ... or fall back the most general message. */
        die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
            "Use '--' to separate paths from revisions", arg);
@@@ -135,105 -126,6 +135,105 @@@ void verify_non_filename(const char *pr
            "Use '--' to separate filenames from revisions", arg);
  }
  
 +/*
 + * Magic pathspec
 + *
 + * NEEDSWORK: These need to be moved to dir.h or even to a new
 + * pathspec.h when we restructure get_pathspec() users to use the
 + * "struct pathspec" interface.
 + *
 + * Possible future magic semantics include stuff like:
 + *
 + *    { PATHSPEC_NOGLOB, '!', "noglob" },
 + *    { PATHSPEC_ICASE, '\0', "icase" },
 + *    { PATHSPEC_RECURSIVE, '*', "recursive" },
 + *    { PATHSPEC_REGEXP, '\0', "regexp" },
 + *
 + */
 +#define PATHSPEC_FROMTOP    (1<<0)
 +
 +static struct pathspec_magic {
 +      unsigned bit;
 +      char mnemonic; /* this cannot be ':'! */
 +      const char *name;
 +} pathspec_magic[] = {
 +      { PATHSPEC_FROMTOP, '/', "top" },
 +};
 +
 +/*
 + * Take an element of a pathspec and check for magic signatures.
 + * Append the result to the prefix.
 + *
 + * For now, we only parse the syntax and throw out anything other than
 + * "top" magic.
 + *
 + * NEEDSWORK: This needs to be rewritten when we start migrating
 + * get_pathspec() users to use the "struct pathspec" interface.  For
 + * example, a pathspec element may be marked as case-insensitive, but
 + * the prefix part must always match literally, and a single stupid
 + * string cannot express such a case.
 + */
 +static const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
 +{
 +      unsigned magic = 0;
 +      const char *copyfrom = elt;
 +      int i;
 +
 +      if (elt[0] != ':') {
 +              ; /* nothing to do */
 +      } else if (elt[1] == '(') {
 +              /* longhand */
 +              const char *nextat;
 +              for (copyfrom = elt + 2;
 +                   *copyfrom && *copyfrom != ')';
 +                   copyfrom = nextat) {
 +                      size_t len = strcspn(copyfrom, ",)");
 +                      if (copyfrom[len] == ')')
 +                              nextat = copyfrom + len;
 +                      else
 +                              nextat = copyfrom + len + 1;
 +                      if (!len)
 +                              continue;
 +                      for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
 +                              if (strlen(pathspec_magic[i].name) == len &&
 +                                  !strncmp(pathspec_magic[i].name, copyfrom, len)) {
 +                                      magic |= pathspec_magic[i].bit;
 +                                      break;
 +                              }
 +                      if (ARRAY_SIZE(pathspec_magic) <= i)
 +                              die("Invalid pathspec magic '%.*s' in '%s'",
 +                                  (int) len, copyfrom, elt);
 +              }
 +              if (*copyfrom == ')')
 +                      copyfrom++;
 +      } else {
 +              /* shorthand */
 +              for (copyfrom = elt + 1;
 +                   *copyfrom && *copyfrom != ':';
 +                   copyfrom++) {
 +                      char ch = *copyfrom;
 +
 +                      if (!is_pathspec_magic(ch))
 +                              break;
 +                      for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
 +                              if (pathspec_magic[i].mnemonic == ch) {
 +                                      magic |= pathspec_magic[i].bit;
 +                                      break;
 +                              }
 +                      if (ARRAY_SIZE(pathspec_magic) <= i)
 +                              die("Unimplemented pathspec magic '%c' in '%s'",
 +                                  ch, elt);
 +              }
 +              if (*copyfrom == ':')
 +                      copyfrom++;
 +      }
 +
 +      if (magic & PATHSPEC_FROMTOP)
 +              return xstrdup(copyfrom);
 +      else
 +              return prefix_path(prefix, prefixlen, copyfrom);
 +}
 +
  const char **get_pathspec(const char *prefix, const char **pathspec)
  {
        const char *entry = *pathspec;
        dst = pathspec;
        prefixlen = prefix ? strlen(prefix) : 0;
        while (*src) {
 -              const char *p = prefix_path(prefix, prefixlen, *src);
 -              *(dst++) = p;
 +              *(dst++) = prefix_pathspec(prefix, prefixlen, *src);
                src++;
        }
        *dst = NULL;
@@@ -382,7 -275,7 +382,7 @@@ const char *read_gitfile_gently(const c
        const char *slash;
        struct stat st;
        int fd;
-       size_t len;
+       ssize_t len;
  
        if (stat(path, &st))
                return NULL;
@@@ -432,7 -325,6 +432,7 @@@ static const char *setup_explicit_git_d
        const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
        const char *worktree;
        char *gitfile;
 +      int offset;
  
        if (PATH_MAX - 40 < strlen(gitdirenv))
                die("'$%s' too big", GIT_DIR_ENVIRONMENT);
                return NULL;
        }
  
 -      if (!prefixcmp(cwd, worktree) &&
 -          cwd[strlen(worktree)] == '/') { /* cwd inside worktree */
 +      offset = dir_inside_of(cwd, worktree);
 +      if (offset >= 0) {      /* cwd inside worktree? */
                set_git_dir(real_path(gitdirenv));
                if (chdir(worktree))
                        die_errno("Could not chdir to '%s'", worktree);
                cwd[len++] = '/';
                cwd[len] = '\0';
                free(gitfile);
 -              return cwd + strlen(worktree) + 1;
 +              return cwd + offset;
        }
  
        /* cwd outside worktree */
diff --combined transport.c
index 1a3998ee51e6edf090b18cfd90f4c2c78aece7c6,69dae711037ea7a54d86910932d74ac2b24b57fd..c9c8056f9de69bd378cd271d70363b5560f13e07
@@@ -156,7 -156,7 +156,7 @@@ static void set_upstreams(struct transp
                        continue;
                if (!ref->peer_ref)
                        continue;
-               if (!ref->new_sha1 || is_null_sha1(ref->new_sha1))
+               if (is_null_sha1(ref->new_sha1))
                        continue;
  
                /* Follow symbolic refs (mainly for HEAD). */
@@@ -1190,20 -1190,14 +1190,20 @@@ literal_copy
        return xstrdup(url);
  }
  
 -int refs_from_alternate_cb(struct alternate_object_database *e, void *cb)
 +struct alternate_refs_data {
 +      alternate_ref_fn *fn;
 +      void *data;
 +};
 +
 +static int refs_from_alternate_cb(struct alternate_object_database *e,
 +                                void *data)
  {
        char *other;
        size_t len;
        struct remote *remote;
        struct transport *transport;
        const struct ref *extra;
 -      alternate_ref_fn *ref_fn = cb;
 +      struct alternate_refs_data *cb = data;
  
        e->name[-1] = '\0';
        other = xstrdup(real_path(e->base));
        for (extra = transport_get_remote_refs(transport);
             extra;
             extra = extra->next)
 -              ref_fn(extra, NULL);
 +              cb->fn(extra, cb->data);
        transport_disconnect(transport);
        free(other);
        return 0;
  }
 +
 +void for_each_alternate_ref(alternate_ref_fn fn, void *data)
 +{
 +      struct alternate_refs_data cb;
 +      cb.fn = fn;
 +      cb.data = data;
 +      foreach_alt_odb(refs_from_alternate_cb, &cb);
 +}