Merge branch 'jc/magic-pathspec'
authorJunio C Hamano <gitster@pobox.com>
Mon, 23 May 2011 16:58:35 +0000 (09:58 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 23 May 2011 16:58:35 +0000 (09:58 -0700)
* jc/magic-pathspec:
setup.c: Fix some "symbol not declared" sparse warnings
t3703: Skip tests using directory name ":" on Windows
revision.c: leave a note for "a lone :" enhancement
t3703, t4208: add test cases for magic pathspec
rev/path disambiguation: further restrict "misspelled index entry" diag
fix overslow :/no-such-string-ever-existed diagnostics
fix overstrict :<path> diagnosis
grep: use get_pathspec() correctly
pathspec: drop "lone : means no pathspec" from get_pathspec()
Revert "magic pathspec: add ":(icase)path" to match case insensitively"
magic pathspec: add ":(icase)path" to match case insensitively
magic pathspec: futureproof shorthand form
magic pathspec: add tentative ":/path/from/top/level" pathspec support

1  2 
builtin/grep.c
cache.h
git-compat-util.h
revision.c
setup.c
sha1_name.c
diff --combined builtin/grep.c
index 3ee2ec51def59695813ee14f104d142a62d530b6,222dd6d9afa98d02fc661926732620bc65d0881d..931eee0d750cd8662347e2a7c8e8a89d23228e17
@@@ -244,7 -244,7 +244,7 @@@ static void start_threads(struct grep_o
                err = pthread_create(&threads[i], NULL, run, o);
  
                if (err)
 -                      die("grep: failed to create thread: %s",
 +                      die(_("grep: failed to create thread: %s"),
                            strerror(err));
        }
  }
@@@ -302,19 -302,6 +302,19 @@@ static int grep_config(const char *var
        default: return 0;
        }
  
 +      if (!strcmp(var, "grep.extendedregexp")) {
 +              if (git_config_bool(var, value))
 +                      opt->regflags |= REG_EXTENDED;
 +              else
 +                      opt->regflags &= ~REG_EXTENDED;
 +              return 0;
 +      }
 +
 +      if (!strcmp(var, "grep.linenumber")) {
 +              opt->linenum = git_config_bool(var, value);
 +              return 0;
 +      }
 +
        if (!strcmp(var, "color.grep"))
                opt->color = git_config_colorbool(var, value, -1);
        else if (!strcmp(var, "color.grep.context"))
@@@ -362,7 -349,7 +362,7 @@@ static void *load_sha1(const unsigned c
        void *data = lock_and_read_sha1_file(sha1, &type, size);
  
        if (!data)
 -              error("'%s': unable to read %s", name, sha1_to_hex(sha1));
 +              error(_("'%s': unable to read %s"), name, sha1_to_hex(sha1));
  
        return data;
  }
@@@ -413,21 -400,21 +413,21 @@@ static void *load_file(const char *file
        if (lstat(filename, &st) < 0) {
        err_ret:
                if (errno != ENOENT)
 -                      error("'%s': %s", filename, strerror(errno));
 -              return 0;
 +                      error(_("'%s': %s"), filename, strerror(errno));
 +              return NULL;
        }
        if (!S_ISREG(st.st_mode))
 -              return 0;
 +              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));
 +              error(_("'%s': short read %s"), filename, strerror(errno));
                close(i);
                free(data);
 -              return 0;
 +              return NULL;
        }
        close(i);
        data[*sz] = 0;
@@@ -486,7 -473,7 +486,7 @@@ static void run_pager(struct grep_opt *
        argv[path_list->nr] = NULL;
  
        if (prefix && chdir(prefix))
 -              die("Failed to chdir: %s", prefix);
 +              die(_("Failed to chdir: %s"), prefix);
        status = run_command_v_opt(argv, RUN_USING_SHELL);
        if (status)
                exit(status);
@@@ -533,18 -520,18 +533,18 @@@ static int grep_cache(struct grep_opt *
  static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                     struct tree_desc *tree, struct strbuf *base, int tn_len)
  {
 -      int hit = 0, matched = 0;
 +      int hit = 0, match = 0;
        struct name_entry entry;
        int old_baselen = base->len;
  
        while (tree_entry(tree, &entry)) {
                int te_len = tree_entry_len(entry.path, entry.sha1);
  
 -              if (matched != 2) {
 -                      matched = tree_entry_interesting(&entry, base, tn_len, pathspec);
 -                      if (matched == -1)
 -                              break; /* no more matches */
 -                      if (!matched)
 +              if (match != 2) {
 +                      match = tree_entry_interesting(&entry, base, tn_len, pathspec);
 +                      if (match < 0)
 +                              break;
 +                      if (match == 0)
                                continue;
                }
  
  
                        data = lock_and_read_sha1_file(entry.sha1, &type, &size);
                        if (!data)
 -                              die("unable to read tree (%s)",
 +                              die(_("unable to read tree (%s)"),
                                    sha1_to_hex(entry.sha1));
  
                        strbuf_addch(base, '/');
@@@ -592,7 -579,7 +592,7 @@@ static int grep_object(struct grep_opt 
                data = read_object_with_reference(obj->sha1, tree_type,
                                                  &size, NULL);
                if (!data)
 -                      die("unable to read tree (%s)", sha1_to_hex(obj->sha1));
 +                      die(_("unable to read tree (%s)"), sha1_to_hex(obj->sha1));
  
                len = name ? strlen(name) : 0;
                strbuf_init(&base, PATH_MAX + len + 1);
                free(data);
                return hit;
        }
 -      die("unable to grep from object of type %s", typename(obj->type));
 +      die(_("unable to grep from object of type %s"), typename(obj->type));
  }
  
  static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
@@@ -662,7 -649,7 +662,7 @@@ static int context_callback(const struc
        }
        value = strtol(arg, (char **)&endp, 10);
        if (*endp) {
 -              return error("switch `%c' expects a numerical value",
 +              return error(_("switch `%c' expects a numerical value"),
                             opt->short_name);
        }
        grep_opt->pre_context = grep_opt->post_context = value;
@@@ -679,7 -666,7 +679,7 @@@ static int file_callback(const struct o
  
        patterns = from_stdin ? stdin : fopen(arg, "r");
        if (!patterns)
 -              die_errno("cannot open '%s'", arg);
 +              die_errno(_("cannot open '%s'"), arg);
        while (strbuf_getline(&sb, patterns, '\n') == 0) {
                char *s;
                size_t len;
@@@ -782,7 -769,7 +782,7 @@@ int cmd_grep(int argc, const char **arg
                OPT_BOOLEAN('F', "fixed-strings", &opt.fixed,
                        "interpret patterns as fixed strings"),
                OPT_GROUP(""),
 -              OPT_BOOLEAN('n', NULL, &opt.linenum, "show line numbers"),
 +              OPT_BOOLEAN('n', "line-number", &opt.linenum, "show line numbers"),
                OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1),
                OPT_BIT('H', NULL, &opt.pathname, "show filenames", 1),
                OPT_NEGBIT(0, "full-name", &opt.relative,
        }
  
        if (!opt.pattern_list)
 -              die("no pattern given.");
 +              die(_("no pattern given."));
        if (!opt.fixed && opt.ignore_case)
                opt.regflags |= REG_ICASE;
        if ((opt.regflags != REG_NEWLINE) && opt.fixed)
 -              die("cannot mix --fixed-strings and regexp");
 +              die(_("cannot mix --fixed-strings and regexp"));
  
  #ifndef NO_PTHREADS
        if (online_cpus() == 1 || !grep_threads_ok(&opt))
                if (!get_sha1(arg, sha1)) {
                        struct object *object = parse_object(sha1);
                        if (!object)
 -                              die("bad object %s", arg);
 +                              die(_("bad object %s"), arg);
                        add_object_array(object, arg, &list);
                        continue;
                }
                        verify_filename(prefix, argv[j]);
        }
  
-       if (i < argc)
-               paths = get_pathspec(prefix, argv + i);
-       else if (prefix) {
-               paths = xcalloc(2, sizeof(const char *));
-               paths[0] = prefix;
-               paths[1] = NULL;
-       }
+       paths = get_pathspec(prefix, argv + i);
        init_pathspec(&pathspec, paths);
        pathspec.max_depth = opt.max_depth;
        pathspec.recursive = 1;
  
        if (show_in_pager && (cached || list.nr))
 -              die("--open-files-in-pager only works on the worktree");
 +              die(_("--open-files-in-pager only works on the worktree"));
  
        if (show_in_pager && opt.pattern_list && !opt.pattern_list->next) {
                const char *pager = path_list.items[0].string;
  
        if (!use_index) {
                if (cached)
 -                      die("--cached cannot be used with --no-index.");
 +                      die(_("--cached cannot be used with --no-index."));
                if (list.nr)
 -                      die("--no-index cannot be used with revs.");
 +                      die(_("--no-index cannot be used with revs."));
                hit = grep_directory(&opt, &pathspec);
        } else if (!list.nr) {
                if (!cached)
                hit = grep_cache(&opt, &pathspec, cached);
        } else {
                if (cached)
 -                      die("both --cached and trees are given.");
 +                      die(_("both --cached and trees are given."));
                hit = grep_objects(&opt, &pathspec, &list);
        }
  
diff --combined cache.h
index 66ddfb71b66922e47d95efed4a80df39f1e829da,a9e641967a4a155b7e1c4738671d88cf6d359113..f1c0887d6075212dba22c7feb62316480afb1fa2
+++ b/cache.h
@@@ -437,7 -437,6 +437,7 @@@ extern void verify_non_filename(const c
  
  #define INIT_DB_QUIET 0x0001
  
 +extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int);
  extern int init_db(const char *template_dir, unsigned int flags);
  
  #define alloc_nr(x) (((x)+16)*3/2)
@@@ -511,7 -510,7 +511,7 @@@ struct pathspec 
        struct pathspec_item {
                const char *match;
                int len;
 -              unsigned int has_wildcard:1;
 +              unsigned int use_wildcard:1;
        } *items;
  };
  
@@@ -544,7 -543,6 +544,7 @@@ extern NORETURN void unable_to_lock_ind
  extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
  extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
  extern int commit_lock_file(struct lock_file *);
 +extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
  extern int commit_locked_index(struct lock_file *);
@@@ -558,7 -556,6 +558,7 @@@ extern int trust_executable_bit
  extern int trust_ctime;
  extern int quote_path_fully;
  extern int has_symlinks;
 +extern int minimum_abbrev, default_abbrev;
  extern int ignore_case;
  extern int assume_unchanged;
  extern int prefer_symlink_refs;
@@@ -573,7 -570,6 +573,7 @@@ extern int core_compression_seen
  extern size_t packed_git_window_size;
  extern size_t packed_git_limit;
  extern size_t delta_base_cache_limit;
 +extern unsigned long big_file_threshold;
  extern int read_replace_refs;
  extern int fsync_object_files;
  extern int core_preload_index;
@@@ -606,7 -602,7 +606,7 @@@ enum eol 
  #endif
  };
  
 -extern enum eol eol;
 +extern enum eol core_eol;
  
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
@@@ -676,24 -672,14 +676,24 @@@ extern char *sha1_pack_name(const unsig
  extern char *sha1_pack_index_name(const unsigned char *sha1);
  extern const char *find_unique_abbrev(const unsigned char *sha1, int);
  extern const unsigned char null_sha1[20];
 -static inline int is_null_sha1(const unsigned char *sha1)
 +
 +static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
  {
 -      return !memcmp(sha1, null_sha1, 20);
 +      int i;
 +
 +      for (i = 0; i < 20; i++, sha1++, sha2++) {
 +              if (*sha1 != *sha2)
 +                      return *sha1 - *sha2;
 +      }
 +
 +      return 0;
  }
 -static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
 +
 +static inline int is_null_sha1(const unsigned char *sha1)
  {
 -      return memcmp(sha1, sha2, 20);
 +      return !hashcmp(sha1, null_sha1);
  }
 +
  static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
  {
        memcpy(sha_dst, sha_src, 20);
@@@ -739,7 -725,6 +739,7 @@@ int set_shared_perm(const char *path, i
  #define adjust_shared_perm(path) set_shared_perm((path), 0)
  int safe_create_leading_directories(char *path);
  int safe_create_leading_directories_const(const char *path);
 +int mkdir_in_gitdir(const char *path);
  extern char *expand_user_path(const char *path);
  char *enter_repo(char *path, int strict);
  static inline int is_absolute_path(const char *path)
@@@ -756,23 -741,13 +756,23 @@@ char *strip_path_suffix(const char *pat
  int daemon_avoid_alias(const char *path);
  int offset_1st_component(const char *path);
  
 -/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 -extern int sha1_object_info(const unsigned char *, unsigned long *);
 -extern void *read_sha1_file_repl(const unsigned char *sha1, enum object_type *type, unsigned long *size, const unsigned char **replacement);
 +/* object replacement */
 +#define READ_SHA1_FILE_REPLACE 1
 +extern void *read_sha1_file_extended(const unsigned char *sha1, enum object_type *type, unsigned long *size, unsigned flag);
  static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
  {
 -      return read_sha1_file_repl(sha1, type, size, NULL);
 +      return read_sha1_file_extended(sha1, type, size, READ_SHA1_FILE_REPLACE);
 +}
 +extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1);
 +static inline const unsigned char *lookup_replace_object(const unsigned char *sha1)
 +{
 +      if (!read_replace_refs)
 +              return sha1;
 +      return do_lookup_replace_object(sha1);
  }
 +
 +/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 +extern int sha1_object_info(const unsigned char *, unsigned long *);
  extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
  extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
@@@ -800,8 -775,8 +800,8 @@@ static inline unsigned int hexval(unsig
  }
  
  /* Convert to/from hex/sha1 representation */
 -#define MINIMUM_ABBREV 4
 -#define DEFAULT_ABBREV 7
 +#define MINIMUM_ABBREV minimum_abbrev
 +#define DEFAULT_ABBREV default_abbrev
  
  struct object_context {
        unsigned char tree[20];
  };
  
  extern int get_sha1(const char *str, unsigned char *sha1);
- extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, const char *prefix);
+ extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int only_to_die, const char *prefix);
  static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
  {
-       return get_sha1_with_mode_1(str, sha1, mode, 1, NULL);
+       return get_sha1_with_mode_1(str, sha1, mode, 0, NULL);
  }
- extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int gently, const char *prefix);
+ extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int only_to_die, const char *prefix);
  static inline int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc)
  {
-       return get_sha1_with_context_1(str, sha1, orc, 1, NULL);
+       return get_sha1_with_context_1(str, sha1, orc, 0, NULL);
  }
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
@@@ -985,7 -960,6 +985,7 @@@ extern struct ref *find_ref_by_name(con
  extern char *git_getpass(const char *prompt);
  extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
  extern int finish_connect(struct child_process *conn);
 +extern int git_connection_is_socket(struct child_process *conn);
  extern int path_match(const char *path, int nr, char **match);
  struct extra_have_objects {
        int nr, alloc;
@@@ -1048,6 -1022,7 +1048,6 @@@ extern const char *git_etc_gitconfig(vo
  extern int check_repository_format_version(const char *var, const char *value, void *cb);
  extern int git_env_bool(const char *, int);
  extern int git_config_system(void);
 -extern int git_config_global(void);
  extern int config_error_nonbool(const char *);
  extern const char *get_log_output_encoding(void);
  extern const char *get_commit_output_encoding(void);
diff --combined git-compat-util.h
index 40498b33c9f09ef9cac1f7340a9a1ceb2ffcd50d,d88cf8afb67f1140164a640d17a28b0a3e67599a..e0bb81ed8d0bd89f18b31b1c03d3e23744aea5a1
  #endif
  #ifndef __MINGW32__
  #include <sys/wait.h>
 +#include <sys/resource.h>
  #include <sys/socket.h>
  #include <sys/ioctl.h>
  #include <termios.h>
@@@ -463,6 -462,7 +463,7 @@@ extern unsigned char sane_ctype[256]
  #define GIT_ALPHA 0x04
  #define GIT_GLOB_SPECIAL 0x08
  #define GIT_REGEX_SPECIAL 0x10
+ #define GIT_PATHSPEC_MAGIC 0x20
  #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
  #define isascii(x) (((x) & ~0x7f) == 0)
  #define isspace(x) sane_istest(x,GIT_SPACE)
  #define is_regex_special(x) sane_istest(x,GIT_GLOB_SPECIAL | GIT_REGEX_SPECIAL)
  #define tolower(x) sane_case((unsigned char)(x), 0x20)
  #define toupper(x) sane_case((unsigned char)(x), 0)
+ #define is_pathspec_magic(x) sane_istest(x,GIT_PATHSPEC_MAGIC)
  
  static inline int sane_case(int x, int high)
  {
diff --combined revision.c
index cf0b001570838af00b79b5c305dc9e5da65d5653,17f9fcb9664c76c97efa6daed7a63e6c2e3b6036..7934b2f68bbaf9327ff3957aaa3eb316c2a7fb05
@@@ -535,7 -535,6 +535,7 @@@ static void cherry_pick_list(struct com
        int left_count = 0, right_count = 0;
        int left_first;
        struct patch_ids ids;
 +      unsigned cherry_flag;
  
        /* First count the commits on the left and on the right */
        for (p = list; p; p = p->next) {
                commit->util = add_commit_patch_id(commit, &ids);
        }
  
 +      /* either cherry_mark or cherry_pick are true */
 +      cherry_flag = revs->cherry_mark ? PATCHSAME : SHOWN;
 +
        /* Check the other side */
        for (p = list; p; p = p->next) {
                struct commit *commit = p->item;
                if (!id)
                        continue;
                id->seen = 1;
 -              commit->object.flags |= SHOWN;
 +              commit->object.flags |= cherry_flag;
        }
  
        /* Now check the original side for seen ones */
                if (!ent)
                        continue;
                if (ent->seen)
 -                      commit->object.flags |= SHOWN;
 +                      commit->object.flags |= cherry_flag;
                commit->util = NULL;
        }
  
@@@ -733,23 -729,6 +733,23 @@@ static struct commit_list *collect_bott
        return bottom;
  }
  
 +/* Assumes either left_only or right_only is set */
 +static void limit_left_right(struct commit_list *list, struct rev_info *revs)
 +{
 +      struct commit_list *p;
 +
 +      for (p = list; p; p = p->next) {
 +              struct commit *commit = p->item;
 +
 +              if (revs->right_only) {
 +                      if (commit->object.flags & SYMMETRIC_LEFT)
 +                              commit->object.flags |= SHOWN;
 +              } else  /* revs->left_only is set */
 +                      if (!(commit->object.flags & SYMMETRIC_LEFT))
 +                              commit->object.flags |= SHOWN;
 +      }
 +}
 +
  static int limit_list(struct rev_info *revs)
  {
        int slop = SLOP;
                show(revs, newlist);
                show_early_output = NULL;
        }
 -      if (revs->cherry_pick)
 +      if (revs->cherry_pick || revs->cherry_mark)
                cherry_pick_list(newlist, revs);
  
 +      if (revs->left_only || revs->right_only)
 +              limit_left_right(newlist, revs);
 +
        if (bottom) {
                limit_to_ancestry(bottom, newlist);
                free_commit_list(bottom);
@@@ -941,7 -917,6 +941,7 @@@ void init_revisions(struct rev_info *re
        revs->min_age = -1;
        revs->skip_count = -1;
        revs->max_count = -1;
 +      revs->max_parents = -1;
  
        revs->commit_format = CMIT_FMT_DEFAULT;
  
                revs->diffopt.prefix = prefix;
                revs->diffopt.prefix_length = strlen(prefix);
        }
 +
 +      revs->notes_opt.use_default_notes = -1;
  }
  
  static void add_pending_commit_list(struct rev_info *revs,
@@@ -1179,9 -1152,7 +1179,9 @@@ static int handle_revision_opt(struct r
            !strcmp(arg, "--tags") || !strcmp(arg, "--remotes") ||
            !strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
            !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
 -          !strcmp(arg, "--bisect"))
 +          !strcmp(arg, "--bisect") || !prefixcmp(arg, "--glob=") ||
 +          !prefixcmp(arg, "--branches=") || !prefixcmp(arg, "--tags=") ||
 +          !prefixcmp(arg, "--remotes="))
        {
                unkv[(*unkc)++] = arg;
                return 1;
        } else if (!strcmp(arg, "--remove-empty")) {
                revs->remove_empty_trees = 1;
        } else if (!strcmp(arg, "--merges")) {
 -              revs->merges_only = 1;
 +              revs->min_parents = 2;
        } else if (!strcmp(arg, "--no-merges")) {
 -              revs->no_merges = 1;
 +              revs->max_parents = 1;
 +      } else if (!prefixcmp(arg, "--min-parents=")) {
 +              revs->min_parents = atoi(arg+14);
 +      } else if (!prefixcmp(arg, "--no-min-parents")) {
 +              revs->min_parents = 0;
 +      } else if (!prefixcmp(arg, "--max-parents=")) {
 +              revs->max_parents = atoi(arg+14);
 +      } else if (!prefixcmp(arg, "--no-max-parents")) {
 +              revs->max_parents = -1;
        } else if (!strcmp(arg, "--boundary")) {
                revs->boundary = 1;
        } else if (!strcmp(arg, "--left-right")) {
                revs->left_right = 1;
 +      } else if (!strcmp(arg, "--left-only")) {
 +              if (revs->right_only)
 +                      die("--left-only is incompatible with --right-only"
 +                          " or --cherry");
 +              revs->left_only = 1;
 +      } else if (!strcmp(arg, "--right-only")) {
 +              if (revs->left_only)
 +                      die("--right-only is incompatible with --left-only");
 +              revs->right_only = 1;
 +      } else if (!strcmp(arg, "--cherry")) {
 +              if (revs->left_only)
 +                      die("--cherry is incompatible with --left-only");
 +              revs->cherry_mark = 1;
 +              revs->right_only = 1;
 +              revs->max_parents = 1;
 +              revs->limited = 1;
        } else if (!strcmp(arg, "--count")) {
                revs->count = 1;
 +      } else if (!strcmp(arg, "--cherry-mark")) {
 +              if (revs->cherry_pick)
 +                      die("--cherry-mark is incompatible with --cherry-pick");
 +              revs->cherry_mark = 1;
 +              revs->limited = 1; /* needs limit_list() */
        } else if (!strcmp(arg, "--cherry-pick")) {
 +              if (revs->cherry_mark)
 +                      die("--cherry-pick is incompatible with --cherry-mark");
                revs->cherry_pick = 1;
                revs->limited = 1;
        } else if (!strcmp(arg, "--objects")) {
                revs->verbose_header = 1;
                revs->pretty_given = 1;
                get_commit_format(arg+9, revs);
 -      } else if (!strcmp(arg, "--show-notes")) {
 +      } else if (!strcmp(arg, "--show-notes") || !strcmp(arg, "--notes")) {
                revs->show_notes = 1;
                revs->show_notes_given = 1;
 -      } else if (!prefixcmp(arg, "--show-notes=")) {
 +              revs->notes_opt.use_default_notes = 1;
 +      } else if (!prefixcmp(arg, "--show-notes=") ||
 +                 !prefixcmp(arg, "--notes=")) {
                struct strbuf buf = STRBUF_INIT;
                revs->show_notes = 1;
                revs->show_notes_given = 1;
 -              if (!revs->notes_opt.extra_notes_refs)
 -                      revs->notes_opt.extra_notes_refs = xcalloc(1, sizeof(struct string_list));
 -              if (!prefixcmp(arg+13, "refs/"))
 -                      /* happy */;
 -              else if (!prefixcmp(arg+13, "notes/"))
 -                      strbuf_addstr(&buf, "refs/");
 +              if (!prefixcmp(arg, "--show-notes")) {
 +                      if (revs->notes_opt.use_default_notes < 0)
 +                              revs->notes_opt.use_default_notes = 1;
 +                      strbuf_addstr(&buf, arg+13);
 +              }
                else
 -                      strbuf_addstr(&buf, "refs/notes/");
 -              strbuf_addstr(&buf, arg+13);
 -              string_list_append(revs->notes_opt.extra_notes_refs,
 +                      strbuf_addstr(&buf, arg+8);
 +              expand_notes_ref(&buf);
 +              string_list_append(&revs->notes_opt.extra_notes_refs,
                                   strbuf_detach(&buf, NULL));
        } else if (!strcmp(arg, "--no-notes")) {
                revs->show_notes = 0;
                revs->show_notes_given = 1;
 +              revs->notes_opt.use_default_notes = -1;
 +              /* we have been strdup'ing ourselves, so trick
 +               * string_list into free()ing strings */
 +              revs->notes_opt.extra_notes_refs.strdup_strings = 1;
 +              string_list_clear(&revs->notes_opt.extra_notes_refs, 0);
 +              revs->notes_opt.extra_notes_refs.strdup_strings = 0;
        } else if (!strcmp(arg, "--standard-notes")) {
                revs->show_notes_given = 1;
 -              revs->notes_opt.suppress_default_notes = 0;
 +              revs->notes_opt.use_default_notes = 1;
        } else if (!strcmp(arg, "--no-standard-notes")) {
 -              revs->notes_opt.suppress_default_notes = 1;
 +              revs->notes_opt.use_default_notes = 0;
        } else if (!strcmp(arg, "--oneline")) {
                revs->verbose_header = 1;
                get_commit_format("oneline", revs);
@@@ -1508,69 -1441,6 +1508,69 @@@ static int for_each_good_bisect_ref(con
        return for_each_ref_in_submodule(submodule, "refs/bisect/good", fn, cb_data);
  }
  
 +static int handle_revision_pseudo_opt(const char *submodule,
 +                              struct rev_info *revs,
 +                              int argc, const char **argv, int *flags)
 +{
 +      const char *arg = argv[0];
 +      const char *optarg;
 +      int argcount;
 +
 +      /*
 +       * NOTE!
 +       *
 +       * Commands like "git shortlog" will not accept the options below
 +       * unless parse_revision_opt queues them (as opposed to erroring
 +       * out).
 +       *
 +       * When implementing your new pseudo-option, remember to
 +       * register it in the list at the top of handle_revision_opt.
 +       */
 +      if (!strcmp(arg, "--all")) {
 +              handle_refs(submodule, revs, *flags, for_each_ref_submodule);
 +              handle_refs(submodule, revs, *flags, head_ref_submodule);
 +      } else if (!strcmp(arg, "--branches")) {
 +              handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);
 +      } else if (!strcmp(arg, "--bisect")) {
 +              handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
 +              handle_refs(submodule, revs, *flags ^ UNINTERESTING, for_each_good_bisect_ref);
 +              revs->bisect = 1;
 +      } else if (!strcmp(arg, "--tags")) {
 +              handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);
 +      } else if (!strcmp(arg, "--remotes")) {
 +              handle_refs(submodule, revs, *flags, for_each_remote_ref_submodule);
 +      } else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
 +              struct all_refs_cb cb;
 +              init_all_refs_cb(&cb, revs, *flags);
 +              for_each_glob_ref(handle_one_ref, optarg, &cb);
 +              return argcount;
 +      } else if (!prefixcmp(arg, "--branches=")) {
 +              struct all_refs_cb cb;
 +              init_all_refs_cb(&cb, revs, *flags);
 +              for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
 +      } else if (!prefixcmp(arg, "--tags=")) {
 +              struct all_refs_cb cb;
 +              init_all_refs_cb(&cb, revs, *flags);
 +              for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
 +      } else if (!prefixcmp(arg, "--remotes=")) {
 +              struct all_refs_cb cb;
 +              init_all_refs_cb(&cb, revs, *flags);
 +              for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
 +      } else if (!strcmp(arg, "--reflog")) {
 +              handle_reflog(revs, *flags);
 +      } else if (!strcmp(arg, "--not")) {
 +              *flags ^= UNINTERESTING;
 +      } else if (!strcmp(arg, "--no-walk")) {
 +              revs->no_walk = 1;
 +      } else if (!strcmp(arg, "--do-walk")) {
 +              revs->no_walk = 0;
 +      } else {
 +              return 0;
 +      }
 +
 +      return 1;
 +}
 +
  /*
   * Parse revision information, filling in the "rev_info" structure,
   * and removing the used arguments from the argument list.
@@@ -1583,6 -1453,8 +1583,6 @@@ int setup_revisions(int argc, const cha
        int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
        struct cmdline_pathspec prune_data;
        const char *submodule = NULL;
 -      const char *optarg;
 -      int argcount;
  
        memset(&prune_data, 0, sizeof(prune_data));
        if (opt)
                if (*arg == '-') {
                        int opts;
  
 -                      if (!strcmp(arg, "--all")) {
 -                              handle_refs(submodule, revs, flags, for_each_ref_submodule);
 -                              handle_refs(submodule, revs, flags, head_ref_submodule);
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--branches")) {
 -                              handle_refs(submodule, revs, flags, for_each_branch_ref_submodule);
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--bisect")) {
 -                              handle_refs(submodule, revs, flags, for_each_bad_bisect_ref);
 -                              handle_refs(submodule, revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
 -                              revs->bisect = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--tags")) {
 -                              handle_refs(submodule, revs, flags, for_each_tag_ref_submodule);
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--remotes")) {
 -                              handle_refs(submodule, revs, flags, for_each_remote_ref_submodule);
 -                              continue;
 -                      }
 -                      if ((argcount = parse_long_opt("glob", argv + i, &optarg))) {
 -                              struct all_refs_cb cb;
 -                              i += argcount - 1;
 -                              init_all_refs_cb(&cb, revs, flags);
 -                              for_each_glob_ref(handle_one_ref, optarg, &cb);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--branches=")) {
 -                              struct all_refs_cb cb;
 -                              init_all_refs_cb(&cb, revs, flags);
 -                              for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--tags=")) {
 -                              struct all_refs_cb cb;
 -                              init_all_refs_cb(&cb, revs, flags);
 -                              for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
 -                              continue;
 -                      }
 -                      if (!prefixcmp(arg, "--remotes=")) {
 -                              struct all_refs_cb cb;
 -                              init_all_refs_cb(&cb, revs, flags);
 -                              for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--reflog")) {
 -                              handle_reflog(revs, flags);
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--not")) {
 -                              flags ^= UNINTERESTING;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--no-walk")) {
 -                              revs->no_walk = 1;
 -                              continue;
 -                      }
 -                      if (!strcmp(arg, "--do-walk")) {
 -                              revs->no_walk = 0;
 +                      opts = handle_revision_pseudo_opt(submodule,
 +                                              revs, argc - i, argv + i,
 +                                              &flags);
 +                      if (opts > 0) {
 +                              i += opts - 1;
                                continue;
                        }
 +
                        if (!strcmp(arg, "--stdin")) {
                                if (revs->disable_stdin) {
                                        argv[left++] = arg;
        }
  
        if (prune_data.nr) {
+               /*
+                * If we need to introduce the magic "a lone ':' means no
+                * pathspec whatsoever", here is the place to do so.
+                *
+                * if (prune_data.nr == 1 && !strcmp(prune_data[0], ":")) {
+                *      prune_data.nr = 0;
+                *      prune_data.alloc = 0;
+                *      free(prune_data.path);
+                *      prune_data.path = NULL;
+                * } else {
+                *      terminate prune_data.alloc with NULL and
+                *      call init_pathspec() to set revs->prune_data here.
+                * }
+                */
                ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc);
                prune_data.path[prune_data.nr++] = NULL;
                init_pathspec(&revs->prune_data,
@@@ -2030,15 -1972,10 +2044,15 @@@ enum commit_action get_commit_action(st
                return commit_ignore;
        if (revs->min_age != -1 && (commit->date > revs->min_age))
                return commit_ignore;
 -      if (revs->no_merges && commit->parents && commit->parents->next)
 -              return commit_ignore;
 -      if (revs->merges_only && !(commit->parents && commit->parents->next))
 -              return commit_ignore;
 +      if (revs->min_parents || (revs->max_parents >= 0)) {
 +              int n = 0;
 +              struct commit_list *p;
 +              for (p = commit->parents; p; p = p->next)
 +                      n++;
 +              if ((n < revs->min_parents) ||
 +                  ((revs->max_parents >= 0) && (n > revs->max_parents)))
 +                      return commit_ignore;
 +      }
        if (!commit_match(commit, revs))
                return commit_ignore;
        if (revs->prune && revs->dense) {
@@@ -2285,32 -2222,3 +2299,32 @@@ struct commit *get_revision(struct rev_
                graph_update(revs->graph, c);
        return c;
  }
 +
 +char *get_revision_mark(const struct rev_info *revs, const struct commit *commit)
 +{
 +      if (commit->object.flags & BOUNDARY)
 +              return "-";
 +      else if (commit->object.flags & UNINTERESTING)
 +              return "^";
 +      else if (commit->object.flags & PATCHSAME)
 +              return "=";
 +      else if (!revs || revs->left_right) {
 +              if (commit->object.flags & SYMMETRIC_LEFT)
 +                      return "<";
 +              else
 +                      return ">";
 +      } else if (revs->graph)
 +              return "*";
 +      else if (revs->cherry_mark)
 +              return "+";
 +      return "";
 +}
 +
 +void put_revision_mark(const struct rev_info *revs, const struct commit *commit)
 +{
 +      char *mark = get_revision_mark(revs, commit);
 +      if (!strlen(mark))
 +              return;
 +      fputs(mark, stdout);
 +      putchar(' ');
 +}
diff --combined setup.c
index b6e6b5ae272b3ecae7dfa2cb446aef97e826884a,d7d8e3efbcbb77810dffac2a57fae7e4d5aa7f11..013ad1127534367e64afeec3749e617ceccfb5d4
+++ b/setup.c
@@@ -85,8 -85,17 +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);
@@@ -126,6 -135,105 +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;
@@@ -325,7 -432,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 sha1_name.c
index 69cd6c815d6bb43fdeda9c4b28fc138bed17c057,ec836117d9b0a33f798b272584ec2908c938a49e..ff5992acc971ac5a67fa3d4def1e0c064b90519f
@@@ -1012,13 -1012,11 +1012,13 @@@ static void diagnose_invalid_sha1_path(
                if (!get_tree_entry(tree_sha1, fullname,
                                    sha1, &mode)) {
                        die("Path '%s' exists, but not '%s'.\n"
 -                          "Did you mean '%s:%s'?",
 +                          "Did you mean '%s:%s' aka '%s:./%s'?",
                            fullname,
                            filename,
                            object_name,
 -                          fullname);
 +                          fullname,
 +                          object_name,
 +                          filename);
                }
                die("Path '%s' does not exist in '%s'",
                    filename, object_name);
@@@ -1067,10 -1065,9 +1067,10 @@@ static void diagnose_invalid_index_path
                if (ce_namelen(ce) == fullnamelen &&
                    !memcmp(ce->name, fullname, fullnamelen))
                        die("Path '%s' is in the index, but not '%s'.\n"
 -                          "Did you mean ':%d:%s'?",
 +                          "Did you mean ':%d:%s' aka ':%d:./%s'?",
                            fullname, filename,
 -                          ce_stage(ce), fullname);
 +                          ce_stage(ce), fullname,
 +                          ce_stage(ce), filename);
        }
  
        if (!lstat(filename, &st))
  }
  
  
- int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode, int gently, const char *prefix)
+ int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,
+                        int only_to_die, const char *prefix)
  {
        struct object_context oc;
        int ret;
-       ret = get_sha1_with_context_1(name, sha1, &oc, gently, prefix);
+       ret = get_sha1_with_context_1(name, sha1, &oc, only_to_die, prefix);
        *mode = oc.mode;
        return ret;
  }
@@@ -1111,7 -1109,7 +1112,7 @@@ static char *resolve_relative_path(cons
  
  int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                            struct object_context *oc,
-                           int gently, const char *prefix)
+                           int only_to_die, const char *prefix)
  {
        int ret, bracket_depth;
        int namelen = strlen(name);
                struct cache_entry *ce;
                char *new_path = NULL;
                int pos;
-               if (namelen > 2 && name[1] == '/') {
+               if (!only_to_die && namelen > 2 && name[1] == '/') {
                        struct commit_list *list = NULL;
                        for_each_ref(handle_one_ref, &list);
                        return get_sha1_oneline(name + 2, sha1, list);
                        }
                        pos++;
                }
-               if (!gently)
+               if (only_to_die && name[1] && name[1] != '/')
                        diagnose_invalid_index_path(stage, prefix, cp);
                free(new_path);
                return -1;
        if (*cp == ':') {
                unsigned char tree_sha1[20];
                char *object_name = NULL;
-               if (!gently) {
+               if (only_to_die) {
                        object_name = xmalloc(cp-name+1);
                        strncpy(object_name, name, cp-name);
                        object_name[cp-name] = '\0';
                        if (new_filename)
                                filename = new_filename;
                        ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
-                       if (!gently) {
+                       if (only_to_die) {
                                diagnose_invalid_sha1_path(prefix, filename,
                                                           tree_sha1, object_name);
                                free(object_name);
                        free(new_filename);
                        return ret;
                } else {
-                       if (!gently)
+                       if (only_to_die)
                                die("Invalid object name '%s'.", object_name);
                }
        }