Merge branch 'dt/refs-check-refname-component-optim'
authorJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 19:18:52 +0000 (12:18 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 19:18:52 +0000 (12:18 -0700)
* dt/refs-check-refname-component-optim:
refs.c: optimize check_refname_component()

1  2 
refs.c
diff --combined refs.c
index ca620f07c1a756a5d5549a40810f845cf89b947b,637a93d695a51872ad2b04289898421ca906af54..dc457742eade241ec6b0001580d1ce1a880fee31
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -6,8 -6,29 +6,29 @@@
  #include "string-list.h"
  
  /*
-  * Make sure "ref" is something reasonable to have under ".git/refs/";
-  * We do not like it if:
+  * How to handle various characters in refnames:
+  * 0: An acceptable character for refs
+  * 1: End-of-component
+  * 2: ., look for a preceding . to reject .. in refs
+  * 3: {, look for a preceding @ to reject @{ in refs
+  * 4: A bad character: ASCII control characters, "~", "^", ":" or SP
+  */
+ static unsigned char refname_disposition[256] = {
+       1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+       4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+       4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4
+ };
+ /*
+  * Try to read one refname component from the front of refname.
+  * Return the length of the component found, or -1 if the component is
+  * not legal.  It is legal if it is something reasonable to have under
+  * ".git/refs/"; We do not like it if:
   *
   * - any path component of it begins with ".", or
   * - it has double dots "..", or
   * - it ends with ".lock"
   * - it contains a "\" (backslash)
   */
- /* Return true iff ch is not allowed in reference names. */
- static inline int bad_ref_char(int ch)
- {
-       if (((unsigned) ch) <= ' ' || ch == 0x7f ||
-           ch == '~' || ch == '^' || ch == ':' || ch == '\\')
-               return 1;
-       /* 2.13 Pattern Matching Notation */
-       if (ch == '*' || ch == '?' || ch == '[') /* Unsupported */
-               return 1;
-       return 0;
- }
- /*
-  * Try to read one refname component from the front of refname.  Return
-  * the length of the component found, or -1 if the component is not
-  * legal.
-  */
  static int check_refname_component(const char *refname, int flags)
  {
        const char *cp;
        char last = '\0';
  
        for (cp = refname; ; cp++) {
-               char ch = *cp;
-               if (ch == '\0' || ch == '/')
+               int ch = *cp & 255;
+               unsigned char disp = refname_disposition[ch];
+               switch (disp) {
+               case 1:
+                       goto out;
+               case 2:
+                       if (last == '.')
+                               return -1; /* Refname contains "..". */
+                       break;
+               case 3:
+                       if (last == '@')
+                               return -1; /* Refname contains "@{". */
                        break;
-               if (bad_ref_char(ch))
-                       return -1; /* Illegal character in refname. */
-               if (last == '.' && ch == '.')
-                       return -1; /* Refname contains "..". */
-               if (last == '@' && ch == '{')
-                       return -1; /* Refname contains "@{". */
+               case 4:
+                       return -1;
+               }
                last = ch;
        }
+ out:
        if (cp == refname)
                return 0; /* Component has zero length. */
        if (refname[0] == '.') {
@@@ -1611,7 -1622,6 +1622,7 @@@ int peel_ref(const char *refname, unsig
  struct warn_if_dangling_data {
        FILE *fp;
        const char *refname;
 +      const struct string_list *refnames;
        const char *msg_fmt;
  };
  
@@@ -1626,12 -1636,8 +1637,12 @@@ static int warn_if_dangling_symref(cons
                return 0;
  
        resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
 -      if (!resolves_to || strcmp(resolves_to, d->refname))
 +      if (!resolves_to
 +          || (d->refname
 +              ? strcmp(resolves_to, d->refname)
 +              : !string_list_has_string(d->refnames, resolves_to))) {
                return 0;
 +      }
  
        fprintf(d->fp, d->msg_fmt, refname);
        fputc('\n', d->fp);
@@@ -1644,18 -1650,6 +1655,18 @@@ void warn_dangling_symref(FILE *fp, con
  
        data.fp = fp;
        data.refname = refname;
 +      data.refnames = NULL;
 +      data.msg_fmt = msg_fmt;
 +      for_each_rawref(warn_if_dangling_symref, &data);
 +}
 +
 +void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)
 +{
 +      struct warn_if_dangling_data data;
 +
 +      data.fp = fp;
 +      data.refname = NULL;
 +      data.refnames = refnames;
        data.msg_fmt = msg_fmt;
        for_each_rawref(warn_if_dangling_symref, &data);
  }
@@@ -2016,6 -2010,7 +2027,6 @@@ int dwim_log(const char *str, int len, 
  
        *log = NULL;
        for (p = ref_rev_parse_rules; *p; p++) {
 -              struct stat st;
                unsigned char hash[20];
                char path[PATH_MAX];
                const char *ref, *it;
                ref = resolve_ref_unsafe(path, hash, 1, NULL);
                if (!ref)
                        continue;
 -              if (!stat(git_path("logs/%s", path), &st) &&
 -                  S_ISREG(st.st_mode))
 +              if (reflog_exists(path))
                        it = path;
 -              else if (strcmp(ref, path) &&
 -                       !stat(git_path("logs/%s", ref), &st) &&
 -                       S_ISREG(st.st_mode))
 +              else if (strcmp(ref, path) && reflog_exists(ref))
                        it = ref;
                else
                        continue;
@@@ -2444,7 -2442,7 +2455,7 @@@ static int curate_packed_ref_fn(struct 
        return 0;
  }
  
 -static int repack_without_refs(const char **refnames, int n)
 +int repack_without_refs(const char **refnames, int n)
  {
        struct ref_dir *packed;
        struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
@@@ -2943,133 -2941,122 +2954,133 @@@ int create_symref(const char *ref_targe
        return 0;
  }
  
 -static char *ref_msg(const char *line, const char *endp)
 -{
 -      const char *ep;
 -      line += 82;
 -      ep = memchr(line, '\n', endp - line);
 -      if (!ep)
 -              ep = endp;
 -      return xmemdupz(line, ep - line);
 +struct read_ref_at_cb {
 +      const char *refname;
 +      unsigned long at_time;
 +      int cnt;
 +      int reccnt;
 +      unsigned char *sha1;
 +      int found_it;
 +
 +      unsigned char osha1[20];
 +      unsigned char nsha1[20];
 +      int tz;
 +      unsigned long date;
 +      char **msg;
 +      unsigned long *cutoff_time;
 +      int *cutoff_tz;
 +      int *cutoff_cnt;
 +};
 +
 +static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
 +              const char *email, unsigned long timestamp, int tz,
 +              const char *message, void *cb_data)
 +{
 +      struct read_ref_at_cb *cb = cb_data;
 +
 +      cb->reccnt++;
 +      cb->tz = tz;
 +      cb->date = timestamp;
 +
 +      if (timestamp <= cb->at_time || cb->cnt == 0) {
 +              if (cb->msg)
 +                      *cb->msg = xstrdup(message);
 +              if (cb->cutoff_time)
 +                      *cb->cutoff_time = timestamp;
 +              if (cb->cutoff_tz)
 +                      *cb->cutoff_tz = tz;
 +              if (cb->cutoff_cnt)
 +                      *cb->cutoff_cnt = cb->reccnt - 1;
 +              /*
 +               * we have not yet updated cb->[n|o]sha1 so they still
 +               * hold the values for the previous record.
 +               */
 +              if (!is_null_sha1(cb->osha1)) {
 +                      hashcpy(cb->sha1, nsha1);
 +                      if (hashcmp(cb->osha1, nsha1))
 +                              warning("Log for ref %s has gap after %s.",
 +                                      cb->refname, show_date(cb->date, cb->tz, DATE_RFC2822));
 +              }
 +              else if (cb->date == cb->at_time)
 +                      hashcpy(cb->sha1, nsha1);
 +              else if (hashcmp(nsha1, cb->sha1))
 +                      warning("Log for ref %s unexpectedly ended on %s.",
 +                              cb->refname, show_date(cb->date, cb->tz,
 +                                                 DATE_RFC2822));
 +              hashcpy(cb->osha1, osha1);
 +              hashcpy(cb->nsha1, nsha1);
 +              cb->found_it = 1;
 +              return 1;
 +      }
 +      hashcpy(cb->osha1, osha1);
 +      hashcpy(cb->nsha1, nsha1);
 +      if (cb->cnt > 0)
 +              cb->cnt--;
 +      return 0;
 +}
 +
 +static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
 +                                const char *email, unsigned long timestamp,
 +                                int tz, const char *message, void *cb_data)
 +{
 +      struct read_ref_at_cb *cb = cb_data;
 +
 +      if (cb->msg)
 +              *cb->msg = xstrdup(message);
 +      if (cb->cutoff_time)
 +              *cb->cutoff_time = timestamp;
 +      if (cb->cutoff_tz)
 +              *cb->cutoff_tz = tz;
 +      if (cb->cutoff_cnt)
 +              *cb->cutoff_cnt = cb->reccnt;
 +      hashcpy(cb->sha1, osha1);
 +      if (is_null_sha1(cb->sha1))
 +              hashcpy(cb->sha1, nsha1);
 +      /* We just want the first entry */
 +      return 1;
  }
  
  int read_ref_at(const char *refname, unsigned long at_time, int cnt,
                unsigned char *sha1, char **msg,
                unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
  {
 -      const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;
 -      char *tz_c;
 -      int logfd, tz, reccnt = 0;
 -      struct stat st;
 -      unsigned long date;
 -      unsigned char logged_sha1[20];
 -      void *log_mapped;
 -      size_t mapsz;
 +      struct read_ref_at_cb cb;
  
 -      logfile = git_path("logs/%s", refname);
 -      logfd = open(logfile, O_RDONLY, 0);
 -      if (logfd < 0)
 -              die_errno("Unable to read log '%s'", logfile);
 -      fstat(logfd, &st);
 -      if (!st.st_size)
 -              die("Log %s is empty.", logfile);
 -      mapsz = xsize_t(st.st_size);
 -      log_mapped = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, logfd, 0);
 -      logdata = log_mapped;
 -      close(logfd);
 +      memset(&cb, 0, sizeof(cb));
 +      cb.refname = refname;
 +      cb.at_time = at_time;
 +      cb.cnt = cnt;
 +      cb.msg = msg;
 +      cb.cutoff_time = cutoff_time;
 +      cb.cutoff_tz = cutoff_tz;
 +      cb.cutoff_cnt = cutoff_cnt;
 +      cb.sha1 = sha1;
 +
 +      for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
 +
 +      if (!cb.reccnt)
 +              die("Log for %s is empty.", refname);
 +      if (cb.found_it)
 +              return 0;
 +
 +      for_each_reflog_ent(refname, read_ref_at_ent_oldest, &cb);
  
 -      lastrec = NULL;
 -      rec = logend = logdata + st.st_size;
 -      while (logdata < rec) {
 -              reccnt++;
 -              if (logdata < rec && *(rec-1) == '\n')
 -                      rec--;
 -              lastgt = NULL;
 -              while (logdata < rec && *(rec-1) != '\n') {
 -                      rec--;
 -                      if (*rec == '>')
 -                              lastgt = rec;
 -              }
 -              if (!lastgt)
 -                      die("Log %s is corrupt.", logfile);
 -              date = strtoul(lastgt + 1, &tz_c, 10);
 -              if (date <= at_time || cnt == 0) {
 -                      tz = strtoul(tz_c, NULL, 10);
 -                      if (msg)
 -                              *msg = ref_msg(rec, logend);
 -                      if (cutoff_time)
 -                              *cutoff_time = date;
 -                      if (cutoff_tz)
 -                              *cutoff_tz = tz;
 -                      if (cutoff_cnt)
 -                              *cutoff_cnt = reccnt - 1;
 -                      if (lastrec) {
 -                              if (get_sha1_hex(lastrec, logged_sha1))
 -                                      die("Log %s is corrupt.", logfile);
 -                              if (get_sha1_hex(rec + 41, sha1))
 -                                      die("Log %s is corrupt.", logfile);
 -                              if (hashcmp(logged_sha1, sha1)) {
 -                                      warning("Log %s has gap after %s.",
 -                                              logfile, show_date(date, tz, DATE_RFC2822));
 -                              }
 -                      }
 -                      else if (date == at_time) {
 -                              if (get_sha1_hex(rec + 41, sha1))
 -                                      die("Log %s is corrupt.", logfile);
 -                      }
 -                      else {
 -                              if (get_sha1_hex(rec + 41, logged_sha1))
 -                                      die("Log %s is corrupt.", logfile);
 -                              if (hashcmp(logged_sha1, sha1)) {
 -                                      warning("Log %s unexpectedly ended on %s.",
 -                                              logfile, show_date(date, tz, DATE_RFC2822));
 -                              }
 -                      }
 -                      munmap(log_mapped, mapsz);
 -                      return 0;
 -              }
 -              lastrec = rec;
 -              if (cnt > 0)
 -                      cnt--;
 -      }
 -
 -      rec = logdata;
 -      while (rec < logend && *rec != '>' && *rec != '\n')
 -              rec++;
 -      if (rec == logend || *rec == '\n')
 -              die("Log %s is corrupt.", logfile);
 -      date = strtoul(rec + 1, &tz_c, 10);
 -      tz = strtoul(tz_c, NULL, 10);
 -      if (get_sha1_hex(logdata, sha1))
 -              die("Log %s is corrupt.", logfile);
 -      if (is_null_sha1(sha1)) {
 -              if (get_sha1_hex(logdata + 41, sha1))
 -                      die("Log %s is corrupt.", logfile);
 -      }
 -      if (msg)
 -              *msg = ref_msg(logdata, logend);
 -      munmap(log_mapped, mapsz);
 -
 -      if (cutoff_time)
 -              *cutoff_time = date;
 -      if (cutoff_tz)
 -              *cutoff_tz = tz;
 -      if (cutoff_cnt)
 -              *cutoff_cnt = reccnt;
        return 1;
  }
  
 +int reflog_exists(const char *refname)
 +{
 +      struct stat st;
 +
 +      return !lstat(git_path("logs/%s", refname), &st) &&
 +              S_ISREG(st.st_mode);
 +}
 +
 +int delete_reflog(const char *refname)
 +{
 +      return remove_path(git_path("logs/%s", refname));
 +}
 +
  static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)
  {
        unsigned char osha1[20], nsha1[20];