Merge branch 'maint-1.7.11' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 10 Sep 2012 22:31:06 +0000 (15:31 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 10 Sep 2012 22:31:06 +0000 (15:31 -0700)
* maint-1.7.11:
Almost 1.7.11.6
gitweb: URL-decode $my_url/$my_uri when stripping PATH_INFO
rebase -i: use full onto sha1 in reflog
sh-setup: protect from exported IFS
receive-pack: do not leak output from auto-gc to standard output
t/t5400: demonstrate breakage caused by informational message from prune
setup: clarify error messages for file/revisions ambiguity
send-email: improve RFC2047 quote parsing
fsck: detect null sha1 in tree entries
do not write null sha1s to on-disk index
diff: do not use null sha1 as a sentinel value

1  2 
builtin.h
builtin/blame.c
builtin/cat-file.c
diff.c
git-rebase--interactive.sh
gitweb/gitweb.perl
read-cache.c
revision.c
setup.c
diff --combined builtin.h
index ba6626b03505dd0622faea4e2bea51ed4b6720f5,dffb34ef4e29799bcb9a8c78bdf439547845794f..8e377522fee4be94f9cdca40d44ecdcc18b42d8b
+++ b/builtin.h
@@@ -9,6 -9,7 +9,6 @@@
  
  #define DEFAULT_MERGE_LOG_LEN 20
  
 -extern const char git_version_string[];
  extern const char git_usage_string[];
  extern const char git_more_info_string[];
  
@@@ -43,7 -44,7 +43,7 @@@ extern int check_pager_config(const cha
  struct diff_options;
  extern void setup_diff_pager(struct diff_options *);
  
- extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, char **buf, unsigned long *buf_size);
+ extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, int sha1_valid, char **buf, unsigned long *buf_size);
  
  extern int cmd_add(int argc, const char **argv, const char *prefix);
  extern int cmd_annotate(int argc, const char **argv, const char *prefix);
@@@ -67,7 -68,6 +67,7 @@@ extern int cmd_commit(int argc, const c
  extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
  extern int cmd_config(int argc, const char **argv, const char *prefix);
  extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
 +extern int cmd_credential(int argc, const char **argv, const char *prefix);
  extern int cmd_describe(int argc, const char **argv, const char *prefix);
  extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
  extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
diff --combined builtin/blame.c
index 0d50273ce975d956cba3c17160bf3704b57f3f9c,a9705d06a5364e881eb894c5b401e6a3b953202d..f2c48ef5afc11966ba6df354978be52ccfa92814
@@@ -110,6 -110,7 +110,7 @@@ static int diff_hunks(mmfile_t *file_a
  int textconv_object(const char *path,
                    unsigned mode,
                    const unsigned char *sha1,
+                   int sha1_valid,
                    char **buf,
                    unsigned long *buf_size)
  {
        struct userdiff_driver *textconv;
  
        df = alloc_filespec(path);
-       fill_filespec(df, sha1, mode);
+       fill_filespec(df, sha1, sha1_valid, mode);
        textconv = get_textconv(df);
        if (!textconv) {
                free_filespec(df);
@@@ -142,7 -143,7 +143,7 @@@ static void fill_origin_blob(struct dif
  
                num_read_blob++;
                if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
-                   textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size))
+                   textconv_object(o->path, o->mode, o->blob_sha1, 1, &file->ptr, &file_size))
                        ;
                else
                        file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
@@@ -2123,7 -2124,7 +2124,7 @@@ static struct commit *fake_working_tree
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
                        if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
-                           textconv_object(read_from, mode, null_sha1, &buf_ptr, &buf_len))
+                           textconv_object(read_from, mode, null_sha1, 0, &buf_ptr, &buf_len))
                                strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
                        else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
                                die_errno("cannot open or read '%s'", read_from);
        ce = xcalloc(1, size);
        hashcpy(ce->sha1, origin->blob_sha1);
        memcpy(ce->name, path, len);
 -      ce->ce_flags = create_ce_flags(len, 0);
 +      ce->ce_flags = create_ce_flags(0);
 +      ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
  
@@@ -2516,7 -2516,7 +2517,7 @@@ parse_done
                        die("no such path %s in %s", path, final_commit_name);
  
                if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
-                   textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf,
+                   textconv_object(path, o->mode, o->blob_sha1, 1, (char **) &sb.final_buf,
                                    &sb.final_buf_size))
                        ;
                else
diff --combined builtin/cat-file.c
index af74e775a182bdf764436362cb37471e74930459,60568f9c39955e72e454c7d22b143d3e72a1a9a5..0eca2d7bd0e6d620edc70b4f643a8911efbbc8ae
@@@ -91,7 -91,7 +91,7 @@@ static int cat_one_file(int opt, const 
        unsigned long size;
        struct object_context obj_context;
  
 -      if (get_sha1_with_context(obj_name, sha1, &obj_context))
 +      if (get_sha1_with_context(obj_name, 0, sha1, &obj_context))
                die("Not a valid object name %s", obj_name);
  
        buf = NULL;
                        die("git cat-file --textconv %s: <object> must be <sha1:path>",
                            obj_name);
  
-               if (!textconv_object(obj_context.path, obj_context.mode, sha1, &buf, &size))
+               if (!textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size))
                        die("git cat-file --textconv: unable to run textconv on %s",
                            obj_name);
                break;
diff --combined diff.c
index 95706a5b4098afc8f20fe53425ff760c996a3b4e,15125e8894c61f895c8f054cac0e8890210d3b7b..359f40a2101bb0247a4a685bf23565d9debe8633
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -1397,7 -1397,7 +1397,7 @@@ int print_stat_summary(FILE *fp, int fi
  
        if (!files) {
                assert(insertions == 0 && deletions == 0);
 -              return fputs(_(" 0 files changed\n"), fp);
 +              return fprintf(fp, "%s\n", _(" 0 files changed"));
        }
  
        strbuf_addf(&sb,
@@@ -2541,12 -2541,12 +2541,12 @@@ void free_filespec(struct diff_filespe
  }
  
  void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
-                  unsigned short mode)
+                  int sha1_valid, unsigned short mode)
  {
        if (mode) {
                spec->mode = canon_mode(mode);
                hashcpy(spec->sha1, sha1);
-               spec->sha1_valid = !is_null_sha1(sha1);
+               spec->sha1_valid = sha1_valid;
        }
  }
  
@@@ -4693,6 -4693,7 +4693,7 @@@ static int is_submodule_ignored(const c
  void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
+                   int sha1_valid,
                    const char *concatpath, unsigned dirty_submodule)
  {
        struct diff_filespec *one, *two;
        two = alloc_filespec(concatpath);
  
        if (addremove != '+')
-               fill_filespec(one, sha1, mode);
+               fill_filespec(one, sha1, sha1_valid, mode);
        if (addremove != '-') {
-               fill_filespec(two, sha1, mode);
+               fill_filespec(two, sha1, sha1_valid, mode);
                two->dirty_submodule = dirty_submodule;
        }
  
@@@ -4739,6 -4740,7 +4740,7 @@@ void diff_change(struct diff_options *o
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
+                int old_sha1_valid, int new_sha1_valid,
                 const char *concatpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
                const unsigned char *tmp_c;
                tmp = old_mode; old_mode = new_mode; new_mode = tmp;
                tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
+               tmp = old_sha1_valid; old_sha1_valid = new_sha1_valid;
+                       new_sha1_valid = tmp;
                tmp = old_dirty_submodule; old_dirty_submodule = new_dirty_submodule;
                        new_dirty_submodule = tmp;
        }
  
        one = alloc_filespec(concatpath);
        two = alloc_filespec(concatpath);
-       fill_filespec(one, old_sha1, old_mode);
-       fill_filespec(two, new_sha1, new_mode);
+       fill_filespec(one, old_sha1, old_sha1_valid, old_mode);
+       fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
        one->dirty_submodule = old_dirty_submodule;
        two->dirty_submodule = new_dirty_submodule;
  
index 0d2056f027cbd6811faee3c45088a11b24d86102,84926783d1c4956727226bbabe796467819d36e5..a09e8423ddb1d05c581ef72101c3f5201ce2eeb9
@@@ -9,7 -9,9 +9,7 @@@
  #
  # The original idea comes from Eric W. Biederman, in
  # http://article.gmane.org/gmane.comp.version-control.git/22407
 -
 -. git-sh-setup
 -
 +#
  # The file containing rebase commands, comments, and empty lines.
  # This file is created by "git rebase -i" then edited by the user.  As
  # the lines are processed, they are removed from the front of this
@@@ -415,29 -417,6 +415,29 @@@ record_in_rewritten() 
        esac
  }
  
 +do_pick () {
 +      if test "$(git rev-parse HEAD)" = "$squash_onto"
 +      then
 +              # Set the correct commit message and author info on the
 +              # sentinel root before cherry-picking the original changes
 +              # without committing (-n).  Finally, update the sentinel again
 +              # to include these changes.  If the cherry-pick results in a
 +              # conflict, this means our behaviour is similar to a standard
 +              # failed cherry-pick during rebase, with a dirty index to
 +              # resolve before manually running git commit --amend then git
 +              # rebase --continue.
 +              git commit --allow-empty --allow-empty-message --amend \
 +                         --no-post-rewrite -n -q -C $1 &&
 +                      pick_one -n $1 &&
 +                      git commit --allow-empty --allow-empty-message \
 +                                 --amend --no-post-rewrite -n -q -C $1 ||
 +                      die_with_patch $1 "Could not apply $1... $2"
 +      else
 +              pick_one $1 ||
 +                      die_with_patch $1 "Could not apply $1... $2"
 +      fi
 +}
 +
  do_next () {
        rm -f "$msg" "$author_script" "$amend" || exit
        read -r command sha1 rest < "$todo"
                comment_for_reflog pick
  
                mark_action_done
 -              pick_one $sha1 ||
 -                      die_with_patch $sha1 "Could not apply $sha1... $rest"
 +              do_pick $sha1 "$rest"
                record_in_rewritten $sha1
                ;;
        reword|r)
                comment_for_reflog reword
  
                mark_action_done
 -              pick_one $sha1 ||
 -                      die_with_patch $sha1 "Could not apply $sha1... $rest"
 +              do_pick $sha1 "$rest"
                git commit --amend --no-post-rewrite || {
                        warn "Could not amend commit after successfully picking $sha1... $rest"
                        warn "This is most likely due to an empty commit message, or the pre-commit hook"
                comment_for_reflog edit
  
                mark_action_done
 -              pick_one $sha1 ||
 -                      die_with_patch $sha1 "Could not apply $sha1... $rest"
 +              do_pick $sha1 "$rest"
                warn "Stopped at $sha1... $rest"
                exit_with_patch $sha1 0
                ;;
                author_script_content=$(get_author_ident_from_commit HEAD)
                echo "$author_script_content" > "$author_script"
                eval "$author_script_content"
 -              output git reset --soft HEAD^
 -              pick_one -n $sha1 || die_failed_squash $sha1 "$rest"
 +              if ! pick_one -n $sha1
 +              then
 +                      git rev-parse --verify HEAD >"$amend"
 +                      die_failed_squash $sha1 "$rest"
 +              fi
                case "$(peek_next_command)" in
                squash|s|fixup|f)
                        # This is an intermediate commit; its message will only be
                        # used in case of trouble.  So use the long version:
 -                      do_with_author output git commit --no-verify -F "$squash_msg" ||
 +                      do_with_author output git commit --amend --no-verify -F "$squash_msg" ||
                                die_failed_squash $sha1 "$rest"
                        ;;
                *)
                        # This is the final command of this squash/fixup group
                        if test -f "$fixup_msg"
                        then
 -                              do_with_author git commit --no-verify -F "$fixup_msg" ||
 +                              do_with_author git commit --amend --no-verify -F "$fixup_msg" ||
                                        die_failed_squash $sha1 "$rest"
                        else
                                cp "$squash_msg" "$GIT_DIR"/SQUASH_MSG || exit
                                rm -f "$GIT_DIR"/MERGE_MSG
 -                              do_with_author git commit --no-verify -e ||
 +                              do_with_author git commit --amend --no-verify -F "$GIT_DIR"/SQUASH_MSG -e ||
                                        die_failed_squash $sha1 "$rest"
                        fi
                        rm -f "$squash_msg" "$fixup_msg"
        test -s "$todo" && return
  
        comment_for_reflog finish &&
-       shortonto=$(git rev-parse --short $onto) &&
        newhead=$(git rev-parse HEAD) &&
        case $head_name in
        refs/*)
-               message="$GIT_REFLOG_ACTION: $head_name onto $shortonto" &&
+               message="$GIT_REFLOG_ACTION: $head_name onto $onto" &&
                git update-ref -m "$message" $head_name $newhead $orig_head &&
                git symbolic-ref \
                  -m "$GIT_REFLOG_ACTION: returning to $head_name" \
@@@ -705,27 -683,6 +704,27 @@@ rearrange_squash () 
        rm -f "$1.sq" "$1.rearranged"
  }
  
 +# Add commands after a pick or after a squash/fixup serie
 +# in the todo list.
 +add_exec_commands () {
 +      {
 +              first=t
 +              while read -r insn rest
 +              do
 +                      case $insn in
 +                      pick)
 +                              test -n "$first" ||
 +                              printf "%s" "$cmd"
 +                              ;;
 +                      esac
 +                      printf "%s %s\n" "$insn" "$rest"
 +                      first=
 +              done
 +              printf "%s" "$cmd"
 +      } <"$1" >"$1.new" &&
 +      mv "$1.new" "$1"
 +}
 +
  case "$action" in
  continue)
        # do we have anything to commit?
@@@ -751,6 -708,7 +750,6 @@@ In both case, once you're done, continu
                fi
                . "$author_script" ||
                        die "Error trying to find the author identity to amend commit"
 -              current_head=
                if test -f "$amend"
                then
                        current_head=$(git rev-parse --verify HEAD)
                        die "\
  You have uncommitted changes in your working tree. Please, commit them
  first and then run 'git rebase --continue' again."
 -                      git reset --soft HEAD^ ||
 -                      die "Cannot rewind the HEAD"
 +                      do_with_author git commit --amend --no-verify -F "$msg" -e ||
 +                              die "Could not commit staged changes."
 +              else
 +                      do_with_author git commit --no-verify -F "$msg" -e ||
 +                              die "Could not commit staged changes."
                fi
 -              do_with_author git commit --no-verify -F "$msg" -e || {
 -                      test -n "$current_head" && git reset --soft $current_head
 -                      die "Could not commit staged changes."
 -              }
        fi
  
        record_in_rewritten "$(cat "$state_dir"/stopped-sha)"
@@@ -897,8 -856,6 +896,8 @@@ f
  
  test -s "$todo" || echo noop >> "$todo"
  test -n "$autosquash" && rearrange_squash "$todo"
 +test -n "$cmd" && add_exec_commands "$todo"
 +
  cat >> "$todo" << EOF
  
  # Rebase $shortrevisions onto $shortonto
diff --combined gitweb/gitweb.perl
index 3d6a7053881ec7646dc28a2eba991a15ff80c634,a40ed0ceb0756d85e26afaabf6d46b7c9105f576..7f8c1878d407e07c3ac4ac16d840557a18176d29
@@@ -54,6 -54,11 +54,11 @@@ sub evaluate_uri 
        # to build the base URL ourselves:
        our $path_info = decode_utf8($ENV{"PATH_INFO"});
        if ($path_info) {
+               # $path_info has already been URL-decoded by the web server, but
+               # $my_url and $my_uri have not. URL-decode them so we can properly
+               # strip $path_info.
+               $my_url = unescape($my_url);
+               $my_uri = unescape($my_uri);
                if ($my_url =~ s,\Q$path_info\E$,, &&
                    $my_uri =~ s,\Q$path_info\E$,, &&
                    defined $ENV{'SCRIPT_NAME'}) {
@@@ -4484,33 -4489,30 +4489,33 @@@ sub git_print_log 
        }
  
        # print log
 -      my $signoff = 0;
 -      my $empty = 0;
 +      my $skip_blank_line = 0;
        foreach my $line (@$log) {
 -              if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) {
 -                      $signoff = 1;
 -                      $empty = 0;
 +              if ($line =~ m/^\s*([A-Z][-A-Za-z]*-[Bb]y|C[Cc]): /) {
                        if (! $opts{'-remove_signoff'}) {
                                print "<span class=\"signoff\">" . esc_html($line) . "</span><br/>\n";
 -                              next;
 -                      } else {
 -                              # remove signoff lines
 -                              next;
 +                              $skip_blank_line = 1;
                        }
 -              } else {
 -                      $signoff = 0;
 +                      next;
 +              }
 +
 +              if ($line =~ m,\s*([a-z]*link): (https?://\S+),i) {
 +                      if (! $opts{'-remove_signoff'}) {
 +                              print "<span class=\"signoff\">" . esc_html($1) . ": " .
 +                                      "<a href=\"" . esc_html($2) . "\">" . esc_html($2) . "</a>" .
 +                                      "</span><br/>\n";
 +                              $skip_blank_line = 1;
 +                      }
 +                      next;
                }
  
                # print only one empty line
                # do not print empty line after signoff
                if ($line eq "") {
 -                      next if ($empty || $signoff);
 -                      $empty = 1;
 +                      next if ($skip_blank_line);
 +                      $skip_blank_line = 1;
                } else {
 -                      $empty = 0;
 +                      $skip_blank_line = 0;
                }
  
                print format_log_line_html($line) . "<br/>\n";
  
        if ($opts{'-final_empty_line'}) {
                # end with single empty line
 -              print "<br/>\n" unless $empty;
 +              print "<br/>\n" unless $skip_blank_line;
        }
  }
  
diff --combined read-cache.c
index 2f8159fb165f853aafb5e0cee61f35aa854271ec,7d4bf68e33d35ebeb5e9f985ac5c9dc2962fc8ba..d2be78ea95e2e554b085721bafa939c9024125ed
  
  static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
  
 +/* Mask for the name length in ce_flags in the on-disk index */
 +
 +#define CE_NAMEMASK  (0x0fff)
 +
  /* Index extensions.
   *
   * The first letter should be 'A'..'Z' for extensions that are not
@@@ -58,8 -54,8 +58,8 @@@ void rename_index_entry_at(struct index
  
        new = xmalloc(cache_entry_size(namelen));
        copy_cache_entry(new, old);
 -      new->ce_flags &= ~(CE_STATE_MASK | CE_NAMEMASK);
 -      new->ce_flags |= (namelen >= CE_NAMEMASK ? CE_NAMEMASK : namelen);
 +      new->ce_flags &= ~CE_STATE_MASK;
 +      new->ce_namelen = namelen;
        memcpy(new->name, new_name, namelen + 1);
  
        cache_tree_invalidate_path(istate->cache_tree, old->name);
@@@ -399,10 -395,17 +399,10 @@@ int df_name_compare(const char *name1, 
        return c1 - c2;
  }
  
 -int cache_name_compare(const char *name1, int flags1, const char *name2, int flags2)
 +int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2)
  {
 -      int len1, len2, len, cmp;
 -
 -      len1 = flags1 & CE_NAMEMASK;
 -      if (CE_NAMEMASK <= len1)
 -              len1 = strlen(name1 + CE_NAMEMASK) + CE_NAMEMASK;
 -      len2 = flags2 & CE_NAMEMASK;
 -      if (CE_NAMEMASK <= len2)
 -              len2 = strlen(name2 + CE_NAMEMASK) + CE_NAMEMASK;
 -      len = len1 < len2 ? len1 : len2;
 +      int len = len1 < len2 ? len1 : len2;
 +      int cmp;
  
        cmp = memcmp(name1, name2, len);
        if (cmp)
        if (len1 > len2)
                return 1;
  
 -      /* Compare stages  */
 -      flags1 &= CE_STAGEMASK;
 -      flags2 &= CE_STAGEMASK;
 -
 -      if (flags1 < flags2)
 +      if (stage1 < stage2)
                return -1;
 -      if (flags1 > flags2)
 +      if (stage1 > stage2)
                return 1;
        return 0;
  }
  
 -int index_name_pos(const struct index_state *istate, const char *name, int namelen)
 +int cache_name_compare(const char *name1, int len1, const char *name2, int len2)
 +{
 +      return cache_name_stage_compare(name1, len1, 0, name2, len2, 0);
 +}
 +
 +int index_name_stage_pos(const struct index_state *istate, const char *name, int namelen, int stage)
  {
        int first, last;
  
        while (last > first) {
                int next = (last + first) >> 1;
                struct cache_entry *ce = istate->cache[next];
 -              int cmp = cache_name_compare(name, namelen, ce->name, ce->ce_flags);
 +              int cmp = cache_name_stage_compare(name, namelen, stage, ce->name, ce_namelen(ce), ce_stage(ce));
                if (!cmp)
                        return next;
                if (cmp < 0) {
        return -first-1;
  }
  
 +int index_name_pos(const struct index_state *istate, const char *name, int namelen)
 +{
 +      return index_name_stage_pos(istate, name, namelen, 0);
 +}
 +
  /* Remove entry, return true if there are more entries to go.. */
  int remove_index_entry_at(struct index_state *istate, int pos)
  {
@@@ -589,7 -586,7 +589,7 @@@ int add_to_index(struct index_state *is
        size = cache_entry_size(namelen);
        ce = xcalloc(1, size);
        memcpy(ce->name, path, namelen);
 -      ce->ce_flags = namelen;
 +      ce->ce_namelen = namelen;
        if (!intent_only)
                fill_stat_cache_info(ce, st);
        else
@@@ -691,8 -688,7 +691,8 @@@ struct cache_entry *make_cache_entry(un
  
        hashcpy(ce->sha1, sha1);
        memcpy(ce->name, path, len);
 -      ce->ce_flags = create_ce_flags(len, stage);
 +      ce->ce_flags = create_ce_flags(stage);
 +      ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
  
        if (refresh)
@@@ -829,7 -825,7 +829,7 @@@ static int has_dir_name(struct index_st
                }
                len = slash - name;
  
 -              pos = index_name_pos(istate, name, create_ce_flags(len, stage));
 +              pos = index_name_stage_pos(istate, name, len, stage);
                if (pos >= 0) {
                        /*
                         * Found one, but not so fast.  This could
@@@ -919,7 -915,7 +919,7 @@@ static int add_index_entry_with_check(s
        int new_only = option & ADD_CACHE_NEW_ONLY;
  
        cache_tree_invalidate_path(istate->cache_tree, ce->name);
 -      pos = index_name_pos(istate, ce->name, ce->ce_flags);
 +      pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
  
        /* existing match? Just replace it. */
        if (pos >= 0) {
                if (!ok_to_replace)
                        return error("'%s' appears as both a file and as a directory",
                                     ce->name);
 -              pos = index_name_pos(istate, ce->name, ce->ce_flags);
 +              pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
                pos = -pos-1;
        }
        return pos + 1;
@@@ -1128,7 -1124,7 +1128,7 @@@ int refresh_index(struct index_state *i
                        continue;
  
                if (pathspec &&
 -                  !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen))
 +                  !match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
                        filtered = 1;
  
                if (ce_stage(ce)) {
@@@ -1328,8 -1324,7 +1328,8 @@@ static struct cache_entry *cache_entry_
        ce->ce_uid   = ntoh_l(ondisk->uid);
        ce->ce_gid   = ntoh_l(ondisk->gid);
        ce->ce_size  = ntoh_l(ondisk->size);
 -      ce->ce_flags = flags;
 +      ce->ce_flags = flags & ~CE_NAMEMASK;
 +      ce->ce_namelen = len;
        hashcpy(ce->sha1, ondisk->sha1);
        memcpy(ce->name, name, len);
        ce->name[len] = '\0';
@@@ -1656,8 -1651,6 +1656,8 @@@ static void ce_smudge_racily_clean_entr
  static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
                                       struct cache_entry *ce)
  {
 +      short flags;
 +
        ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
        ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
        ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec);
        ondisk->gid  = htonl(ce->ce_gid);
        ondisk->size = htonl(ce->ce_size);
        hashcpy(ondisk->sha1, ce->sha1);
 -      ondisk->flags = htons(ce->ce_flags);
 +
 +      flags = ce->ce_flags;
 +      flags |= (ce_namelen(ce) >= CE_NAMEMASK ? CE_NAMEMASK : ce_namelen(ce));
 +      ondisk->flags = htons(flags);
        if (ce->ce_flags & CE_EXTENDED) {
                struct ondisk_cache_entry_extended *ondisk2;
                ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
@@@ -1800,6 -1790,8 +1800,8 @@@ int write_index(struct index_state *ist
                        continue;
                if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
                        ce_smudge_racily_clean_entry(ce);
+               if (is_null_sha1(ce->sha1))
+                       return error("cache entry has null sha1: %s", ce->name);
                if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
                        return -1;
        }
@@@ -1856,12 -1848,11 +1858,12 @@@ int read_index_unmerged(struct index_st
                if (!ce_stage(ce))
                        continue;
                unmerged = 1;
 -              len = strlen(ce->name);
 +              len = ce_namelen(ce);
                size = cache_entry_size(len);
                new_ce = xcalloc(1, size);
                memcpy(new_ce->name, ce->name, len);
 -              new_ce->ce_flags = create_ce_flags(len, 0) | CE_CONFLICTED;
 +              new_ce->ce_flags = create_ce_flags(0) | CE_CONFLICTED;
 +              new_ce->ce_namelen = len;
                new_ce->ce_mode = ce->ce_mode;
                if (add_index_entry(istate, new_ce, 0))
                        return error("%s: cannot drop to stage #0",
diff --combined revision.c
index 9e8f47a25d8def94cbf2a8389039e4d3d670cd06,74c484ca84867c5edfb1ac569ac63efef1a893e6..04c7de87d089e576d6101628dc0417344a12f67e
@@@ -345,6 -345,7 +345,7 @@@ static int tree_difference = REV_TREE_S
  static void file_add_remove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
+                   int sha1_valid,
                    const char *fullpath, unsigned dirty_submodule)
  {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
@@@ -358,6 -359,7 +359,7 @@@ static void file_change(struct diff_opt
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
+                int old_sha1_valid, int new_sha1_valid,
                 const char *fullpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
@@@ -1000,7 -1002,7 +1002,7 @@@ static int add_parents_only(struct rev_
                flags ^= UNINTERESTING;
                arg++;
        }
 -      if (get_sha1(arg, sha1))
 +      if (get_sha1_committish(arg, sha1))
                return 0;
        while (1) {
                it = get_reference(revs, arg, sha1, 0);
@@@ -1114,16 -1116,16 +1116,16 @@@ static void prepare_show_merge(struct r
        revs->limited = 1;
  }
  
 -int handle_revision_arg(const char *arg_, struct rev_info *revs,
 -                      int flags,
 -                      int cant_be_filename)
 +int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
  {
 -      unsigned mode;
 +      struct object_context oc;
        char *dotdot;
        struct object *object;
        unsigned char sha1[20];
        int local_flags;
        const char *arg = arg_;
 +      int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
 +      unsigned get_sha1_flags = 0;
  
        dotdot = strstr(arg, "..");
        if (dotdot) {
                        next = "HEAD";
                if (dotdot == arg)
                        this = "HEAD";
 -              if (!get_sha1(this, from_sha1) &&
 -                  !get_sha1(next, sha1)) {
 +              if (!get_sha1_committish(this, from_sha1) &&
 +                  !get_sha1_committish(next, sha1)) {
                        struct commit *a, *b;
                        struct commit_list *exclude;
  
                local_flags = UNINTERESTING;
                arg++;
        }
 -      if (get_sha1_with_mode(arg, sha1, &mode))
 +
 +      if (revarg_opt & REVARG_COMMITTISH)
 +              get_sha1_flags = GET_SHA1_COMMITTISH;
 +
 +      if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc))
                return revs->ignore_missing ? 0 : -1;
        if (!cant_be_filename)
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, sha1, flags ^ local_flags);
        add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
 -      add_pending_object_with_mode(revs, object, arg, mode);
 +      add_pending_object_with_mode(revs, object, arg, oc.mode);
        return 0;
  }
  
@@@ -1261,7 -1259,7 +1263,7 @@@ static void read_revisions_from_stdin(s
                        }
                        die("options not supported in --stdin mode");
                }
 -              if (handle_revision_arg(sb.buf, revs, 0, 1))
 +              if (handle_revision_arg(sb.buf, revs, 0, REVARG_CANNOT_BE_FILENAME))
                        die("bad revision '%s'", sb.buf);
        }
        if (seen_dashdash)
@@@ -1712,7 -1710,7 +1714,7 @@@ static int handle_revision_pseudo_opt(c
   */
  int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
  {
 -      int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
 +      int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0, revarg_opt;
        struct cmdline_pathspec prune_data;
        const char *submodule = NULL;
  
  
        /* Second, deal with arguments and options */
        flags = 0;
 +      revarg_opt = opt ? opt->revarg_opt : 0;
 +      if (seen_dashdash)
 +              revarg_opt |= REVARG_CANNOT_BE_FILENAME;
        read_from_stdin = 0;
        for (left = i = 1; i < argc; i++) {
                const char *arg = argv[i];
                        continue;
                }
  
 -              if (handle_revision_arg(arg, revs, flags, seen_dashdash)) {
 +
 +              if (handle_revision_arg(arg, revs, flags, revarg_opt)) {
                        int j;
                        if (seen_dashdash || *arg == '^')
                                die("bad revision '%s'", arg);
        if (revs->def && !revs->pending.nr && !got_rev_arg) {
                unsigned char sha1[20];
                struct object *object;
 -              unsigned mode;
 -              if (get_sha1_with_mode(revs->def, sha1, &mode))
 +              struct object_context oc;
 +              if (get_sha1_with_context(revs->def, 0, sha1, &oc))
                        die("bad default revision '%s'", revs->def);
                object = get_reference(revs, revs->def, sha1, 0);
 -              add_pending_object_with_mode(revs, object, revs->def, mode);
 +              add_pending_object_with_mode(revs, object, revs->def, oc.mode);
        }
  
        /* Did the user ask for any diff output? Run the diff! */
@@@ -2369,28 -2363,29 +2371,28 @@@ static struct commit *get_revision_inte
        }
  
        /*
 -       * Now pick up what they want to give us
 +       * If our max_count counter has reached zero, then we are done. We
 +       * don't simply return NULL because we still might need to show
 +       * boundary commits. But we want to avoid calling get_revision_1, which
 +       * might do a considerable amount of work finding the next commit only
 +       * for us to throw it away.
 +       *
 +       * If it is non-zero, then either we don't have a max_count at all
 +       * (-1), or it is still counting, in which case we decrement.
         */
 -      c = get_revision_1(revs);
 -      if (c) {
 -              while (0 < revs->skip_count) {
 -                      revs->skip_count--;
 -                      c = get_revision_1(revs);
 -                      if (!c)
 -                              break;
 +      if (revs->max_count) {
 +              c = get_revision_1(revs);
 +              if (c) {
 +                      while (0 < revs->skip_count) {
 +                              revs->skip_count--;
 +                              c = get_revision_1(revs);
 +                              if (!c)
 +                                      break;
 +                      }
                }
 -      }
  
 -      /*
 -       * Check the max_count.
 -       */
 -      switch (revs->max_count) {
 -      case -1:
 -              break;
 -      case 0:
 -              c = NULL;
 -              break;
 -      default:
 -              revs->max_count--;
 +              if (revs->max_count > 0)
 +                      revs->max_count--;
        }
  
        if (c)
diff --combined setup.c
index 9139beefc75c4fe92cf8c7dc54a9fae972995aca,7663a4cbb2e3d179c3a16d647733da7bf89a3e2d..3a1b2fd45580cad7ecd55efa755872efc81075ad
+++ b/setup.c
@@@ -77,22 -77,27 +77,23 @@@ static void NORETURN die_verify_filenam
                                         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.",
+                   "Use 'git <command> -- <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
         * 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.
 +       * let maybe_die_on_misspelt_object_name() even trigger.
         */
        if (!(arg[0] == ':' && !isalnum(arg[1])))
 -              /* try a detailed diagnostic ... */
 -              get_sha1_with_mode_1(arg, sha1, &mode, 1, prefix);
 +              maybe_die_on_misspelt_object_name(arg, 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);
+           "Use '--' to separate paths from revisions, like this:\n"
+           "'git <command> [<revision>...] -- [<file>...]'", arg);
  
  }
  
@@@ -141,7 -146,8 +142,8 @@@ void verify_non_filename(const char *pr
        if (!check_filename(prefix, arg))
                return;
        die("ambiguous argument '%s': both revision and filename\n"
-           "Use '--' to separate filenames from revisions", arg);
+           "Use '--' to separate paths from revisions, like this:\n"
+           "'git <command> [<revision>...] -- [<file>...]'", arg);
  }
  
  /*