Merge branch 'tr/previous-branch'
authorJunio C Hamano <gitster@pobox.com>
Wed, 28 Jan 2009 23:00:27 +0000 (15:00 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 28 Jan 2009 23:00:27 +0000 (15:00 -0800)
* tr/previous-branch:
t1505: remove debugging cruft
Simplify parsing branch switching events in reflog
Introduce for_each_recent_reflog_ent().
interpret_nth_last_branch(): plug small memleak
Fix reflog parsing for a malformed branch switching entry
Fix parsing of @{-1}@{1}
interpret_nth_last_branch(): avoid traversing the reflog twice
checkout: implement "-" abbreviation, add docs and tests
sha1_name: support @{-N} syntax in get_sha1()
sha1_name: tweak @{-N} lookup
checkout: implement "@{-N}" shortcut name for N-th last branch

Conflicts:
sha1_name.c

1  2 
builtin-checkout.c
cache.h
sha1_name.c
diff --combined builtin-checkout.c
index 6cdb320ae72b438031049892a9bb78e5bc523c73,b0a101bac7432719d62c013d98bb0e6171e45406..20b34ce6e10d9b863226b501cf5a35178b898995
@@@ -38,13 -38,23 +38,13 @@@ struct checkout_opts 
  static int post_checkout_hook(struct commit *old, struct commit *new,
                              int changed)
  {
 -      struct child_process proc;
 -      const char *name = git_path("hooks/post-checkout");
 -      const char *argv[5];
 +      return run_hook(NULL, "post-checkout",
 +                      sha1_to_hex(old ? old->object.sha1 : null_sha1),
 +                      sha1_to_hex(new ? new->object.sha1 : null_sha1),
 +                      changed ? "1" : "0", NULL);
 +      /* "new" can be NULL when checking out from the index before
 +         a commit exists. */
  
 -      if (access(name, X_OK) < 0)
 -              return 0;
 -
 -      memset(&proc, 0, sizeof(proc));
 -      argv[0] = name;
 -      argv[1] = xstrdup(sha1_to_hex(old ? old->object.sha1 : null_sha1));
 -      argv[2] = xstrdup(sha1_to_hex(new->object.sha1));
 -      argv[3] = changed ? "1" : "0";
 -      argv[4] = NULL;
 -      proc.argv = argv;
 -      proc.no_stdin = 1;
 -      proc.stdout_to_stderr = 1;
 -      return run_command(&proc);
  }
  
  static int update_some(const unsigned char *sha1, const char *base, int baselen,
@@@ -230,7 -240,7 +230,7 @@@ static int checkout_paths(struct tree *
  
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
 -              pathspec_match(pathspec, ps_matched, ce->name, 0);
 +              match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
        }
  
        if (report_path_error(ps_matched, pathspec, 0))
        /* Any unmerged paths? */
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
 -              if (pathspec_match(pathspec, NULL, ce->name, 0)) {
 +              if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
                        if (!ce_stage(ce))
                                continue;
                        if (opts->force) {
        state.refresh_cache = 1;
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
 -              if (pathspec_match(pathspec, NULL, ce->name, 0)) {
 +              if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
                        if (!ce_stage(ce)) {
                                errs |= checkout_entry(ce, &state, NULL);
                                continue;
@@@ -351,8 -361,16 +351,16 @@@ struct branch_info 
  static void setup_branch_path(struct branch_info *branch)
  {
        struct strbuf buf = STRBUF_INIT;
-       strbuf_addstr(&buf, "refs/heads/");
-       strbuf_addstr(&buf, branch->name);
+       int ret;
+       if ((ret = interpret_nth_last_branch(branch->name, &buf))
+           && ret == strlen(branch->name)) {
+               branch->name = xstrdup(buf.buf);
+               strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
+       } else {
+               strbuf_addstr(&buf, "refs/heads/");
+               strbuf_addstr(&buf, branch->name);
+       }
        branch->path = strbuf_detach(&buf, NULL);
  }
  
@@@ -661,6 -679,9 +669,9 @@@ int cmd_checkout(int argc, const char *
                arg = argv[0];
                has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
  
+               if (!strcmp(arg, "-"))
+                       arg = "@{-1}";
                if (get_sha1(arg, rev)) {
                        if (has_dash_dash)          /* case (1) */
                                die("invalid reference: %s", arg);
diff --combined cache.h
index c17fbf58a764e0c96cc4f23d2804ab16e6df7aea,0dd9168be58be2147edeb5ca81dca6dd7abd8f05..45e713e9283dcf7b241291ac121e4d4a771f5796
+++ b/cache.h
  #define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
  #endif
  
 +void git_inflate_init(z_streamp strm);
 +void git_inflate_end(z_streamp strm);
 +int git_inflate(z_streamp strm, int flush);
 +
  #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
  #define DTYPE(de)     ((de)->d_type)
  #else
@@@ -667,6 -663,7 +667,7 @@@ extern int read_ref(const char *filenam
  extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
  extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
  extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
+ extern int interpret_nth_last_branch(const char *str, struct strbuf *);
  
  extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
  extern const char *ref_rev_parse_rules[];
@@@ -721,10 -718,6 +722,10 @@@ struct checkout 
  
  extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
  extern int has_symlink_leading_path(int len, const char *name);
 +extern int has_symlink_or_noent_leading_path(int len, const char *name);
 +extern int has_dirs_only_path(int len, const char *name, int prefix_len);
 +extern void invalidate_lstat_cache(int len, const char *name);
 +extern void clear_lstat_cache(void);
  
  extern struct alternate_object_database {
        struct alternate_object_database *next;
@@@ -941,6 -934,7 +942,6 @@@ extern int ws_fix_copy(char *, const ch
  extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
  
  /* ls-files */
 -int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
  int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
  void overlay_tree_on_cache(const char *tree_name, const char *prefix);
  
diff --combined sha1_name.c
index 722fc35a6d98e1c260c6e14738ad3b2d41d924aa,7d95bbb27ae7485525d89281fb79f8f1fbc3da51..5d0ac0263d04d7ec72a3b7dec4aaf47aec80da5e
@@@ -238,8 -238,28 +238,28 @@@ static int ambiguous_path(const char *p
        return slash;
  }
  
+ /*
+  * *string and *len will only be substituted, and *string returned (for
+  * later free()ing) if the string passed in is of the form @{-<n>}.
+  */
+ static char *substitute_nth_last_branch(const char **string, int *len)
+ {
+       struct strbuf buf = STRBUF_INIT;
+       int ret = interpret_nth_last_branch(*string, &buf);
+       if (ret == *len) {
+               size_t size;
+               *string = strbuf_detach(&buf, &size);
+               *len = size;
+               return (char *)*string;
+       }
+       return NULL;
+ }
  int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
  {
+       char *last_branch = substitute_nth_last_branch(&str, &len);
        const char **p, *r;
        int refs_found = 0;
  
                                break;
                }
        }
+       free(last_branch);
        return refs_found;
  }
  
  int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
  {
+       char *last_branch = substitute_nth_last_branch(&str, &len);
        const char **p;
        int logs_found = 0;
  
                if (!warn_ambiguous_refs)
                        break;
        }
+       free(last_branch);
        return logs_found;
  }
  
+ static int get_sha1_1(const char *name, int len, unsigned char *sha1);
  static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
  {
        static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
        if (len == 40 && !get_sha1_hex(str, sha1))
                return 0;
  
-       /* basic@{time or number} format to query ref-log */
+       /* basic@{time or number or -number} format to query ref-log */
        reflog_len = at = 0;
 -      if (str[len-1] == '}') {
 +      if (len && str[len-1] == '}') {
-               for (at = 0; at < len - 1; at++) {
+               for (at = len-2; at >= 0; at--) {
                        if (str[at] == '@' && str[at+1] == '{') {
                                reflog_len = (len-1) - (at+2);
                                len = at;
                return -1;
  
        if (!len && reflog_len) {
+               struct strbuf buf = STRBUF_INIT;
+               int ret;
+               /* try the @{-N} syntax for n-th checkout */
+               ret = interpret_nth_last_branch(str+at, &buf);
+               if (ret > 0) {
+                       /* substitute this branch name and restart */
+                       return get_sha1_1(buf.buf, buf.len, sha1);
+               } else if (ret == 0) {
+                       return -1;
+               }
                /* allow "@{...}" to mean the current branch reflog */
                refs_found = dwim_ref("HEAD", 4, sha1, &real_ref);
        } else if (reflog_len)
        return 0;
  }
  
- static int get_sha1_1(const char *name, int len, unsigned char *sha1);
  static int get_parent(const char *name, int len,
                      unsigned char *result, int idx)
  {
@@@ -674,6 -707,92 +707,92 @@@ static int get_sha1_oneline(const char 
        return retval;
  }
  
+ struct grab_nth_branch_switch_cbdata {
+       long cnt, alloc;
+       struct strbuf *buf;
+ };
+ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
+                                 const char *email, unsigned long timestamp, int tz,
+                                 const char *message, void *cb_data)
+ {
+       struct grab_nth_branch_switch_cbdata *cb = cb_data;
+       const char *match = NULL, *target = NULL;
+       size_t len;
+       int nth;
+       if (!prefixcmp(message, "checkout: moving from ")) {
+               match = message + strlen("checkout: moving from ");
+               target = strstr(match, " to ");
+       }
+       if (!match || !target)
+               return 0;
+       len = target - match;
+       nth = cb->cnt++ % cb->alloc;
+       strbuf_reset(&cb->buf[nth]);
+       strbuf_add(&cb->buf[nth], match, len);
+       return 0;
+ }
+ /*
+  * This reads "@{-N}" syntax, finds the name of the Nth previous
+  * branch we were on, and places the name of the branch in the given
+  * buf and returns the number of characters parsed if successful.
+  *
+  * If the input is not of the accepted format, it returns a negative
+  * number to signal an error.
+  *
+  * If the input was ok but there are not N branch switches in the
+  * reflog, it returns 0.
+  */
+ int interpret_nth_last_branch(const char *name, struct strbuf *buf)
+ {
+       long nth;
+       int i, retval;
+       struct grab_nth_branch_switch_cbdata cb;
+       const char *brace;
+       char *num_end;
+       if (name[0] != '@' || name[1] != '{' || name[2] != '-')
+               return -1;
+       brace = strchr(name, '}');
+       if (!brace)
+               return -1;
+       nth = strtol(name+3, &num_end, 10);
+       if (num_end != brace)
+               return -1;
+       if (nth <= 0)
+               return -1;
+       cb.alloc = nth;
+       cb.buf = xmalloc(nth * sizeof(struct strbuf));
+       for (i = 0; i < nth; i++)
+               strbuf_init(&cb.buf[i], 20);
+       cb.cnt = 0;
+       retval = 0;
+       for_each_recent_reflog_ent("HEAD", grab_nth_branch_switch, 40960, &cb);
+       if (cb.cnt < nth) {
+               cb.cnt = 0;
+               for (i = 0; i < nth; i++)
+                       strbuf_release(&cb.buf[i]);
+               for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
+       }
+       if (cb.cnt < nth)
+               goto release_return;
+       i = cb.cnt % nth;
+       strbuf_reset(buf);
+       strbuf_add(buf, cb.buf[i].buf, cb.buf[i].len);
+       retval = brace-name+1;
+ release_return:
+       for (i = 0; i < nth; i++)
+               strbuf_release(&cb.buf[i]);
+       free(cb.buf);
+       return retval;
+ }
  /*
   * This is like "get_sha1_basic()", except it allows "sha1 expressions",
   * notably "xyz^" for "parent of xyz"