Merge branch 'jc/maint-reflog-bad-timestamp'
authorJunio C Hamano <gitster@pobox.com>
Wed, 27 Jan 2010 22:57:37 +0000 (14:57 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 27 Jan 2010 22:57:37 +0000 (14:57 -0800)
* jc/maint-reflog-bad-timestamp:
t0101: use a fixed timestamp when searching in the reflog
Update @{bogus.timestamp} fix not to die()
approxidate_careful() reports errorneous date string

1  2 
cache.h
date.c
sha1_name.c
diff --combined cache.h
index b3370eb41e500b59bdf9d03679e7c17dde59e911,0f163dbbe11462be9ed96b65371e3e07a31bc4a3..d478eff1f323f25a474cf019e0de2254c5ff0360
+++ b/cache.h
@@@ -177,22 -177,15 +177,22 @@@ struct cache_entry 
  
  #define CE_HASHED    (0x100000)
  #define CE_UNHASHED  (0x200000)
 +#define CE_CONFLICTED (0x800000)
 +
 +/* Only remove in work directory, not index */
 +#define CE_WT_REMOVE (0x400000)
 +
 +#define CE_UNPACKED  (0x1000000)
  
  /*
   * Extended on-disk flags
   */
  #define CE_INTENT_TO_ADD 0x20000000
 +#define CE_SKIP_WORKTREE 0x40000000
  /* CE_EXTENDED2 is for future extension */
  #define CE_EXTENDED2 0x80000000
  
 -#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD)
 +#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
  
  /*
   * Safeguard to avoid saving wrong flags:
@@@ -241,7 -234,6 +241,7 @@@ static inline size_t ce_namelen(const s
                            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)
  #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
  
  #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
@@@ -290,7 -282,6 +290,7 @@@ static inline int ce_to_dtype(const str
  struct index_state {
        struct cache_entry **cache;
        unsigned int cache_nr, cache_alloc, cache_changed;
 +      struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
        struct cache_time timestamp;
        void *alloc;
@@@ -345,9 -336,6 +345,9 @@@ static inline void remove_name_hash(str
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
  #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
  #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
 +#define resolve_undo_clear() resolve_undo_clear_index(&the_index)
 +#define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
 +#define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)
  #endif
  
  enum object_type {
@@@ -381,12 -369,9 +381,12 @@@ static inline enum object_type object_t
  #define CONFIG_ENVIRONMENT "GIT_CONFIG"
  #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
  #define CEILING_DIRECTORIES_ENVIRONMENT "GIT_CEILING_DIRECTORIES"
 +#define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS"
  #define GITATTRIBUTES_FILE ".gitattributes"
  #define INFOATTRIBUTES_FILE "info/attributes"
  #define ATTRIBUTE_MACRO_PREFIX "[attr]"
 +#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
 +#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
  
  extern int is_bare_repository_cfg;
  extern int is_bare_repository(void);
@@@ -411,7 -396,6 +411,7 @@@ extern const char *setup_git_directory_
  extern const char *setup_git_directory(void);
  extern const 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_non_filename(const char *prefix, const char *name);
  
@@@ -457,6 -441,7 +457,6 @@@ extern int index_name_pos(const struct 
  #define ADD_CACHE_JUST_APPEND 8               /* Append only; tree.c::read_tree() */
  #define ADD_CACHE_NEW_ONLY 16         /* Do not replace existing ones */
  extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
 -extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
  extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
  extern int remove_index_entry_at(struct index_state *, int pos);
  extern void remove_marked_cache_entries(struct index_state *istate);
@@@ -475,9 -460,7 +475,9 @@@ extern int index_name_is_other(const st
  /* do stat comparison even if CE_VALID is true */
  #define CE_MATCH_IGNORE_VALID         01
  /* do not check the contents but report dirty on racily-clean entries */
 -#define CE_MATCH_RACY_IS_DIRTY        02
 +#define CE_MATCH_RACY_IS_DIRTY                02
 +/* do stat comparison even if CE_SKIP_WORKTREE is true */
 +#define CE_MATCH_IGNORE_SKIP_WORKTREE 04
  extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  
@@@ -486,6 -469,9 +486,6 @@@ extern int index_fd(unsigned char *sha1
  extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
  extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
  
 -/* "careful lstat()" */
 -extern int check_path(const char *path, int len, struct stat *st, int skiplen);
 -
  #define REFRESH_REALLY                0x0001  /* ignore_valid */
  #define REFRESH_UNMERGED      0x0002  /* allow unmerged */
  #define REFRESH_QUIET         0x0004  /* be quiet about it */
@@@ -539,7 -525,6 +539,7 @@@ extern int auto_crlf
  extern int read_replace_refs;
  extern int fsync_object_files;
  extern int core_preload_index;
 +extern int core_apply_sparse_checkout;
  
  enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
@@@ -555,7 -540,6 +555,7 @@@ enum branch_track 
        BRANCH_TRACK_REMOTE,
        BRANCH_TRACK_ALWAYS,
        BRANCH_TRACK_EXPLICIT,
 +      BRANCH_TRACK_OVERRIDE,
  };
  
  enum rebase_setup_type {
@@@ -583,8 -567,6 +583,8 @@@ enum object_creation_mode 
  
  extern enum object_creation_mode object_creation_mode;
  
 +extern char *notes_ref_name;
 +
  extern int grafts_replace_parents;
  
  #define GIT_REPO_VERSION 0
@@@ -630,6 -612,7 +630,6 @@@ static inline void hashclr(unsigned cha
  {
        memset(hash, 0, 20);
  }
 -extern int is_empty_blob_sha1(const unsigned char *sha1);
  
  #define EMPTY_TREE_SHA1_HEX \
        "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
@@@ -674,7 -657,6 +674,7 @@@ const char *make_relative_path(const ch
  int normalize_path_copy(char *dst, const char *src);
  int longest_ancestor_length(const char *path, const char *prefix_list);
  char *strip_path_suffix(const char *path, const char *suffix);
 +int daemon_avoid_alias(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 *);
@@@ -699,6 -681,7 +699,6 @@@ extern int has_sha1_pack(const unsigne
  extern int has_sha1_file(const unsigned char *sha1);
  extern int has_loose_object_nonlocal(const unsigned char *sha1);
  
 -extern int has_pack_file(const unsigned char *sha1);
  extern int has_pack_index(const unsigned char *sha1);
  
  extern const signed char hexval_table[256];
@@@ -712,11 -695,7 +712,11 @@@ static inline unsigned int hexval(unsig
  #define DEFAULT_ABBREV 7
  
  extern int get_sha1(const char *str, unsigned char *sha1);
 -extern int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode);
 +extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, 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);
 +}
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
  extern int read_ref(const char *filename, unsigned char *sha1);
@@@ -724,7 -703,6 +724,7 @@@ extern const char *resolve_ref(const ch
  extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
  extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
  extern int interpret_branch_name(const char *str, struct strbuf *);
 +extern int get_sha1_mb(const char *str, unsigned char *sha1);
  
  extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
  extern const char *ref_rev_parse_rules[];
@@@ -762,7 -740,8 +762,8 @@@ const char *show_date_relative(unsigne
                               size_t timebuf_size);
  int parse_date(const char *date, char *buf, int bufsize);
  void datestamp(char *buf, int bufsize);
- unsigned long approxidate(const char *);
+ #define approxidate(s) approxidate_careful((s), NULL)
+ unsigned long approxidate_careful(const char *, int *);
  unsigned long approxidate_relative(const char *date, const struct timeval *now);
  enum date_mode parse_date_format(const char *format);
  
@@@ -773,8 -752,6 +774,8 @@@ 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 *git_editor(void);
 +extern const char *git_pager(void);
  
  struct checkout {
        const char *base_dir;
@@@ -799,6 -776,8 +800,6 @@@ extern int has_symlink_leading_path(con
  extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
  extern int has_symlink_or_noent_leading_path(const char *name, int len);
  extern int has_dirs_only_path(const char *name, int len, int prefix_len);
 -extern void invalidate_lstat_cache(const char *name, int len);
 -extern void clear_lstat_cache(void);
  extern void schedule_dir_for_removal(const char *name, int len);
  extern void remove_scheduled_dirs(void);
  
@@@ -879,6 -858,7 +880,6 @@@ extern struct ref *find_ref_by_name(con
  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 path_match(const char *path, int nr, char **match);
 -extern int get_ack(int fd, unsigned char *result_sha1);
  struct extra_have_objects {
        int nr, alloc;
        unsigned char (*array)[20];
@@@ -938,11 -918,7 +939,11 @@@ extern const char *config_exclusive_fil
  #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)
  extern int user_ident_explicitly_given;
 +extern int user_ident_sufficiently_given(void);
  
  extern const char *git_commit_encoding;
  extern const char *git_log_output_encoding;
@@@ -987,9 -963,7 +988,9 @@@ extern void *alloc_object_node(void)
  extern void alloc_report(void);
  
  /* trace.c */
 +__attribute__((format (printf, 1, 2)))
  extern void trace_printf(const char *format, ...);
 +__attribute__((format (printf, 2, 3)))
  extern void trace_argv_printf(const char **argv, const char *format, ...);
  
  /* convert.c */
@@@ -1010,7 -984,6 +1011,7 @@@ extern int diff_auto_refresh_index
  
  /* match-trees.c */
  void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
 +void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *, const char *);
  
  /*
   * whitespace rules.
diff --combined date.c
index 45f3684ceee465170a520ecf5f25dcaa0e5ed84e,17385a9b62abf17868403b0df40155c19efa3515..002aa3c8d6d4ff08d8790a155b8979bc117a2b95
--- 1/date.c
--- 2/date.c
+++ b/date.c
@@@ -9,7 -9,7 +9,7 @@@
  /*
   * This is like mktime, but without normalization of tm_wday and tm_yday.
   */
 -time_t tm_to_time_t(const struct tm *tm)
 +static time_t tm_to_time_t(const struct tm *tm)
  {
        static const int mdays[] = {
            0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
@@@ -696,6 -696,11 +696,11 @@@ static unsigned long update_tm(struct t
        return n;
  }
  
+ static void date_now(struct tm *tm, struct tm *now, int *num)
+ {
+       update_tm(tm, now, 0);
+ }
  static void date_yesterday(struct tm *tm, struct tm *now, int *num)
  {
        update_tm(tm, now, 24*60*60);
@@@ -770,6 -775,7 +775,7 @@@ static const struct special 
        { "PM", date_pm },
        { "AM", date_am },
        { "never", date_never },
+       { "now", date_now },
        { NULL }
  };
  
@@@ -790,7 -796,7 +796,7 @@@ static const struct typelen 
        { NULL }
  };
  
- static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num)
+ static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num, int *touched)
  {
        const struct typelen *tl;
        const struct special *s;
                int match = match_string(date, month_names[i]);
                if (match >= 3) {
                        tm->tm_mon = i;
+                       *touched = 1;
                        return end;
                }
        }
                int len = strlen(s->name);
                if (match_string(date, s->name) == len) {
                        s->fn(tm, now, num);
+                       *touched = 1;
                        return end;
                }
        }
                        int len = strlen(number_name[i]);
                        if (match_string(date, number_name[i]) == len) {
                                *num = i;
+                               *touched = 1;
                                return end;
                        }
                }
-               if (match_string(date, "last") == 4)
+               if (match_string(date, "last") == 4) {
                        *num = 1;
+                       *touched = 1;
+               }
                return end;
        }
  
                if (match_string(date, tl->type) >= len-1) {
                        update_tm(tm, now, tl->length * *num);
                        *num = 0;
+                       *touched = 1;
                        return end;
                }
                tl++;
                        diff += 7*n;
  
                        update_tm(tm, now, diff * 24 * 60 * 60);
+                       *touched = 1;
                        return end;
                }
        }
                        tm->tm_year--;
                }
                tm->tm_mon = n;
+               *touched = 1;
                return end;
        }
  
                update_tm(tm, now, 0); /* fill in date fields if needed */
                tm->tm_year -= *num;
                *num = 0;
+               *touched = 1;
                return end;
        }
  
@@@ -929,9 -944,12 +944,12 @@@ static void pending_number(struct tm *t
        }
  }
  
- static unsigned long approxidate_str(const char *date, const struct timeval *tv)
+ static unsigned long approxidate_str(const char *date,
+                                    const struct timeval *tv,
+                                    int *error_ret)
  {
        int number = 0;
+       int touched = 0;
        struct tm tm, now;
        time_t time_sec;
  
                if (isdigit(c)) {
                        pending_number(&tm, &number);
                        date = approxidate_digit(date-1, &tm, &number);
+                       touched = 1;
                        continue;
                }
                if (isalpha(c))
-                       date = approxidate_alpha(date-1, &tm, &now, &number);
+                       date = approxidate_alpha(date-1, &tm, &now, &number, &touched);
        }
        pending_number(&tm, &number);
+       if (!touched)
+               *error_ret = 1;
        return update_tm(&tm, &now, 0);
  }
  
  unsigned long approxidate_relative(const char *date, const struct timeval *tv)
  {
        char buffer[50];
+       int errors = 0;
  
        if (parse_date(date, buffer, sizeof(buffer)) > 0)
                return strtoul(buffer, NULL, 0);
  
-       return approxidate_str(date, tv);
+       return approxidate_str(date, tv, &errors);
  }
  
- unsigned long approxidate(const char *date)
+ unsigned long approxidate_careful(const char *date, int *error_ret)
  {
        struct timeval tv;
        char buffer[50];
+       int dummy = 0;
+       if (!error_ret)
+               error_ret = &dummy;
  
-       if (parse_date(date, buffer, sizeof(buffer)) > 0)
+       if (parse_date(date, buffer, sizeof(buffer)) > 0) {
+               *error_ret = 0;
                return strtoul(buffer, NULL, 0);
+       }
  
        gettimeofday(&tv, NULL);
-       return approxidate_str(date, &tv);
+       return approxidate_str(date, &tv, error_ret);
  }
diff --combined sha1_name.c
index 9215ad1d050c427b67124f4c5ca76e60e8e022f0,04fb3b8fed2970bf7fde83e0dc38cea9485ca3d2..c7f1510ef102512f1270a064fbb7a842b1d9aed9
@@@ -5,7 -5,6 +5,7 @@@
  #include "blob.h"
  #include "tree-walk.h"
  #include "refs.h"
 +#include "remote.h"
  
  static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
  {
@@@ -241,8 -240,7 +241,8 @@@ static int ambiguous_path(const char *p
  
  /*
   * *string and *len will only be substituted, and *string returned (for
 - * later free()ing) if the string passed in is of the form @{-<n>}.
 + * later free()ing) if the string passed in is a magic short-hand form
 + * to name a branch.
   */
  static char *substitute_branch_name(const char **string, int *len)
  {
@@@ -325,20 -323,6 +325,20 @@@ int dwim_log(const char *str, int len, 
        return logs_found;
  }
  
 +static inline int upstream_mark(const char *string, int len)
 +{
 +      const char *suffix[] = { "@{upstream}", "@{u}" };
 +      int i;
 +
 +      for (i = 0; i < ARRAY_SIZE(suffix); i++) {
 +              int suffix_len = strlen(suffix[i]);
 +              if (suffix_len <= len
 +                  && !memcmp(string, suffix[i], suffix_len))
 +                      return suffix_len;
 +      }
 +      return 0;
 +}
 +
  static int get_sha1_1(const char *name, int len, unsigned char *sha1);
  
  static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
        if (len && str[len-1] == '}') {
                for (at = len-2; at >= 0; at--) {
                        if (str[at] == '@' && str[at+1] == '{') {
 -                              reflog_len = (len-1) - (at+2);
 -                              len = at;
 +                              if (!upstream_mark(str + at, len - at)) {
 +                                      reflog_len = (len-1) - (at+2);
 +                                      len = at;
 +                              }
                                break;
                        }
                }
                } else if (0 <= nth)
                        at_time = 0;
                else {
+                       int errors = 0;
                        char *tmp = xstrndup(str + at + 2, reflog_len);
-                       at_time = approxidate(tmp);
+                       at_time = approxidate_careful(tmp, &errors);
                        free(tmp);
+                       if (errors)
+                               return -1;
                }
                if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
                                &co_time, &co_tz, &co_cnt)) {
@@@ -758,10 -743,17 +761,10 @@@ static int grab_nth_branch_switch(unsig
  }
  
  /*
 - * This reads "@{-N}" syntax, finds the name of the Nth previous
 - * branch we were on, and places the name of the branch in the given
 - * buf and returns the number of characters parsed if successful.
 - *
 - * If the input is not of the accepted format, it returns a negative
 - * number to signal an error.
 - *
 - * If the input was ok but there are not N branch switches in the
 - * reflog, it returns 0.
 + * Parse @{-N} syntax, return the number of characters parsed
 + * if successful; otherwise signal an error with negative value.
   */
 -int interpret_branch_name(const char *name, struct strbuf *buf)
 +static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf)
  {
        long nth;
        int i, retval;
@@@ -805,102 -797,6 +808,102 @@@ release_return
        return retval;
  }
  
 +int get_sha1_mb(const char *name, unsigned char *sha1)
 +{
 +      struct commit *one, *two;
 +      struct commit_list *mbs;
 +      unsigned char sha1_tmp[20];
 +      const char *dots;
 +      int st;
 +
 +      dots = strstr(name, "...");
 +      if (!dots)
 +              return get_sha1(name, sha1);
 +      if (dots == name)
 +              st = get_sha1("HEAD", sha1_tmp);
 +      else {
 +              struct strbuf sb;
 +              strbuf_init(&sb, dots - name);
 +              strbuf_add(&sb, name, dots - name);
 +              st = get_sha1(sb.buf, sha1_tmp);
 +              strbuf_release(&sb);
 +      }
 +      if (st)
 +              return st;
 +      one = lookup_commit_reference_gently(sha1_tmp, 0);
 +      if (!one)
 +              return -1;
 +
 +      if (get_sha1(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
 +              return -1;
 +      two = lookup_commit_reference_gently(sha1_tmp, 0);
 +      if (!two)
 +              return -1;
 +      mbs = get_merge_bases(one, two, 1);
 +      if (!mbs || mbs->next)
 +              st = -1;
 +      else {
 +              st = 0;
 +              hashcpy(sha1, mbs->item->object.sha1);
 +      }
 +      free_commit_list(mbs);
 +      return st;
 +}
 +
 +/*
 + * This reads short-hand syntax that not only evaluates to a commit
 + * object name, but also can act as if the end user spelled the name
 + * of the branch from the command line.
 + *
 + * - "@{-N}" finds the name of the Nth previous branch we were on, and
 + *   places the name of the branch in the given buf and returns the
 + *   number of characters parsed if successful.
 + *
 + * - "<branch>@{upstream}" finds the name of the other ref that
 + *   <branch> is configured to merge with (missing <branch> defaults
 + *   to the current branch), and places the name of the branch in the
 + *   given buf and returns the number of characters parsed if
 + *   successful.
 + *
 + * If the input is not of the accepted format, it returns a negative
 + * number to signal an error.
 + *
 + * If the input was ok but there are not N branch switches in the
 + * reflog, it returns 0.
 + */
 +int interpret_branch_name(const char *name, struct strbuf *buf)
 +{
 +      char *cp;
 +      struct branch *upstream;
 +      int namelen = strlen(name);
 +      int len = interpret_nth_prior_checkout(name, buf);
 +      int tmp_len;
 +
 +      if (!len)
 +              return len; /* syntax Ok, not enough switches */
 +      if (0 < len)
 +              return len; /* consumed from the front */
 +      cp = strchr(name, '@');
 +      if (!cp)
 +              return -1;
 +      tmp_len = upstream_mark(cp, namelen - (cp - name));
 +      if (!tmp_len)
 +              return -1;
 +      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);
 +      free(cp);
 +      cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
 +      strbuf_reset(buf);
 +      strbuf_addstr(buf, cp);
 +      free(cp);
 +      return len;
 +}
 +
  /*
   * This is like "get_sha1_basic()", except it allows "sha1 expressions",
   * notably "xyz^" for "parent of xyz"
@@@ -911,96 -807,7 +914,96 @@@ int get_sha1(const char *name, unsigne
        return get_sha1_with_mode(name, sha1, &unused);
  }
  
 -int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
 +/* Must be called only when object_name:filename doesn't exist. */
 +static void diagnose_invalid_sha1_path(const char *prefix,
 +                                     const char *filename,
 +                                     const unsigned char *tree_sha1,
 +                                     const char *object_name)
 +{
 +      struct stat st;
 +      unsigned char sha1[20];
 +      unsigned mode;
 +
 +      if (!prefix)
 +              prefix = "";
 +
 +      if (!lstat(filename, &st))
 +              die("Path '%s' exists on disk, but not in '%s'.",
 +                  filename, object_name);
 +      if (errno == ENOENT || errno == ENOTDIR) {
 +              char *fullname = xmalloc(strlen(filename)
 +                                           + strlen(prefix) + 1);
 +              strcpy(fullname, prefix);
 +              strcat(fullname, filename);
 +
 +              if (!get_tree_entry(tree_sha1, fullname,
 +                                  sha1, &mode)) {
 +                      die("Path '%s' exists, but not '%s'.\n"
 +                          "Did you mean '%s:%s'?",
 +                          fullname,
 +                          filename,
 +                          object_name,
 +                          fullname);
 +              }
 +              die("Path '%s' does not exist in '%s'",
 +                  filename, object_name);
 +      }
 +}
 +
 +/* Must be called only when :stage:filename doesn't exist. */
 +static void diagnose_invalid_index_path(int stage,
 +                                      const char *prefix,
 +                                      const char *filename)
 +{
 +      struct stat st;
 +      struct cache_entry *ce;
 +      int pos;
 +      unsigned namelen = strlen(filename);
 +      unsigned fullnamelen;
 +      char *fullname;
 +
 +      if (!prefix)
 +              prefix = "";
 +
 +      /* Wrong stage number? */
 +      pos = cache_name_pos(filename, namelen);
 +      if (pos < 0)
 +              pos = -pos - 1;
 +      ce = active_cache[pos];
 +      if (ce_namelen(ce) == namelen &&
 +          !memcmp(ce->name, filename, namelen))
 +              die("Path '%s' is in the index, but not at stage %d.\n"
 +                  "Did you mean ':%d:%s'?",
 +                  filename, stage,
 +                  ce_stage(ce), filename);
 +
 +      /* Confusion between relative and absolute filenames? */
 +      fullnamelen = namelen + strlen(prefix);
 +      fullname = xmalloc(fullnamelen + 1);
 +      strcpy(fullname, prefix);
 +      strcat(fullname, filename);
 +      pos = cache_name_pos(fullname, fullnamelen);
 +      if (pos < 0)
 +              pos = -pos - 1;
 +      ce = active_cache[pos];
 +      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'?",
 +                  fullname, filename,
 +                  ce_stage(ce), fullname);
 +
 +      if (!lstat(filename, &st))
 +              die("Path '%s' exists on disk, but not in the index.", filename);
 +      if (errno == ENOENT || errno == ENOTDIR)
 +              die("Path '%s' does not exist (neither on disk nor in the index).",
 +                  filename);
 +
 +      free(fullname);
 +}
 +
 +
 +int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode, int gently, const char *prefix)
  {
        int ret, bracket_depth;
        int namelen = strlen(name);
                        }
                        pos++;
                }
 +              if (!gently)
 +                      diagnose_invalid_index_path(stage, prefix, cp);
                return -1;
        }
        for (cp = name, bracket_depth = 0; *cp; cp++) {
        }
        if (*cp == ':') {
                unsigned char tree_sha1[20];
 -              if (!get_sha1_1(name, cp-name, tree_sha1))
 -                      return get_tree_entry(tree_sha1, cp+1, sha1,
 -                                            mode);
 +              char *object_name = NULL;
 +              if (!gently) {
 +                      object_name = xmalloc(cp-name+1);
 +                      strncpy(object_name, name, cp-name);
 +                      object_name[cp-name] = '\0';
 +              }
 +              if (!get_sha1_1(name, cp-name, tree_sha1)) {
 +                      const char *filename = cp+1;
 +                      ret = get_tree_entry(tree_sha1, filename, sha1, mode);
 +                      if (!gently) {
 +                              diagnose_invalid_sha1_path(prefix, filename,
 +                                                         tree_sha1, object_name);
 +                              free(object_name);
 +                      }
 +                      return ret;
 +              } else {
 +                      if (!gently)
 +                              die("Invalid object name '%s'.", object_name);
 +              }
        }
        return ret;
  }