Merge branch 'maint'
authorShawn O. Pearce <spearce@spearce.org>
Thu, 25 Sep 2008 15:27:53 +0000 (08:27 -0700)
committerShawn O. Pearce <spearce@spearce.org>
Thu, 25 Sep 2008 15:27:53 +0000 (08:27 -0700)
* maint:
  Update release notes for 1.6.0.3
  checkout: Do not show local changes when in quiet mode
  for-each-ref: Fix --format=%(subject) for log message without newlines
  git-stash.sh: don't default to refs/stash if invalid ref supplied
  maint: check return of split_cmdline to avoid bad config strings

1  2 
builtin-checkout.c
builtin-for-each-ref.c
builtin-merge.c
git.c

diff --combined builtin-checkout.c
@@@ -184,7 -184,7 +184,7 @@@ struct checkout_opts 
        int force;
        int writeout_error;
  
 -      char *new_branch;
 +      const char *new_branch;
        int new_branch_log;
        enum branch_track track;
  };
@@@ -328,7 -328,7 +328,7 @@@ static int merge_working_tree(struct ch
            commit_locked_index(lock_file))
                die("unable to write new index file");
  
-       if (!opts->force)
+       if (!opts->force && !opts->quiet)
                show_local_changes(&new->commit->object);
  
        return 0;
@@@ -464,28 -464,13 +464,28 @@@ int cmd_checkout(int argc, const char *
  
        git_config(git_default_config, NULL);
  
 -      opts.track = git_branch_track;
 +      opts.track = BRANCH_TRACK_UNSPECIFIED;
  
        argc = parse_options(argc, argv, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
  
 -      if (!opts.new_branch && (opts.track != git_branch_track))
 -              die("git checkout: --track and --no-track require -b");
 +      /* --track without -b should DWIM */
 +      if (0 < opts.track && !opts.new_branch) {
 +              const char *argv0 = argv[0];
 +              if (!argc || !strcmp(argv0, "--"))
 +                      die ("--track needs a branch name");
 +              if (!prefixcmp(argv0, "refs/"))
 +                      argv0 += 5;
 +              if (!prefixcmp(argv0, "remotes/"))
 +                      argv0 += 8;
 +              argv0 = strchr(argv0, '/');
 +              if (!argv0 || !argv0[1])
 +                      die ("Missing branch name; try -b");
 +              opts.new_branch = argv0 + 1;
 +      }
 +
 +      if (opts.track == BRANCH_TRACK_UNSPECIFIED)
 +              opts.track = git_branch_track;
  
        if (opts.force && opts.merge)
                die("git checkout: -f and -m are incompatible");
@@@ -580,18 -565,6 +580,18 @@@ no_reference
                return checkout_paths(source_tree, pathspec);
        }
  
 +      if (opts.new_branch) {
 +              struct strbuf buf;
 +              strbuf_init(&buf, 0);
 +              strbuf_addstr(&buf, "refs/heads/");
 +              strbuf_addstr(&buf, opts.new_branch);
 +              if (!get_sha1(buf.buf, rev))
 +                      die("git checkout: branch %s already exists", opts.new_branch);
 +              if (check_ref_format(buf.buf))
 +                      die("git checkout: we do not like '%s' as a branch name.", opts.new_branch);
 +              strbuf_release(&buf);
 +      }
 +
        if (new.name && !new.commit) {
                die("Cannot switch branch to a non-commit.");
        }
diff --combined builtin-for-each-ref.c
@@@ -321,8 -321,8 +321,8 @@@ static const char *find_wholine(const c
  static const char *copy_line(const char *buf)
  {
        const char *eol = strchr(buf, '\n');
-       if (!eol)
-               return "";
+       if (!eol) // simulate strchrnul()
+               eol = buf + strlen(buf);
        return xmemdupz(buf, eol - buf);
  }
  
@@@ -545,107 -545,6 +545,107 @@@ static void grab_values(struct atom_val
        }
  }
  
 +/*
 + * generate a format suitable for scanf from a ref_rev_parse_rules
 + * rule, that is replace the "%.*s" spec with a "%s" spec
 + */
 +static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
 +{
 +      char *spec;
 +
 +      spec = strstr(rule, "%.*s");
 +      if (!spec || strstr(spec + 4, "%.*s"))
 +              die("invalid rule in ref_rev_parse_rules: %s", rule);
 +
 +      /* copy all until spec */
 +      strncpy(scanf_fmt, rule, spec - rule);
 +      scanf_fmt[spec - rule] = '\0';
 +      /* copy new spec */
 +      strcat(scanf_fmt, "%s");
 +      /* copy remaining rule */
 +      strcat(scanf_fmt, spec + 4);
 +
 +      return;
 +}
 +
 +/*
 + * Shorten the refname to an non-ambiguous form
 + */
 +static char *get_short_ref(struct refinfo *ref)
 +{
 +      int i;
 +      static char **scanf_fmts;
 +      static int nr_rules;
 +      char *short_name;
 +
 +      /* pre generate scanf formats from ref_rev_parse_rules[] */
 +      if (!nr_rules) {
 +              size_t total_len = 0;
 +
 +              /* the rule list is NULL terminated, count them first */
 +              for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
 +                      /* no +1 because strlen("%s") < strlen("%.*s") */
 +                      total_len += strlen(ref_rev_parse_rules[nr_rules]);
 +
 +              scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
 +
 +              total_len = 0;
 +              for (i = 0; i < nr_rules; i++) {
 +                      scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
 +                                      + total_len;
 +                      gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
 +                      total_len += strlen(ref_rev_parse_rules[i]);
 +              }
 +      }
 +
 +      /* bail out if there are no rules */
 +      if (!nr_rules)
 +              return ref->refname;
 +
 +      /* buffer for scanf result, at most ref->refname must fit */
 +      short_name = xstrdup(ref->refname);
 +
 +      /* skip first rule, it will always match */
 +      for (i = nr_rules - 1; i > 0 ; --i) {
 +              int j;
 +              int short_name_len;
 +
 +              if (1 != sscanf(ref->refname, scanf_fmts[i], short_name))
 +                      continue;
 +
 +              short_name_len = strlen(short_name);
 +
 +              /*
 +               * check if the short name resolves to a valid ref,
 +               * but use only rules prior to the matched one
 +               */
 +              for (j = 0; j < i; j++) {
 +                      const char *rule = ref_rev_parse_rules[j];
 +                      unsigned char short_objectname[20];
 +
 +                      /*
 +                       * the short name is ambiguous, if it resolves
 +                       * (with this previous rule) to a valid ref
 +                       * read_ref() returns 0 on success
 +                       */
 +                      if (!read_ref(mkpath(rule, short_name_len, short_name),
 +                                    short_objectname))
 +                              break;
 +              }
 +
 +              /*
 +               * short name is non-ambiguous if all previous rules
 +               * haven't resolved to a valid ref
 +               */
 +              if (j == i)
 +                      return short_name;
 +      }
 +
 +      free(short_name);
 +      return ref->refname;
 +}
 +
 +
  /*
   * Parse the object referred by ref, and grab needed value.
   */
@@@ -671,33 -570,13 +671,33 @@@ static void populate_value(struct refin
        for (i = 0; i < used_atom_cnt; i++) {
                const char *name = used_atom[i];
                struct atom_value *v = &ref->value[i];
 -              if (!strcmp(name, "refname"))
 -                      v->s = ref->refname;
 -              else if (!strcmp(name, "*refname")) {
 -                      int len = strlen(ref->refname);
 -                      char *s = xmalloc(len + 4);
 -                      sprintf(s, "%s^{}", ref->refname);
 -                      v->s = s;
 +              int deref = 0;
 +              if (*name == '*') {
 +                      deref = 1;
 +                      name++;
 +              }
 +              if (!prefixcmp(name, "refname")) {
 +                      const char *formatp = strchr(name, ':');
 +                      const char *refname = ref->refname;
 +
 +                      /* look for "short" refname format */
 +                      if (formatp) {
 +                              formatp++;
 +                              if (!strcmp(formatp, "short"))
 +                                      refname = get_short_ref(ref);
 +                              else
 +                                      die("unknown refname format %s",
 +                                          formatp);
 +                      }
 +
 +                      if (!deref)
 +                              v->s = refname;
 +                      else {
 +                              int len = strlen(refname);
 +                              char *s = xmalloc(len + 4);
 +                              sprintf(s, "%s^{}", refname);
 +                              v->s = s;
 +                      }
                }
        }
  
diff --combined builtin-merge.c
@@@ -22,7 -22,6 +22,7 @@@
  #include "log-tree.h"
  #include "color.h"
  #include "rerere.h"
 +#include "help.h"
  
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@@ -78,9 -77,7 +78,9 @@@ static int option_parse_message(const s
  static struct strategy *get_strategy(const char *name)
  {
        int i;
 -      struct strbuf err;
 +      struct strategy *ret;
 +      static struct cmdnames main_cmds, other_cmds;
 +      static int loaded;
  
        if (!name)
                return NULL;
                if (!strcmp(name, all_strategy[i].name))
                        return &all_strategy[i];
  
 -      strbuf_init(&err, 0);
 -      for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
 -              strbuf_addf(&err, " %s", all_strategy[i].name);
 -      fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
 -      fprintf(stderr, "Available strategies are:%s.\n", err.buf);
 -      exit(1);
 +      if (!loaded) {
 +              struct cmdnames not_strategies;
 +              loaded = 1;
 +
 +              memset(&not_strategies, 0, sizeof(struct cmdnames));
 +              load_command_list("git-merge-", &main_cmds, &other_cmds);
 +              for (i = 0; i < main_cmds.cnt; i++) {
 +                      int j, found = 0;
 +                      struct cmdname *ent = main_cmds.names[i];
 +                      for (j = 0; j < ARRAY_SIZE(all_strategy); j++)
 +                              if (!strncmp(ent->name, all_strategy[j].name, ent->len)
 +                                              && !all_strategy[j].name[ent->len])
 +                                      found = 1;
 +                      if (!found)
 +                              add_cmdname(&not_strategies, ent->name, ent->len);
 +                      exclude_cmds(&main_cmds, &not_strategies);
 +              }
 +      }
 +      if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) {
 +              fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
 +              fprintf(stderr, "Available strategies are:");
 +              for (i = 0; i < main_cmds.cnt; i++)
 +                      fprintf(stderr, " %s", main_cmds.names[i]->name);
 +              fprintf(stderr, ".\n");
 +              if (other_cmds.cnt) {
 +                      fprintf(stderr, "Available custom strategies are:");
 +                      for (i = 0; i < other_cmds.cnt; i++)
 +                              fprintf(stderr, " %s", other_cmds.names[i]->name);
 +                      fprintf(stderr, ".\n");
 +              }
 +              exit(1);
 +      }
 +
 +      ret = xmalloc(sizeof(struct strategy));
 +      memset(ret, 0, sizeof(struct strategy));
 +      ret->name = xstrdup(name);
 +      return ret;
  }
  
  static void append_strategy(struct strategy *s)
@@@ -476,6 -442,8 +476,8 @@@ static int git_merge_config(const char 
  
                buf = xstrdup(v);
                argc = split_cmdline(buf, &argv);
+               if (argc < 0)
+                       die("Bad branch.%s.mergeoptions string", branch);
                argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
                memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
                argc++;
@@@ -868,11 -836,6 +870,11 @@@ int cmd_merge(int argc, const char **ar
                if (argc != 1)
                        die("Can merge only exactly one commit into "
                                "empty head");
 +              if (squash)
 +                      die("Squash commit into empty head not supported yet");
 +              if (!allow_fast_forward)
 +                      die("Non-fast-forward commit does not make sense into "
 +                          "an empty head");
                remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
                if (!remote_head)
                        die("%s - not something we can merge", argv[0]);
diff --combined git.c
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -162,6 -162,8 +162,8 @@@ static int handle_alias(int *argcp, con
                            alias_string + 1, alias_command);
                }
                count = split_cmdline(alias_string, &new_argv);
+               if (count < 0)
+                       die("Bad alias.%s string", alias_command);
                option_count = handle_options(&new_argv, &count, &envchanged);
                if (envchanged)
                        die("alias '%s' changes environment variables\n"
@@@ -364,7 -366,7 +366,7 @@@ static void handle_internal_command(in
        if (sizeof(ext) > 1) {
                i = strlen(argv[0]) - strlen(ext);
                if (i > 0 && !strcmp(argv[0] + i, ext)) {
 -                      char *argv0 = strdup(argv[0]);
 +                      char *argv0 = xstrdup(argv[0]);
                        argv[0] = cmd = argv0;
                        argv0[i] = '\0';
                }
@@@ -499,9 -501,7 +501,9 @@@ int main(int argc, const char **argv
                                cmd, argv[0]);
                        exit(1);
                }
 -              help_unknown_cmd(cmd);
 +              argv[0] = help_unknown_cmd(cmd);
 +              handle_internal_command(argc, argv);
 +              execv_dashed_external(argv);
        }
  
        fprintf(stderr, "Failed to run command '%s': %s\n",