Merge branch 'mm/verify-filename-fix' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 11 Jul 2012 19:45:49 +0000 (12:45 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 11 Jul 2012 19:45:49 +0000 (12:45 -0700)
"git diff COPYING HEAD:COPYING" gave a nonsense error message that
claimed that the treeish HEAD did not have COPYING in it.

* mm/verify-filename-fix:
verify_filename(): ask the caller to chose the kind of diagnosis
sha1_name: do not trigger detailed diagnosis for file arguments

1  2 
builtin/grep.c
builtin/rev-parse.c
cache.h
revision.c
setup.c
sha1_name.c
diff --combined builtin/grep.c
index fe1726f5ef60d054938eaa849f2ae6020a95f805,d56555651438e693dd9d13d4ab6de863d4daae29..29adb0ac9399002b07942711863fa3b353926468
@@@ -29,12 -29,25 +29,12 @@@ static int use_threads = 1
  #define THREADS 8
  static pthread_t threads[THREADS];
  
 -static void *load_sha1(const unsigned char *sha1, unsigned long *size,
 -                     const char *name);
 -static void *load_file(const char *filename, size_t *sz);
 -
 -enum work_type {WORK_SHA1, WORK_FILE};
 -
  /* We use one producer thread and THREADS consumer
   * threads. The producer adds struct work_items to 'todo' and the
   * consumers pick work items from the same array.
   */
  struct work_item {
 -      enum work_type type;
 -      char *name;
 -
 -      /* if type == WORK_SHA1, then 'identifier' is a SHA1,
 -       * otherwise type == WORK_FILE, and 'identifier' is a NUL
 -       * terminated filename.
 -       */
 -      void *identifier;
 +      struct grep_source source;
        char done;
        struct strbuf out;
  };
@@@ -72,6 -85,21 +72,6 @@@ static inline void grep_unlock(void
                pthread_mutex_unlock(&grep_mutex);
  }
  
 -/* Used to serialize calls to read_sha1_file. */
 -static pthread_mutex_t read_sha1_mutex;
 -
 -static inline void read_sha1_lock(void)
 -{
 -      if (use_threads)
 -              pthread_mutex_lock(&read_sha1_mutex);
 -}
 -
 -static inline void read_sha1_unlock(void)
 -{
 -      if (use_threads)
 -              pthread_mutex_unlock(&read_sha1_mutex);
 -}
 -
  /* Signalled when a new work_item is added to todo. */
  static pthread_cond_t cond_add;
  
@@@ -85,8 -113,7 +85,8 @@@ static pthread_cond_t cond_result
  
  static int skip_first_line;
  
 -static void add_work(enum work_type type, char *name, void *id)
 +static void add_work(struct grep_opt *opt, enum grep_source_type type,
 +                   const char *name, const void *id)
  {
        grep_lock();
  
                pthread_cond_wait(&cond_write, &grep_mutex);
        }
  
 -      todo[todo_end].type = type;
 -      todo[todo_end].name = name;
 -      todo[todo_end].identifier = id;
 +      grep_source_init(&todo[todo_end].source, type, name, id);
 +      if (opt->binary != GREP_BINARY_TEXT)
 +              grep_source_load_driver(&todo[todo_end].source);
        todo[todo_end].done = 0;
        strbuf_reset(&todo[todo_end].out);
        todo_end = (todo_end + 1) % ARRAY_SIZE(todo);
@@@ -124,6 -151,21 +124,6 @@@ static struct work_item *get_work(void
        return ret;
  }
  
 -static void grep_sha1_async(struct grep_opt *opt, char *name,
 -                          const unsigned char *sha1)
 -{
 -      unsigned char *s;
 -      s = xmalloc(20);
 -      memcpy(s, sha1, 20);
 -      add_work(WORK_SHA1, name, s);
 -}
 -
 -static void grep_file_async(struct grep_opt *opt, char *name,
 -                          const char *filename)
 -{
 -      add_work(WORK_FILE, name, xstrdup(filename));
 -}
 -
  static void work_done(struct work_item *w)
  {
        int old_done;
  
                        write_or_die(1, p, len);
                }
 -              free(w->name);
 -              free(w->identifier);
 +              grep_source_clear(&w->source);
        }
  
        if (old_done != todo_done)
@@@ -173,8 -216,25 +173,8 @@@ static void *run(void *arg
                        break;
  
                opt->output_priv = w;
 -              if (w->type == WORK_SHA1) {
 -                      unsigned long sz;
 -                      void* data = load_sha1(w->identifier, &sz, w->name);
 -
 -                      if (data) {
 -                              hit |= grep_buffer(opt, w->name, data, sz);
 -                              free(data);
 -                      }
 -              } else if (w->type == WORK_FILE) {
 -                      size_t sz;
 -                      void* data = load_file(w->identifier, &sz);
 -                      if (data) {
 -                              hit |= grep_buffer(opt, w->name, data, sz);
 -                              free(data);
 -                      }
 -              } else {
 -                      assert(0);
 -              }
 -
 +              hit |= grep_source(opt, &w->source);
 +              grep_source_clear_data(&w->source);
                work_done(w);
        }
        free_grep_patterns(arg);
@@@ -194,12 -254,11 +194,12 @@@ static void start_threads(struct grep_o
        int i;
  
        pthread_mutex_init(&grep_mutex, NULL);
 -      pthread_mutex_init(&read_sha1_mutex, NULL);
 +      pthread_mutex_init(&grep_read_mutex, NULL);
        pthread_mutex_init(&grep_attr_mutex, NULL);
        pthread_cond_init(&cond_add, NULL);
        pthread_cond_init(&cond_write, NULL);
        pthread_cond_init(&cond_result, NULL);
 +      grep_use_locks = 1;
  
        for (i = 0; i < ARRAY_SIZE(todo); i++) {
                strbuf_init(&todo[i].out, 0);
@@@ -243,16 -302,17 +243,16 @@@ static int wait_all(void
        }
  
        pthread_mutex_destroy(&grep_mutex);
 -      pthread_mutex_destroy(&read_sha1_mutex);
 +      pthread_mutex_destroy(&grep_read_mutex);
        pthread_mutex_destroy(&grep_attr_mutex);
        pthread_cond_destroy(&cond_add);
        pthread_cond_destroy(&cond_write);
        pthread_cond_destroy(&cond_result);
 +      grep_use_locks = 0;
  
        return hit;
  }
  #else /* !NO_PTHREADS */
 -#define read_sha1_lock()
 -#define read_sha1_unlock()
  
  static int wait_all(void)
  {
@@@ -265,8 -325,11 +265,8 @@@ static int grep_config(const char *var
        struct grep_opt *opt = cb;
        char *color = NULL;
  
 -      switch (userdiff_config(var, value)) {
 -      case 0: break;
 -      case -1: return -1;
 -      default: return 0;
 -      }
 +      if (userdiff_config(var, value) < 0)
 +              return -1;
  
        if (!strcmp(var, "grep.extendedregexp")) {
                if (git_config_bool(var, value))
@@@ -311,9 -374,21 +311,9 @@@ static void *lock_and_read_sha1_file(co
  {
        void *data;
  
 -      read_sha1_lock();
 +      grep_read_lock();
        data = read_sha1_file(sha1, type, size);
 -      read_sha1_unlock();
 -      return data;
 -}
 -
 -static void *load_sha1(const unsigned char *sha1, unsigned long *size,
 -                     const char *name)
 -{
 -      enum object_type type;
 -      void *data = lock_and_read_sha1_file(sha1, &type, size);
 -
 -      if (!data)
 -              error(_("'%s': unable to read %s"), name, sha1_to_hex(sha1));
 -
 +      grep_read_unlock();
        return data;
  }
  
@@@ -321,6 -396,7 +321,6 @@@ static int grep_sha1(struct grep_opt *o
                     const char *filename, int tree_name_len)
  {
        struct strbuf pathbuf = STRBUF_INIT;
 -      char *name;
  
        if (opt->relative && opt->prefix_length) {
                quote_path_relative(filename + tree_name_len, -1, &pathbuf,
                strbuf_addstr(&pathbuf, filename);
        }
  
 -      name = strbuf_detach(&pathbuf, NULL);
 -
  #ifndef NO_PTHREADS
        if (use_threads) {
 -              grep_sha1_async(opt, name, sha1);
 +              add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, sha1);
 +              strbuf_release(&pathbuf);
                return 0;
        } else
  #endif
        {
 +              struct grep_source gs;
                int hit;
 -              unsigned long sz;
 -              void *data = load_sha1(sha1, &sz, name);
 -              if (!data)
 -                      hit = 0;
 -              else
 -                      hit = grep_buffer(opt, name, data, sz);
  
 -              free(data);
 -              free(name);
 -              return hit;
 -      }
 -}
 +              grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, sha1);
 +              strbuf_release(&pathbuf);
 +              hit = grep_source(opt, &gs);
  
 -static void *load_file(const char *filename, size_t *sz)
 -{
 -      struct stat st;
 -      char *data;
 -      int i;
 -
 -      if (lstat(filename, &st) < 0) {
 -      err_ret:
 -              if (errno != ENOENT)
 -                      error(_("'%s': %s"), filename, strerror(errno));
 -              return NULL;
 -      }
 -      if (!S_ISREG(st.st_mode))
 -              return NULL;
 -      *sz = xsize_t(st.st_size);
 -      i = open(filename, O_RDONLY);
 -      if (i < 0)
 -              goto err_ret;
 -      data = xmalloc(*sz + 1);
 -      if (st.st_size != read_in_full(i, data, *sz)) {
 -              error(_("'%s': short read %s"), filename, strerror(errno));
 -              close(i);
 -              free(data);
 -              return NULL;
 +              grep_source_clear(&gs);
 +              return hit;
        }
 -      close(i);
 -      data[*sz] = 0;
 -      return data;
  }
  
  static int grep_file(struct grep_opt *opt, const char *filename)
  {
        struct strbuf buf = STRBUF_INIT;
 -      char *name;
  
        if (opt->relative && opt->prefix_length)
                quote_path_relative(filename, -1, &buf, opt->prefix);
        else
                strbuf_addstr(&buf, filename);
 -      name = strbuf_detach(&buf, NULL);
  
  #ifndef NO_PTHREADS
        if (use_threads) {
 -              grep_file_async(opt, name, filename);
 +              add_work(opt, GREP_SOURCE_FILE, buf.buf, filename);
 +              strbuf_release(&buf);
                return 0;
        } else
  #endif
        {
 +              struct grep_source gs;
                int hit;
 -              size_t sz;
 -              void *data = load_file(filename, &sz);
 -              if (!data)
 -                      hit = 0;
 -              else
 -                      hit = grep_buffer(opt, name, data, sz);
  
 -              free(data);
 -              free(name);
 +              grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename);
 +              strbuf_release(&buf);
 +              hit = grep_source(opt, &gs);
 +
 +              grep_source_clear(&gs);
                return hit;
        }
  }
@@@ -503,10 -615,10 +503,10 @@@ static int grep_object(struct grep_opt 
                struct strbuf base;
                int hit, len;
  
 -              read_sha1_lock();
 +              grep_read_lock();
                data = read_object_with_reference(obj->sha1, tree_type,
                                                  &size, NULL);
 -              read_sha1_unlock();
 +              grep_read_unlock();
  
                if (!data)
                        die(_("unable to read tree (%s)"), sha1_to_hex(obj->sha1));
@@@ -600,12 -712,15 +600,12 @@@ static int file_callback(const struct o
        if (!patterns)
                die_errno(_("cannot open '%s'"), arg);
        while (strbuf_getline(&sb, patterns, '\n') == 0) {
 -              char *s;
 -              size_t len;
 -
                /* ignore empty line like grep does */
                if (sb.len == 0)
                        continue;
  
 -              s = strbuf_detach(&sb, &len);
 -              append_grep_pat(grep_opt, s, len, arg, ++lno, GREP_PATTERN);
 +              append_grep_pat(grep_opt, sb.buf, sb.len, arg, ++lno,
 +                              GREP_PATTERN);
        }
        if (!from_stdin)
                fclose(patterns);
@@@ -681,8 -796,9 +681,8 @@@ int cmd_grep(int argc, const char **arg
        struct option options[] = {
                OPT_BOOLEAN(0, "cached", &cached,
                        "search in index instead of in the work tree"),
 -              { OPTION_BOOLEAN, 0, "index", &use_index, NULL,
 -                      "finds in contents not managed by git",
 -                      PARSE_OPT_NOARG | PARSE_OPT_NEGHELP },
 +              OPT_NEGBIT(0, "no-index", &use_index,
 +                       "finds in contents not managed by git", 1),
                OPT_BOOLEAN(0, "untracked", &untracked,
                        "search in both tracked and untracked files"),
                OPT_SET_INT(0, "exclude-standard", &opt_exclude,
        use_threads = 0;
  #endif
  
 -      opt.use_threads = use_threads;
 -
  #ifndef NO_PTHREADS
        if (use_threads) {
 -              if (opt.pre_context || opt.post_context || opt.file_break ||
 -                  opt.funcbody)
 +              if (!(opt.name_only || opt.unmatch_name_only || opt.count)
 +                  && (opt.pre_context || opt.post_context ||
 +                      opt.file_break || opt.funcbody))
                        skip_first_line = 1;
                start_threads(&opt);
        }
        if (!seen_dashdash) {
                int j;
                for (j = i; j < argc; j++)
-                       verify_filename(prefix, argv[j]);
+                       verify_filename(prefix, argv[j], j == i);
        }
  
        paths = get_pathspec(prefix, argv + i);
diff --combined builtin/rev-parse.c
index 733f626f6c3e4ef54d54df923230f7ae4fbb2d7d,3e2f5bd44afb1adee53e554153285f380848c5d9..13495b88f5da1efc2094c0e69abfe93605ee8c03
@@@ -486,7 -486,7 +486,7 @@@ int cmd_rev_parse(int argc, const char 
  
                if (as_is) {
                        if (show_file(arg) && as_is < 2)
-                               verify_filename(prefix, arg);
+                               verify_filename(prefix, arg, 0);
                        continue;
                }
                if (!strcmp(arg,"-n")) {
                        if (!strcmp(arg, "--show-prefix")) {
                                if (prefix)
                                        puts(prefix);
 +                              else
 +                                      putchar('\n');
                                continue;
                        }
                        if (!strcmp(arg, "--show-cdup")) {
                as_is = 1;
                if (!show_file(arg))
                        continue;
-               verify_filename(prefix, arg);
+               verify_filename(prefix, arg, 1);
        }
        if (verify) {
                if (revs_count == 1) {
diff --combined cache.h
index 06413e1584dc762063feaa3728209a4d2864ebc7,35383e3f66977b80736e8418fbb512f3a8d3e8ee..506d1574f2ddba4b19146d5275e620f5f6733402
+++ b/cache.h
@@@ -105,9 -105,6 +105,9 @@@ struct cache_header 
        unsigned int hdr_entries;
  };
  
 +#define INDEX_FORMAT_LB 2
 +#define INDEX_FORMAT_UB 4
 +
  /*
   * The "cache_time" is just the low 32 bits of the
   * time. It doesn't matter if it overflows - we only
@@@ -118,6 -115,48 +118,6 @@@ struct cache_time 
        unsigned int nsec;
  };
  
 -/*
 - * dev/ino/uid/gid/size are also just tracked to the low 32 bits
 - * Again - this is just a (very strong in practice) heuristic that
 - * the inode hasn't changed.
 - *
 - * We save the fields in big-endian order to allow using the
 - * index file over NFS transparently.
 - */
 -struct ondisk_cache_entry {
 -      struct cache_time ctime;
 -      struct cache_time mtime;
 -      unsigned int dev;
 -      unsigned int ino;
 -      unsigned int mode;
 -      unsigned int uid;
 -      unsigned int gid;
 -      unsigned int size;
 -      unsigned char sha1[20];
 -      unsigned short flags;
 -      char name[FLEX_ARRAY]; /* more */
 -};
 -
 -/*
 - * This struct is used when CE_EXTENDED bit is 1
 - * The struct must match ondisk_cache_entry exactly from
 - * ctime till flags
 - */
 -struct ondisk_cache_entry_extended {
 -      struct cache_time ctime;
 -      struct cache_time mtime;
 -      unsigned int dev;
 -      unsigned int ino;
 -      unsigned int mode;
 -      unsigned int uid;
 -      unsigned int gid;
 -      unsigned int size;
 -      unsigned char sha1[20];
 -      unsigned short flags;
 -      unsigned short flags2;
 -      char name[FLEX_ARRAY]; /* more */
 -};
 -
  struct cache_entry {
        struct cache_time ce_ctime;
        struct cache_time ce_mtime;
@@@ -214,6 -253,9 +214,6 @@@ static inline size_t ce_namelen(const s
  }
  
  #define ce_size(ce) cache_entry_size(ce_namelen(ce))
 -#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
 -                          ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
 -                          ondisk_cache_entry_size(ce_namelen(ce)))
  #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
  #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
  #define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
@@@ -264,11 -306,13 +264,11 @@@ static inline unsigned int canon_mode(u
        return S_IFGITLINK;
  }
  
 -#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
  #define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
 -#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
 -#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
  
  struct index_state {
        struct cache_entry **cache;
 +      unsigned int version;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
@@@ -388,7 -432,6 +388,7 @@@ extern char *git_work_tree_cfg
  extern int is_inside_work_tree(void);
  extern int have_git_dir(void);
  extern const char *get_git_dir(void);
 +extern int is_git_directory(const char *path);
  extern char *get_object_directory(void);
  extern char *get_index_file(void);
  extern char *get_graft_file(void);
@@@ -409,7 -452,9 +409,9 @@@ extern const char *setup_git_directory(
  extern char *prefix_path(const char *prefix, int len, const char *path);
  extern const char *prefix_filename(const char *prefix, int len, const char *path);
  extern int check_filename(const char *prefix, const char *name);
- extern void verify_filename(const char *prefix, const char *name);
+ extern void verify_filename(const char *prefix,
+                           const char *name,
+                           int diagnose_misspelt_rev);
  extern void verify_non_filename(const char *prefix, const char *name);
  
  #define INIT_DB_QUIET 0x0001
@@@ -580,10 -625,8 +582,10 @@@ enum rebase_setup_type 
  enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
 +      PUSH_DEFAULT_SIMPLE,
        PUSH_DEFAULT_UPSTREAM,
 -      PUSH_DEFAULT_CURRENT
 +      PUSH_DEFAULT_CURRENT,
 +      PUSH_DEFAULT_UNSPECIFIED
  };
  
  extern enum branch_track git_branch_track;
@@@ -666,19 -709,6 +668,19 @@@ static inline void hashclr(unsigned cha
  #define EMPTY_TREE_SHA1_BIN \
         ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
  
 +#define EMPTY_BLOB_SHA1_HEX \
 +      "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
 +#define EMPTY_BLOB_SHA1_BIN_LITERAL \
 +      "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
 +      "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
 +#define EMPTY_BLOB_SHA1_BIN \
 +      ((const unsigned char *) EMPTY_BLOB_SHA1_BIN_LITERAL)
 +
 +static inline int is_empty_blob_sha1(const unsigned char *sha1)
 +{
 +      return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
 +}
 +
  int git_mkstemp(char *path, size_t n, const char *template);
  
  int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
@@@ -877,8 -907,10 +879,8 @@@ enum date_mode 
  };
  
  const char *show_date(unsigned long time, int timezone, enum date_mode mode);
 -const char *show_date_relative(unsigned long time, int tz,
 -                             const struct timeval *now,
 -                             char *timebuf,
 -                             size_t timebuf_size);
 +void show_date_relative(unsigned long time, int tz, const struct timeval *now,
 +                      struct strbuf *timebuf);
  int parse_date(const char *date, char *buf, int bufsize);
  int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
  void datestamp(char *buf, int bufsize);
@@@ -887,35 -919,15 +889,35 @@@ unsigned long approxidate_careful(cons
  unsigned long approxidate_relative(const char *date, const struct timeval *now);
  enum date_mode parse_date_format(const char *format);
  
 -#define IDENT_WARN_ON_NO_NAME  1
 -#define IDENT_ERROR_ON_NO_NAME 2
 -#define IDENT_NO_DATE        4
 +#define IDENT_STRICT         1
 +#define IDENT_NO_DATE        2
 +#define IDENT_NO_NAME        4
  extern const char *git_author_info(int);
  extern const char *git_committer_info(int);
  extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
  extern const char *fmt_name(const char *name, const char *email);
 +extern const char *ident_default_name(void);
 +extern const char *ident_default_email(void);
 +extern const char *ident_default_date(void);
  extern const char *git_editor(void);
  extern const char *git_pager(int stdout_is_tty);
 +extern int git_ident_config(const char *, const char *, void *);
 +
 +struct ident_split {
 +      const char *name_begin;
 +      const char *name_end;
 +      const char *mail_begin;
 +      const char *mail_end;
 +      const char *date_begin;
 +      const char *date_end;
 +      const char *tz_begin;
 +      const char *tz_end;
 +};
 +/*
 + * Signals an success with 0, but time part of the result may be NULL
 + * if the input lacks timestamp and zone
 + */
 +extern int split_ident_line(struct ident_split *, const char *, int);
  
  struct checkout {
        const char *base_dir;
@@@ -939,9 -951,7 +941,9 @@@ struct cache_def 
  extern int has_symlink_leading_path(const char *name, int len);
  extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
  extern int check_leading_path(const char *name, int len);
 +extern int threaded_check_leading_path(struct cache_def *cache, const char *name, int len);
  extern int has_dirs_only_path(const char *name, int len, int prefix_len);
 +extern int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len);
  extern void schedule_dir_for_removal(const char *name, int len);
  extern void remove_scheduled_dirs(void);
  
@@@ -951,7 -961,6 +953,7 @@@ extern struct alternate_object_databas
        char base[FLEX_ARRAY]; /* more */
  } *alt_odb_list;
  extern void prepare_alt_odb(void);
 +extern void read_info_alternates(const char * relative_base, int depth);
  extern void add_to_alternates_file(const char *reference);
  typedef int alt_odb_fn(struct alternate_object_database *, void *);
  extern void foreach_alt_odb(alt_odb_fn, void*);
@@@ -1030,7 -1039,6 +1032,7 @@@ struct extra_have_objects 
  };
  extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
  extern int server_supports(const char *feature);
 +extern const char *parse_feature_request(const char *features, const char *feature);
  
  extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
  
@@@ -1107,8 -1115,6 +1109,8 @@@ extern int git_config_from_file(config_
  extern void git_config_push_parameter(const char *text);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern int git_config(config_fn_t fn, void *);
 +extern int git_config_with_options(config_fn_t fn, void *,
 +                                 const char *filename, int respect_includes);
  extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
  extern int git_parse_ulong(const char *, unsigned long *);
  extern int git_config_int(const char *, const char *);
@@@ -1124,7 -1130,6 +1126,7 @@@ extern int git_config_parse_key(const c
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  extern int git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
 +extern int git_config_rename_section_in_file(const char *, const char *, const char *);
  extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value, void *cb);
  extern int git_env_bool(const char *, int);
@@@ -1135,14 -1140,11 +1137,14 @@@ extern const char *get_commit_output_en
  
  extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
  
 -extern const char *config_exclusive_filename;
 +struct config_include_data {
 +      int depth;
 +      config_fn_t fn;
 +      void *data;
 +};
 +#define CONFIG_INCLUDE_INIT { 0 }
 +extern int git_config_include(const char *name, const char *value, void *data);
  
 -#define MAX_GITNAME (1000)
 -extern char git_default_email[MAX_GITNAME];
 -extern char git_default_name[MAX_GITNAME];
  #define IDENT_NAME_GIVEN 01
  #define IDENT_MAIL_GIVEN 02
  #define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
@@@ -1175,8 -1177,6 +1177,8 @@@ extern void setup_pager(void)
  extern const char *pager_program;
  extern int pager_in_use(void);
  extern int pager_use_color;
 +extern int term_columns(void);
 +extern int decimal_width(int);
  
  extern const char *editor_program;
  extern const char *askpass_program;
@@@ -1263,6 -1263,4 +1265,6 @@@ extern struct startup_info *startup_inf
  /* builtin/merge.c */
  int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
  
 +int sane_execvp(const char *file, char *const argv[]);
 +
  #endif /* CACHE_H */
diff --combined revision.c
index 935e7a7ba413668c95a6c3e846b9058be07f0425,b3c61e1423deef88791309506a10d85ac55bfa9a..39cc6b0eab413d59c8f8c35a8d994df8d2025741
@@@ -139,32 -139,11 +139,32 @@@ void mark_tree_uninteresting(struct tre
  
  void mark_parents_uninteresting(struct commit *commit)
  {
 -      struct commit_list *parents = commit->parents;
 +      struct commit_list *parents = NULL, *l;
 +
 +      for (l = commit->parents; l; l = l->next)
 +              commit_list_insert(l->item, &parents);
  
        while (parents) {
                struct commit *commit = parents->item;
 -              if (!(commit->object.flags & UNINTERESTING)) {
 +              l = parents;
 +              parents = parents->next;
 +              free(l);
 +
 +              while (commit) {
 +                      /*
 +                       * A missing commit is ok iff its parent is marked
 +                       * uninteresting.
 +                       *
 +                       * We just mark such a thing parsed, so that when
 +                       * it is popped next time around, we won't be trying
 +                       * to parse it and get an error.
 +                       */
 +                      if (!has_sha1_file(commit->object.sha1))
 +                              commit->object.parsed = 1;
 +
 +                      if (commit->object.flags & UNINTERESTING)
 +                              break;
 +
                        commit->object.flags |= UNINTERESTING;
  
                        /*
                         * wasn't uninteresting), in which case we need
                         * to mark its parents recursively too..
                         */
 -                      if (commit->parents)
 -                              mark_parents_uninteresting(commit);
 -              }
 +                      if (!commit->parents)
 +                              break;
  
 -              /*
 -               * A missing commit is ok iff its parent is marked
 -               * uninteresting.
 -               *
 -               * We just mark such a thing parsed, so that when
 -               * it is popped next time around, we won't be trying
 -               * to parse it and get an error.
 -               */
 -              if (!has_sha1_file(commit->object.sha1))
 -                      commit->object.parsed = 1;
 -              parents = parents->next;
 +                      for (l = commit->parents->next; l; l = l->next)
 +                              commit_list_insert(l->item, &parents);
 +                      commit = commit->parents->item;
 +              }
        }
  }
  
@@@ -429,7 -416,7 +429,7 @@@ static int rev_same_tree_as_empty(struc
  static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
  {
        struct commit_list **pp, *parent;
 -      int tree_changed = 0, tree_same = 0;
 +      int tree_changed = 0, tree_same = 0, nth_parent = 0;
  
        /*
         * If we don't do pruning, everything is interesting
        while ((parent = *pp) != NULL) {
                struct commit *p = parent->item;
  
 +              /*
 +               * Do not compare with later parents when we care only about
 +               * the first parent chain, in order to avoid derailing the
 +               * traversal to follow a side branch that brought everything
 +               * in the path we are limited to by the pathspec.
 +               */
 +              if (revs->first_parent_only && nth_parent++)
 +                      break;
                if (parse_commit(p) < 0)
                        die("cannot simplify commit %s (because of %s)",
                            sha1_to_hex(commit->object.sha1),
@@@ -1582,7 -1561,6 +1582,7 @@@ static int handle_revision_opt(struct r
                revs->grep_filter.regflags |= REG_EXTENDED;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
                revs->grep_filter.regflags |= REG_ICASE;
 +              DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
        } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
                revs->grep_filter.fixed = 1;
        } else if (!strcmp(arg, "--all-match")) {
@@@ -1715,21 -1693,17 +1715,21 @@@ int setup_revisions(int argc, const cha
                submodule = opt->submodule;
  
        /* First, search for "--" */
 -      seen_dashdash = 0;
 -      for (i = 1; i < argc; i++) {
 -              const char *arg = argv[i];
 -              if (strcmp(arg, "--"))
 -                      continue;
 -              argv[i] = NULL;
 -              argc = i;
 -              if (argv[i + 1])
 -                      append_prune_data(&prune_data, argv + i + 1);
 +      if (opt && opt->assume_dashdash) {
                seen_dashdash = 1;
 -              break;
 +      } else {
 +              seen_dashdash = 0;
 +              for (i = 1; i < argc; i++) {
 +                      const char *arg = argv[i];
 +                      if (strcmp(arg, "--"))
 +                              continue;
 +                      argv[i] = NULL;
 +                      argc = i;
 +                      if (argv[i + 1])
 +                              append_prune_data(&prune_data, argv + i + 1);
 +                      seen_dashdash = 1;
 +                      break;
 +              }
        }
  
        /* Second, deal with arguments and options */
                         * but the latter we have checked in the main loop.
                         */
                        for (j = i; j < argc; j++)
-                               verify_filename(revs->prefix, argv[j]);
+                               verify_filename(revs->prefix, argv[j], j == i);
  
                        append_prune_data(&prune_data, argv + i);
                        break;
@@@ -2066,16 -2040,10 +2066,16 @@@ static void set_children(struct rev_inf
        }
  }
  
 +void reset_revision_walk(void)
 +{
 +      clear_object_flags(SEEN | ADDED | SHOWN);
 +}
 +
  int prepare_revision_walk(struct rev_info *revs)
  {
        int nr = revs->pending.nr;
        struct object_array_entry *e, *list;
 +      struct commit_list **next = &revs->commits;
  
        e = list = revs->pending.objects;
        revs->pending.nr = 0;
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
                                commit->object.flags |= SEEN;
 -                              commit_list_insert_by_date(commit, &revs->commits);
 +                              next = commit_list_append(commit, next);
                        }
                }
                e++;
        }
 +      commit_list_sort_by_date(&revs->commits);
        if (!revs->leak_pending)
                free(list);
  
@@@ -2161,6 -2128,7 +2161,6 @@@ static int commit_match(struct commit *
        if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
                return 1;
        return grep_buffer(&opt->grep_filter,
 -                         NULL, /* we say nothing, not even filename */
                           commit->buffer, strlen(commit->buffer));
  }
  
diff --combined setup.c
index 731851a4a85161af49a38c481672615a6ac0bbc9,d121b6ab77cb40afff69cb9003be62033a12802f..994976946b4b451ee41508a2649987c9b4e9bdbc
+++ b/setup.c
@@@ -53,11 -53,17 +53,17 @@@ int check_filename(const char *prefix, 
        die_errno("failed to stat '%s'", arg);
  }
  
- static void NORETURN die_verify_filename(const char *prefix, const char *arg)
+ static void NORETURN die_verify_filename(const char *prefix,
+                                        const char *arg,
+                                        int diagnose_misspelt_rev)
  {
        unsigned char sha1[20];
        unsigned mode;
  
+       if (!diagnose_misspelt_rev)
+               die("%s: no such path in the working tree.\n"
+                   "Use '-- <path>...' to specify paths that do not exist locally.",
+                   arg);
        /*
         * Saying "'(icase)foo' does not exist in the index" when the
         * user gave us ":(icase)foo" is just stupid.  A magic pathspec
   * as true, because even if such a filename were to exist, we want
   * it to be preceded by the "--" marker (or we want the user to
   * use a format like "./-filename")
+  *
+  * The "diagnose_misspelt_rev" is used to provide a user-friendly
+  * diagnosis when dying upon finding that "name" is not a pathname.
+  * If set to 1, the diagnosis will try to diagnose "name" as an
+  * invalid object name (e.g. HEAD:foo). If set to 0, the diagnosis
+  * will only complain about an inexisting file.
+  *
+  * This function is typically called to check that a "file or rev"
+  * argument is unambiguous. In this case, the caller will want
+  * diagnose_misspelt_rev == 1 when verifying the first non-rev
+  * argument (which could have been a revision), and
+  * diagnose_misspelt_rev == 0 for the next ones (because we already
+  * saw a filename, there's not ambiguity anymore).
   */
- void verify_filename(const char *prefix, const char *arg)
+ void verify_filename(const char *prefix,
+                    const char *arg,
+                    int diagnose_misspelt_rev)
  {
        if (*arg == '-')
                die("bad flag '%s' used after filename", arg);
        if (check_filename(prefix, arg))
                return;
-       die_verify_filename(prefix, arg);
+       die_verify_filename(prefix, arg, diagnose_misspelt_rev);
  }
  
  /*
@@@ -247,7 -268,7 +268,7 @@@ const char **get_pathspec(const char *p
   *    a proper "ref:", or a regular file HEAD that has a properly
   *    formatted sha1 object name.
   */
 -static int is_git_directory(const char *suspect)
 +int is_git_directory(const char *suspect)
  {
        char path[PATH_MAX];
        size_t len = strlen(suspect);
@@@ -569,15 -590,13 +590,15 @@@ static const char *setup_nongit(const c
        return NULL;
  }
  
 -static dev_t get_device_or_die(const char *path, const char *prefix)
 +static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)
  {
        struct stat buf;
 -      if (stat(path, &buf))
 -              die_errno("failed to stat '%s%s%s'",
 +      if (stat(path, &buf)) {
 +              die_errno("failed to stat '%*s%s%s'",
 +                              prefix_len,
                                prefix ? prefix : "",
                                prefix ? "/" : "", path);
 +      }
        return buf.st_dev;
  }
  
@@@ -591,7 -610,7 +612,7 @@@ static const char *setup_git_directory_
        static char cwd[PATH_MAX+1];
        const char *gitdirenv, *ret;
        char *gitfile;
 -      int len, offset, ceil_offset;
 +      int len, offset, offset_parent, ceil_offset;
        dev_t current_device = 0;
        int one_filesystem = 1;
  
         */
        one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
        if (one_filesystem)
 -              current_device = get_device_or_die(".", NULL);
 +              current_device = get_device_or_die(".", NULL, 0);
        for (;;) {
                gitfile = (char*)read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT);
                if (gitfile)
                if (is_git_directory("."))
                        return setup_bare_git_dir(cwd, offset, len, nongit_ok);
  
 -              while (--offset > ceil_offset && cwd[offset] != '/');
 -              if (offset <= ceil_offset)
 +              offset_parent = offset;
 +              while (--offset_parent > ceil_offset && cwd[offset_parent] != '/');
 +              if (offset_parent <= ceil_offset)
                        return setup_nongit(cwd, nongit_ok);
                if (one_filesystem) {
 -                      dev_t parent_device = get_device_or_die("..", cwd);
 +                      dev_t parent_device = get_device_or_die("..", cwd, offset);
                        if (parent_device != current_device) {
                                if (nongit_ok) {
                                        if (chdir(cwd))
                                        return NULL;
                                }
                                cwd[offset] = '\0';
 -                              die("Not a git repository (or any parent up to mount parent %s)\n"
 +                              die("Not a git repository (or any parent up to mount point %s)\n"
                                "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd);
                        }
                }
                        cwd[offset] = '\0';
                        die_errno("Cannot change to '%s/..'", cwd);
                }
 +              offset = offset_parent;
        }
  }
  
diff --combined sha1_name.c
index c6331136d19c5224078fa78b6e5e794fcc587fe2,aff224b2a0cbc955b05eb67119bf5b15d05e2d64..5d81ea0564c8d90b417eb8ec5ed8c32baa2c3ea3
@@@ -856,22 -856,10 +856,22 @@@ int interpret_branch_name(const char *n
        len = cp + tmp_len - name;
        cp = xstrndup(name, cp - name);
        upstream = branch_get(*cp ? cp : NULL);
 -      if (!upstream
 -          || !upstream->merge
 -          || !upstream->merge[0]->dst)
 -              return error("No upstream branch found for '%s'", cp);
 +      /*
 +       * Upstream can be NULL only if cp refers to HEAD and HEAD
 +       * points to something different than a branch.
 +       */
 +      if (!upstream)
 +              return error(_("HEAD does not point to a branch"));
 +      if (!upstream->merge || !upstream->merge[0]->dst) {
 +              if (!ref_exists(upstream->refname))
 +                      return error(_("No such branch: '%s'"), cp);
 +              if (!upstream->merge)
 +                      return error(_("No upstream configured for branch '%s'"),
 +                                   upstream->name);
 +              return error(
 +                      _("Upstream branch '%s' not stored as a remote-tracking branch"),
 +                      upstream->merge[0]->src);
 +      }
        free(cp);
        cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
        strbuf_reset(buf);
@@@ -1127,7 -1115,7 +1127,7 @@@ int get_sha1_with_context_1(const char 
                        if (new_filename)
                                filename = new_filename;
                        ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
-                       if (only_to_die) {
+                       if (ret && only_to_die) {
                                diagnose_invalid_sha1_path(prefix, filename,
                                                           tree_sha1, object_name);
                                free(object_name);