Sync with v1.9.5
authorJunio C Hamano <gitster@pobox.com>
Wed, 17 Dec 2014 19:28:02 +0000 (11:28 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 17 Dec 2014 19:28:54 +0000 (11:28 -0800)
* maint-1.9:
Git 1.9.5
Git 1.8.5.6
fsck: complain about NTFS ".git" aliases in trees
read-cache: optionally disallow NTFS .git variants
path: add is_ntfs_dotgit() helper
fsck: complain about HFS+ ".git" aliases in trees
read-cache: optionally disallow HFS+ .git variants
utf8: add is_hfs_dotgit() helper
fsck: notice .git case-insensitively
t1450: refactor ".", "..", and ".git" fsck tests
verify_dotfile(): reject .git case-insensitively
read-tree: add tests for confusing paths like ".." and ".git"
unpack-trees: propagate errors adding entries to the index

12 files changed:
1  2 
Documentation/config.txt
Documentation/git.txt
cache.h
config.c
config.mak.uname
environment.c
fsck.c
path.c
read-cache.c
t/test-lib.sh
unpack-trees.c
utf8.c
diff --combined Documentation/config.txt
index c08286e968ad057c288b72f77e877f42a640ae5f,7076aa928286d6777e6a05c136d8401780379f58..0c325976390ff0878f8aede3a416587f9b9af75f
@@@ -131,13 -131,8 +131,13 @@@ Variable
  
  Note that this list is non-comprehensive and not necessarily complete.
  For command-specific variables, you will find a more detailed description
 -in the appropriate manual page. You will find a description of non-core
 -porcelain configuration variables in the respective porcelain documentation.
 +in the appropriate manual page.
 +
 +Other git-related tools may and do use their own variables.  When
 +inventing new variables for use in your own tool, make sure their
 +names do not conflict with those that are used by Git itself and
 +other popular tools, and describe them in your documentation.
 +
  
  advice.*::
        These variables control various optional help messages designed to
  --
        pushUpdateRejected::
                Set this variable to 'false' if you want to disable
 -              'pushNonFFCurrent', 'pushNonFFDefault',
 +              'pushNonFFCurrent',
                'pushNonFFMatching', 'pushAlreadyExists',
                'pushFetchFirst', and 'pushNeedsForce'
                simultaneously.
        pushNonFFCurrent::
                Advice shown when linkgit:git-push[1] fails due to a
                non-fast-forward update to the current branch.
 -      pushNonFFDefault::
 -              Advice to set 'push.default' to 'upstream' or 'current'
 -              when you ran linkgit:git-push[1] and pushed 'matching
 -              refs' by default (i.e. you did not provide an explicit
 -              refspec, and no 'push.default' configuration was set)
 -              and it resulted in a non-fast-forward error.
        pushNonFFMatching::
                Advice shown when you ran linkgit:git-push[1] and pushed
                'matching refs' explicitly (i.e. you used ':', or
@@@ -233,6 -234,17 +233,17 @@@ core.precomposeunicode:
        When false, file names are handled fully transparent by Git,
        which is backward compatible with older versions of Git.
  
+ core.protectHFS::
+       If set to true, do not allow checkout of paths that would
+       be considered equivalent to `.git` on an HFS+ filesystem.
+       Defaults to `true` on Mac OS, and `false` elsewhere.
+ core.protectNTFS::
+       If set to true, do not allow checkout of paths that would
+       cause problems with the NTFS filesystem, e.g. conflict with
+       8.3 "short" names.
+       Defaults to `true` on Windows, and `false` elsewhere.
  core.trustctime::
        If false, the ctime differences between the index and the
        working tree are ignored; useful when the inode change time
@@@ -991,14 -1003,6 +1002,14 @@@ commit.cleanup:
        have to remove the help lines that begin with `#` in the commit log
        template yourself, if you do this).
  
 +commit.gpgsign::
 +
 +      A boolean to specify whether all commits should be GPG signed.
 +      Use of this option when doing operations such as rebase can
 +      result in a large number of commits being signed. It may be
 +      convenient to use an agent to avoid typing your GPG passphrase
 +      several times.
 +
  commit.status::
        A boolean to enable/disable inclusion of status information in the
        commit message template when using an editor to prepare the commit
@@@ -1156,11 -1160,6 +1167,11 @@@ filter.<driver>.smudge:
        object to a worktree file upon checkout.  See
        linkgit:gitattributes[5] for details.
  
 +gc.aggressiveDepth::
 +      The depth parameter used in the delta compression
 +      algorithm used by 'git gc --aggressive'.  This defaults
 +      to 250.
 +
  gc.aggressiveWindow::
        The window size parameter used in the delta compression
        algorithm used by 'git gc --aggressive'.  This defaults
@@@ -1179,10 -1178,6 +1190,10 @@@ gc.autopacklimit:
        --auto` consolidates them into one larger pack.  The
        default value is 50.  Setting this to 0 disables it.
  
 +gc.autodetach::
 +      Make `git gc --auto` return immediately andrun in background
 +      if the system supports it. Default is true.
 +
  gc.packrefs::
        Running `git pack-refs` in a repository renders it
        unclonable by Git versions prior to 1.5.1.2 over dumb
@@@ -1340,10 -1335,6 +1351,10 @@@ gui.diffcontext:
        Specifies how many context lines should be used in calls to diff
        made by the linkgit:git-gui[1]. The default is "5".
  
 +gui.displayuntracked::
 +      Determines if linkgit::git-gui[1] shows untracked files
 +      in the file list. The default is "true".
 +
  gui.encoding::
        Specifies the default encoding to use for displaying of
        file contents in linkgit:git-gui[1] and linkgit:gitk[1].
@@@ -1621,10 -1612,6 +1632,10 @@@ imap:
        The configuration variables in the 'imap' section are described
        in linkgit:git-imap-send[1].
  
 +index.version::
 +      Specify the version with which new index files should be
 +      initialized.  This does not affect existing repositories.
 +
  init.templatedir::
        Specify the directory from which templates will be copied.
        (See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
@@@ -1657,7 -1644,7 +1668,7 @@@ interactive.singlekey:
        linkgit:git-add[1], linkgit:git-checkout[1], linkgit:git-commit[1],
        linkgit:git-reset[1], and linkgit:git-stash[1]. Note that this
        setting is silently ignored if portable keystroke input
 -      is not available.
 +      is not available; requires the Perl module Term::ReadKey.
  
  log.abbrevCommit::
        If true, makes linkgit:git-log[1], linkgit:git-show[1], and
@@@ -1886,31 -1873,6 +1897,31 @@@ pack.packSizeLimit:
        Common unit suffixes of 'k', 'm', or 'g' are
        supported.
  
 +pack.useBitmaps::
 +      When true, git will use pack bitmaps (if available) when packing
 +      to stdout (e.g., during the server side of a fetch). Defaults to
 +      true. You should not generally need to turn this off unless
 +      you are debugging pack bitmaps.
 +
 +pack.writebitmaps::
 +      When true, git will write a bitmap index when packing all
 +      objects to disk (e.g., when `git repack -a` is run).  This
 +      index can speed up the "counting objects" phase of subsequent
 +      packs created for clones and fetches, at the cost of some disk
 +      space and extra time spent on the initial repack.  Defaults to
 +      false.
 +
 +pack.writeBitmapHashCache::
 +      When true, git will include a "hash cache" section in the bitmap
 +      index (if one is written). This cache can be used to feed git's
 +      delta heuristics, potentially leading to better deltas between
 +      bitmapped and non-bitmapped objects (e.g., when serving a fetch
 +      between an older, bitmapped pack and objects that have been
 +      pushed since the last gc). The downside is that it consumes 4
 +      bytes per object of disk space, and that JGit's bitmap
 +      implementation does not understand it, causing it to complain if
 +      Git and JGit are used on the same repository. Defaults to false.
 +
  pager.<cmd>::
        If the value is boolean, turns on or off pagination of the
        output of a particular Git subcommand when writing to a tty.
@@@ -1930,16 -1892,6 +1941,16 @@@ pretty.<name>:
        Note that an alias with the same name as a built-in format
        will be silently ignored.
  
 +pull.ff::
 +      By default, Git does not create an extra merge commit when merging
 +      a commit that is a descendant of the current commit. Instead, the
 +      tip of the current branch is fast-forwarded. When set to `false`,
 +      this variable tells Git to create an extra merge commit in such
 +      a case (equivalent to giving the `--no-ff` option from the command
 +      line). When set to `only`, only such fast-forward merges are
 +      allowed (equivalent to giving the `--ff-only` option from the
 +      command line).
 +
  pull.rebase::
        When true, rebase branches on top of the fetched branch, instead
        of merging the default branch from the default remote when "git
@@@ -1992,7 -1944,7 +2003,7 @@@ When pushing to a remote that is differ
  pull from, work as `current`.  This is the safest option and is suited
  for beginners.
  +
 -This mode will become the default in Git 2.0.
 +This mode has become the default in Git 2.0.
  
  * `matching` - push all branches having the same name on both ends.
    This makes the repository you are pushing to remember the set of
@@@ -2011,8 -1963,8 +2022,8 @@@ suitable for pushing into a shared cent
  people may add new branches there, or update the tip of existing
  branches outside your control.
  +
 -This is currently the default, but Git 2.0 will change the default
 -to `simple`.
 +This used to be the default, but not since Git 2.0 (`simple` is the
 +new default).
  
  --
  
@@@ -2170,13 -2122,6 +2181,13 @@@ repack.usedeltabaseoffset:
        "false" and repack. Access from old Git versions over the
        native protocol are unaffected by this option.
  
 +repack.packKeptObjects::
 +      If set to true, makes `git repack` act as if
 +      `--pack-kept-objects` was passed. See linkgit:git-repack[1] for
 +      details. Defaults to `false` normally, but `true` if a bitmap
 +      index is being written (either via `--write-bitmap-index` or
 +      `pack.writeBitmaps`).
 +
  rerere.autoupdate::
        When set to true, `git-rerere` updates the index with the
        resulting contents after it cleanly resolves conflicts using
@@@ -2293,9 -2238,7 +2304,9 @@@ status.submodulesummary:
        --summary-limit option of linkgit:git-submodule[1]). Please note
        that the summary output command will be suppressed for all
        submodules when `diff.ignoreSubmodules` is set to 'all' or only
 -      for those submodules where `submodule.<name>.ignore=all`. To
 +      for those submodules where `submodule.<name>.ignore=all`. The only
 +      exception to that rule is that status and commit will show staged
 +      submodule changes. To
        also view the summary for ignored submodules you can either use
        the --ignore-submodules=dirty command line option or the 'git
        submodule summary' command, which shows a similar output but does
@@@ -2326,9 -2269,7 +2337,9 @@@ submodule.<name>.fetchRecurseSubmodules
  submodule.<name>.ignore::
        Defines under what circumstances "git status" and the diff family show
        a submodule as modified. When set to "all", it will never be considered
 -      modified, "dirty" will ignore all changes to the submodules work tree and
 +      modified (but it will nonetheless show up in the output of status and
 +      commit when it has been staged), "dirty" will ignore all changes
 +      to the submodules work tree and
        takes only differences between the HEAD of the submodule and the commit
        recorded in the superproject into account. "untracked" will additionally
        let submodules with modified tracked files in their work tree show up.
@@@ -2361,13 -2302,6 +2372,13 @@@ transfer.unpackLimit:
        not set, the value of this variable is used instead.
        The default value is 100.
  
 +uploadarchive.allowUnreachable::
 +      If true, allow clients to use `git archive --remote` to request
 +      any tree, whether reachable from the ref tips or not. See the
 +      discussion in the `SECURITY` section of
 +      linkgit:git-upload-archive[1] for more details. Defaults to
 +      `false`.
 +
  uploadpack.hiderefs::
        String(s) `upload-pack` uses to decide which refs to omit
        from its initial advertisement.  Use more than one
diff --combined Documentation/git.txt
index cd509775ad71608921d5a72d4b34f09c9930c30f,d092ce5684dcc931e43dad0ab431cc5b12f2dd0d..aa5b268e003ccab7f423a02d3f5db7f3cedd3657
@@@ -43,27 -43,20 +43,29 @@@ unreleased) version of Git, that is ava
  branch of the `git.git` repository.
  Documentation for older releases are available here:
  
- * link:v1.9.4/git.html[documentation for release 1.9.4]
 +* link:v2.0.4/git.html[documentation for release 2.0.4]
 +
 +* release notes for
 +  link:RelNotes/2.0.4.txt[2.0.4],
 +  link:RelNotes/2.0.3.txt[2.0.3],
 +  link:RelNotes/2.0.2.txt[2.0.2],
 +  link:RelNotes/2.0.1.txt[2.0.1],
 +  link:RelNotes/2.0.0.txt[2.0.0].
 +
+ * link:v1.9.5/git.html[documentation for release 1.9.5]
  
  * release notes for
+   link:RelNotes/1.9.5.txt[1.9.5],
    link:RelNotes/1.9.4.txt[1.9.4],
    link:RelNotes/1.9.3.txt[1.9.3],
    link:RelNotes/1.9.2.txt[1.9.2],
    link:RelNotes/1.9.1.txt[1.9.1],
    link:RelNotes/1.9.0.txt[1.9.0].
  
- * link:v1.8.5.5/git.html[documentation for release 1.8.5.5]
+ * link:v1.8.5.6/git.html[documentation for release 1.8.5.6]
  
  * release notes for
+   link:RelNotes/1.8.5.6.txt[1.8.5.6],
    link:RelNotes/1.8.5.5.txt[1.8.5.5],
    link:RelNotes/1.8.5.4.txt[1.8.5.4],
    link:RelNotes/1.8.5.3.txt[1.8.5.3],
@@@ -733,11 -726,6 +735,11 @@@ Git so take care if using Cogito etc
        index file. If not specified, the default of `$GIT_DIR/index`
        is used.
  
 +'GIT_INDEX_VERSION'::
 +      This environment variable allows the specification of an index
 +      version for new repositories.  It won't affect existing index
 +      files.  By default index file version [23] is used.
 +
  'GIT_OBJECT_DIRECTORY'::
        If the object storage directory is specified via this
        environment variable then the sha1 directories are created
@@@ -1046,7 -1034,7 +1048,7 @@@ Author
  -------
  Git was started by Linus Torvalds, and is currently maintained by Junio
  C Hamano. Numerous contributions have come from the Git mailing list
 -<git@vger.kernel.org>.  http://www.ohloh.net/p/git/contributors/summary
 +<git@vger.kernel.org>.  http://www.openhub.net/p/git/contributors/summary
  gives you a more complete list of contributors.
  
  If you have a clone of git.git itself, the
diff --combined cache.h
index c708062df95d30eccf713d3f1c38897499750061,017c487609eab63d4db54b14591455880d01ebaa..f23fdbee96fc9ad14e3a16e77d277293207dd6c3
+++ b/cache.h
@@@ -3,7 -3,7 +3,7 @@@
  
  #include "git-compat-util.h"
  #include "strbuf.h"
 -#include "hash.h"
 +#include "hashmap.h"
  #include "advice.h"
  #include "gettext.h"
  #include "convert.h"
@@@ -130,12 -130,12 +130,12 @@@ struct stat_data 
  };
  
  struct cache_entry {
 +      struct hashmap_entry ent;
        struct stat_data ce_stat_data;
        unsigned int ce_mode;
        unsigned int ce_flags;
        unsigned int ce_namelen;
        unsigned char sha1[20];
 -      struct cache_entry *next;
        char name[FLEX_ARRAY]; /* more */
  };
  
  #define CE_ADDED             (1 << 19)
  
  #define CE_HASHED            (1 << 20)
 -#define CE_UNHASHED          (1 << 21)
  #define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
  #define CE_CONFLICTED        (1 << 23)
  
@@@ -194,18 -195,17 +194,18 @@@ struct pathspec
   * Copy the sha1 and stat state of a cache entry from one to
   * another. But we never change the name, or the hash state!
   */
 -#define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
  static inline void copy_cache_entry(struct cache_entry *dst,
                                    const struct cache_entry *src)
  {
 -      unsigned int state = dst->ce_flags & CE_STATE_MASK;
 +      unsigned int state = dst->ce_flags & CE_HASHED;
  
        /* Don't copy hash chain and name */
 -      memcpy(dst, src, offsetof(struct cache_entry, next));
 +      memcpy(&dst->ce_stat_data, &src->ce_stat_data,
 +                      offsetof(struct cache_entry, name) -
 +                      offsetof(struct cache_entry, ce_stat_data));
  
        /* Restore the hash state */
 -      dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
 +      dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
  }
  
  static inline unsigned create_ce_flags(unsigned stage)
@@@ -277,9 -277,8 +277,9 @@@ struct index_state 
        struct cache_time timestamp;
        unsigned name_hash_initialized : 1,
                 initialized : 1;
 -      struct hash_table name_hash;
 -      struct hash_table dir_hash;
 +      struct hashmap name_hash;
 +      struct hashmap dir_hash;
 +      unsigned char sha1[20];
  };
  
  extern struct index_state the_index;
@@@ -317,6 -316,7 +317,6 @@@ extern void free_name_hash(struct index
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
  #define cache_dir_exists(name, namelen) index_dir_exists(&the_index, (name), (namelen))
  #define cache_file_exists(name, namelen, igncase) index_file_exists(&the_index, (name), (namelen), (igncase))
 -#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)
@@@ -434,7 -434,6 +434,7 @@@ extern int set_git_dir_init(const char 
  extern int init_db(const char *template_dir, unsigned int flags);
  
  extern void sanitize_stdfds(void);
 +extern int daemonize(void);
  
  #define alloc_nr(x) (((x)+16)*3/2)
  
@@@ -468,6 -467,7 +468,6 @@@ extern int unmerged_index(const struct 
  extern int verify_path(const char *path);
  extern struct cache_entry *index_dir_exists(struct index_state *istate, const char *name, int namelen);
  extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 -extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
  extern int index_name_pos(const struct index_state *, const char *name, int namelen);
  #define ADD_CACHE_OK_TO_ADD 1         /* Ok to add */
  #define ADD_CACHE_OK_TO_REPLACE 2     /* Ok to replace file/directory */
@@@ -484,11 -484,11 +484,11 @@@ extern int remove_file_from_index(struc
  #define ADD_CACHE_IGNORE_ERRORS       4
  #define ADD_CACHE_IGNORE_REMOVAL 8
  #define ADD_CACHE_INTENT 16
 -#define ADD_CACHE_IMPLICIT_DOT 32     /* internal to "git add -u/-A" */
  extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
  extern int add_file_to_index(struct index_state *, const char *path, int flags);
  extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
  extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
 +extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
  extern int index_name_is_other(const struct index_state *, const char *, int);
  extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
  
@@@ -582,21 -582,13 +582,23 @@@ extern size_t packed_git_limit
  extern size_t delta_base_cache_limit;
  extern unsigned long big_file_threshold;
  extern unsigned long pack_size_limit_cfg;
 -extern int read_replace_refs;
 +
 +/*
 + * Do replace refs need to be checked this run?  This variable is
 + * initialized to true unless --no-replace-object is used or
 + * $GIT_NO_REPLACE_OBJECTS is set, but is set to false by some
 + * commands that do not want replace references to be active.  As an
 + * optimization it is also set to false if replace references have
 + * been sought but there were none.
 + */
 +extern int check_replace_refs;
 +
  extern int fsync_object_files;
  extern int core_preload_index;
  extern int core_apply_sparse_checkout;
  extern int precomposed_unicode;
+ extern int protect_hfs;
+ extern int protect_ntfs;
  
  /*
   * The character that begins a commented line in user-editable file
@@@ -671,28 -663,9 +673,28 @@@ extern char *git_path(const char *fmt, 
  extern char *git_path_submodule(const char *path, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
  
 -extern char *sha1_file_name(const unsigned char *sha1);
 +/*
 + * Return the name of the file in the local object database that would
 + * be used to store a loose object with the specified sha1.  The
 + * return value is a pointer to a statically allocated buffer that is
 + * overwritten each time the function is called.
 + */
 +extern const char *sha1_file_name(const unsigned char *sha1);
 +
 +/*
 + * Return the name of the (local) packfile with the specified sha1 in
 + * its name.  The return value is a pointer to memory that is
 + * overwritten each time this function is called.
 + */
  extern char *sha1_pack_name(const unsigned char *sha1);
 +
 +/*
 + * Return the name of the (local) pack index file with the specified
 + * sha1 in its name.  The return value is a pointer to memory that is
 + * overwritten each time this function is called.
 + */
  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];
  
@@@ -811,6 -784,7 +813,7 @@@ int longest_ancestor_length(const char 
  char *strip_path_suffix(const char *path, const char *suffix);
  int daemon_avoid_alias(const char *path);
  int offset_1st_component(const char *path);
+ extern int is_ntfs_dotgit(const char *name);
  
  /* object replacement */
  #define LOOKUP_REPLACE_OBJECT 1
@@@ -819,26 -793,13 +822,26 @@@ static inline void *read_sha1_file(cons
  {
        return read_sha1_file_extended(sha1, type, size, LOOKUP_REPLACE_OBJECT);
  }
 +
 +/*
 + * This internal function is only declared here for the benefit of
 + * lookup_replace_object().  Please do not call it directly.
 + */
  extern const unsigned char *do_lookup_replace_object(const unsigned char *sha1);
 +
 +/*
 + * If object sha1 should be replaced, return the replacement object's
 + * name (replaced recursively, if necessary).  The return value is
 + * either sha1 or a pointer to a permanently-allocated value.  When
 + * object replacement is suppressed, always return sha1.
 + */
  static inline const unsigned char *lookup_replace_object(const unsigned char *sha1)
  {
 -      if (!read_replace_refs)
 +      if (!check_replace_refs)
                return sha1;
        return do_lookup_replace_object(sha1);
  }
 +
  static inline const unsigned char *lookup_replace_object_extended(const unsigned char *sha1, unsigned flag)
  {
        if (!(flag & LOOKUP_REPLACE_OBJECT))
@@@ -852,7 -813,6 +855,7 @@@ extern int hash_sha1_file(const void *b
  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 *);
  extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 +extern int git_open_noatime(const char *name);
  extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
  extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
  extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
@@@ -865,19 -825,7 +868,19 @@@ extern int check_sha1_signature(const u
  extern int move_temp_to_file(const char *tmpfile, const char *filename);
  
  extern int has_sha1_pack(const unsigned char *sha1);
 +
 +/*
 + * Return true iff we have an object named sha1, whether local or in
 + * an alternate object database, and whether packed or loose.  This
 + * function does not respect replace references.
 + */
  extern int has_sha1_file(const unsigned char *sha1);
 +
 +/*
 + * Return true iff an alternate object database has a loose object
 + * with the specified name.  This function does not respect replace
 + * references.
 + */
  extern int has_loose_object_nonlocal(const unsigned char *sha1);
  
  extern int has_pack_index(const unsigned char *sha1);
@@@ -1141,46 -1089,17 +1144,46 @@@ extern struct packed_git *find_sha1_pac
                                         struct packed_git *packs);
  
  extern void pack_report(void);
 +
 +/*
 + * mmap the index file for the specified packfile (if it is not
 + * already mmapped).  Return 0 on success.
 + */
  extern int open_pack_index(struct packed_git *);
 +
 +/*
 + * munmap the index file for the specified packfile (if it is
 + * currently mmapped).
 + */
  extern void close_pack_index(struct packed_git *);
 +
  extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
  extern void close_pack_windows(struct packed_git *);
  extern void unuse_pack(struct pack_window **);
  extern void free_pack_by_name(const char *);
  extern void clear_delta_base_cache(void);
  extern struct packed_git *add_packed_git(const char *, int, int);
 -extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t);
 -extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t);
 -extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
 +
 +/*
 + * Return the SHA-1 of the nth object within the specified packfile.
 + * Open the index if it is not already open.  The return value points
 + * at the SHA-1 within the mmapped index.  Return NULL if there is an
 + * error.
 + */
 +extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
 +
 +/*
 + * Return the offset of the nth object within the specified packfile.
 + * The index must already be opened.
 + */
 +extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t n);
 +
 +/*
 + * If the object named sha1 is present in the specified packfile,
 + * return its offset within the packfile; otherwise, return 0.
 + */
 +extern off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *);
 +
  extern int is_pack_valid(struct packed_git *);
  extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
  extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
@@@ -1233,12 -1152,6 +1236,12 @@@ extern int update_server_info(int)
  #define CONFIG_INVALID_PATTERN 6
  #define CONFIG_GENERIC_ERROR 7
  
 +struct git_config_source {
 +      unsigned int use_stdin:1;
 +      const char *file;
 +      const char *blob;
 +};
 +
  typedef int (*config_fn_t)(const char *, const char *, void *);
  extern int git_default_config(const char *, const char *, void *);
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
@@@ -1248,7 -1161,8 +1251,7 @@@ extern void git_config_push_parameter(c
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern int git_config(config_fn_t fn, void *);
  extern int git_config_with_options(config_fn_t fn, void *,
 -                                 const char *filename,
 -                                 const char *blob_ref,
 +                                 struct git_config_source *config_source,
                                   int respect_includes);
  extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
  extern int git_parse_ulong(const char *, unsigned long *);
@@@ -1323,8 -1237,6 +1326,8 @@@ extern void fsync_or_die(int fd, const 
  
  extern ssize_t read_in_full(int fd, void *buf, size_t count);
  extern ssize_t write_in_full(int fd, const void *buf, size_t count);
 +extern ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset);
 +
  static inline ssize_t write_str_in_full(int fd, const char *str)
  {
        return write_in_full(fd, str, strlen(str));
@@@ -1354,7 -1266,6 +1357,7 @@@ extern void *alloc_commit_node(void)
  extern void *alloc_tag_node(void);
  extern void *alloc_object_node(void);
  extern void alloc_report(void);
 +extern unsigned int alloc_commit_index(void);
  
  /* trace.c */
  __attribute__((format (printf, 1, 2)))
@@@ -1462,6 -1373,4 +1465,6 @@@ int stat_validity_check(struct stat_val
   */
  void stat_validity_update(struct stat_validity *sv, int fd);
  
 +int versioncmp(const char *s1, const char *s2);
 +
  #endif /* CACHE_H */
diff --combined config.c
index 2634457f6b07ca55b69f3b4a22b8a9e4339fd121,8984ad20c782795a50e314b5023352612f70f0bc..61a9c296f48c929177835c6128133449a1e14a6b
+++ b/config.c
@@@ -21,7 -21,6 +21,7 @@@ struct config_source 
                } buf;
        } u;
        const char *name;
 +      const char *path;
        int die_on_error;
        int linenr;
        int eof;
@@@ -102,12 -101,12 +102,12 @@@ static int handle_path_include(const ch
        if (!is_absolute_path(path)) {
                char *slash;
  
 -              if (!cf || !cf->name)
 +              if (!cf || !cf->path)
                        return error("relative config includes must come from files");
  
 -              slash = find_last_dir_sep(cf->name);
 +              slash = find_last_dir_sep(cf->path);
                if (slash)
 -                      strbuf_add(&buf, cf->name, slash - cf->name + 1);
 +                      strbuf_add(&buf, cf->path, slash - cf->path + 1);
                strbuf_addstr(&buf, path);
                path = buf.buf;
        }
@@@ -557,7 -556,6 +557,7 @@@ int git_parse_ulong(const char *value, 
        return 1;
  }
  
 +NORETURN
  static void die_bad_number(const char *name, const char *value)
  {
        const char *reason = errno == ERANGE ?
@@@ -669,7 -667,20 +669,7 @@@ static int git_default_core_config(cons
                trust_ctime = git_config_bool(var, value);
                return 0;
        }
 -      if (!strcmp(var, "core.statinfo") ||
 -          !strcmp(var, "core.checkstat")) {
 -              /*
 -               * NEEDSWORK: statinfo was a typo in v1.8.2 that has
 -               * never been advertised.  we will remove it at Git
 -               * 2.0 boundary.
 -               */
 -              if (!strcmp(var, "core.statinfo")) {
 -                      static int warned;
 -                      if (!warned++) {
 -                              warning("'core.statinfo' will be removed in Git 2.0; "
 -                                      "use 'core.checkstat' instead.");
 -                      }
 -              }
 +      if (!strcmp(var, "core.checkstat")) {
                if (!strcasecmp(value, "default"))
                        check_stat = 1;
                else if (!strcasecmp(value, "minimal"))
                return 0;
        }
  
+       if (!strcmp(var, "core.protecthfs")) {
+               protect_hfs = git_config_bool(var, value);
+               return 0;
+       }
+       if (!strcmp(var, "core.protectntfs")) {
+               protect_ntfs = git_config_bool(var, value);
+               return 0;
+       }
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
  }
@@@ -952,7 -973,7 +962,7 @@@ static int git_default_push_config(cons
  static int git_default_mailmap_config(const char *var, const char *value)
  {
        if (!strcmp(var, "mailmap.file"))
 -              return git_config_string(&git_mailmap_file, var, value);
 +              return git_config_pathname(&git_mailmap_file, var, value);
        if (!strcmp(var, "mailmap.blob"))
                return git_config_string(&git_mailmap_blob, var, value);
  
@@@ -1023,35 -1044,24 +1033,35 @@@ static int do_config_from(struct config
        return ret;
  }
  
 -int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 +static int do_config_from_file(config_fn_t fn,
 +              const char *name, const char *path, FILE *f, void *data)
  {
 -      int ret;
 -      FILE *f = fopen(filename, "r");
 +      struct config_source top;
  
 -      ret = -1;
 -      if (f) {
 -              struct config_source top;
 +      top.u.file = f;
 +      top.name = name;
 +      top.path = path;
 +      top.die_on_error = 1;
 +      top.do_fgetc = config_file_fgetc;
 +      top.do_ungetc = config_file_ungetc;
 +      top.do_ftell = config_file_ftell;
  
 -              top.u.file = f;
 -              top.name = filename;
 -              top.die_on_error = 1;
 -              top.do_fgetc = config_file_fgetc;
 -              top.do_ungetc = config_file_ungetc;
 -              top.do_ftell = config_file_ftell;
 +      return do_config_from(&top, fn, data);
 +}
 +
 +static int git_config_from_stdin(config_fn_t fn, void *data)
 +{
 +      return do_config_from_file(fn, "<stdin>", NULL, stdin, data);
 +}
  
 -              ret = do_config_from(&top, fn, data);
 +int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 +{
 +      int ret = -1;
 +      FILE *f;
  
 +      f = fopen(filename, "r");
 +      if (f) {
 +              ret = do_config_from_file(fn, filename, filename, f, data);
                fclose(f);
        }
        return ret;
@@@ -1066,7 -1076,6 +1076,7 @@@ int git_config_from_buf(config_fn_t fn
        top.u.buf.len = len;
        top.u.buf.pos = 0;
        top.name = name;
 +      top.path = NULL;
        top.die_on_error = 0;
        top.do_fgetc = config_buf_fgetc;
        top.do_ungetc = config_buf_ungetc;
@@@ -1175,7 -1184,8 +1185,7 @@@ int git_config_early(config_fn_t fn, vo
  }
  
  int git_config_with_options(config_fn_t fn, void *data,
 -                          const char *filename,
 -                          const char *blob_ref,
 +                          struct git_config_source *config_source,
                            int respect_includes)
  {
        char *repo_config = NULL;
         * If we have a specific filename, use it. Otherwise, follow the
         * regular lookup sequence.
         */
 -      if (filename)
 -              return git_config_from_file(fn, filename, data);
 -      else if (blob_ref)
 -              return git_config_from_blob_ref(fn, blob_ref, data);
 +      if (config_source && config_source->use_stdin)
 +              return git_config_from_stdin(fn, data);
 +      else if (config_source && config_source->file)
 +              return git_config_from_file(fn, config_source->file, data);
 +      else if (config_source && config_source->blob)
 +              return git_config_from_blob_ref(fn, config_source->blob, data);
  
        repo_config = git_pathdup("config");
        ret = git_config_early(fn, data, repo_config);
  
  int git_config(config_fn_t fn, void *data)
  {
 -      return git_config_with_options(fn, data, NULL, NULL, 1);
 +      return git_config_with_options(fn, data, NULL, 1);
  }
  
  /*
@@@ -1538,7 -1546,7 +1548,7 @@@ int git_config_set_multivar_in_file(con
         * The lock serves a purpose in addition to locking: the new
         * contents of .git/config will be written into it.
         */
 -      lock = xcalloc(sizeof(struct lock_file), 1);
 +      lock = xcalloc(1, sizeof(struct lock_file));
        fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (fd < 0) {
                error("could not lock config file %s: %s", config_filename, strerror(errno));
@@@ -1793,7 -1801,7 +1803,7 @@@ int git_config_rename_section_in_file(c
        if (!config_filename)
                config_filename = filename_buf = git_pathdup("config");
  
 -      lock = xcalloc(sizeof(struct lock_file), 1);
 +      lock = xcalloc(1, sizeof(struct lock_file));
        out_fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (out_fd < 0) {
                ret = error("could not lock config file %s", config_filename);
diff --combined config.mak.uname
index 7846bd76573fe2513f08a824e89938deae6543de,f3cdcbcddf49441fa181e2bcdb60fbe8368db6b2..aba56dcfad74919491360d0914d1390a9cbba110
@@@ -97,6 -97,7 +97,7 @@@ ifeq ($(uname_S),Darwin
        HAVE_DEV_TTY = YesPlease
        COMPAT_OBJS += compat/precompose_utf8.o
        BASIC_CFLAGS += -DPRECOMPOSE_UNICODE
+       BASIC_CFLAGS += -DPROTECT_HFS_DEFAULT=1
  endif
  ifeq ($(uname_S),SunOS)
        NEEDS_SOCKET = YesPlease
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
        NO_REGEX = YesPlease
 -      NO_FNMATCH_CASEFOLD = YesPlease
        NO_MSGFMT_EXTENDED_OPTIONS = YesPlease
        HAVE_DEV_TTY = YesPlease
        ifeq ($(uname_R),5.6)
@@@ -157,6 -159,7 +158,6 @@@ ifeq ($(uname_O),Cygwin
                NO_SYMLINK_HEAD = YesPlease
                NO_IPV6 = YesPlease
                OLD_ICONV = UnfortunatelyYes
 -              NO_THREAD_SAFE_PREAD = YesPlease
                # There are conflicting reports about this.
                # On some boxes NO_MMAP is needed, and not so elsewhere.
                # Try commenting this out if you suspect MMAP is more efficient
@@@ -258,6 -261,7 +259,6 @@@ ifeq ($(uname_S),IRIX
        # issue, comment out the NO_MMAP statement.
        NO_MMAP = YesPlease
        NO_REGEX = YesPlease
 -      NO_FNMATCH_CASEFOLD = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH = /usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
@@@ -277,6 -281,7 +278,6 @@@ ifeq ($(uname_S),IRIX64
        # issue, comment out the NO_MMAP statement.
        NO_MMAP = YesPlease
        NO_REGEX = YesPlease
 -      NO_FNMATCH_CASEFOLD = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH = /usr/gnu/bin/bash
        NEEDS_LIBGEN = YesPlease
@@@ -293,6 -298,7 +294,6 @@@ ifeq ($(uname_S),HP-UX
        NO_UNSETENV = YesPlease
        NO_HSTRERROR = YesPlease
        NO_SYS_SELECT_H = YesPlease
 -      NO_FNMATCH_CASEFOLD = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
        NO_NSEC = YesPlease
        ifeq ($(uname_R),B.11.00)
@@@ -323,6 -329,7 +324,6 @@@ ifeq ($(uname_S),Windows
        NO_UNSETENV = YesPlease
        NO_STRCASESTR = YesPlease
        NO_STRLCPY = YesPlease
 -      NO_FNMATCH = YesPlease
        NO_MEMMEM = YesPlease
        # NEEDS_LIBICONV = YesPlease
        NO_ICONV = YesPlease
        NO_MKSTEMPS = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
        NO_SVN_TESTS = YesPlease
 -      NO_PERL_MAKEMAKER = YesPlease
        RUNTIME_PREFIX = YesPlease
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
        NO_NSEC = YesPlease
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
 -      NO_CURL = YesPlease
        NO_GETTEXT = YesPlease
        NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
                compat/win32/dirent.o
        COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
        BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
 -      EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib
 +      EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
        PTHREAD_LIBS =
        lib =
+       BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
  ifndef DEBUG
        BASIC_CFLAGS += -GL -Os -MT
        BASIC_LDFLAGS += -LTCG
@@@ -382,11 -392,13 +384,11 @@@ ifeq ($(uname_S),Interix
                NO_INET_NTOP = YesPlease
                NO_INET_PTON = YesPlease
                NO_SOCKADDR_STORAGE = YesPlease
 -              NO_FNMATCH_CASEFOLD = YesPlease
        endif
        ifeq ($(uname_R),5.2)
                NO_INET_NTOP = YesPlease
                NO_INET_PTON = YesPlease
                NO_SOCKADDR_STORAGE = YesPlease
 -              NO_FNMATCH_CASEFOLD = YesPlease
        endif
  endif
  ifeq ($(uname_S),Minix)
@@@ -431,6 -443,7 +433,6 @@@ ifeq ($(uname_S),NONSTOP_KERNEL
        NO_D_TYPE_IN_DIRENT = YesPlease
        NO_HSTRERROR = YesPlease
        NO_STRCASESTR = YesPlease
 -      NO_FNMATCH_CASEFOLD = YesPlease
        NO_MEMMEM = YesPlease
        NO_STRLCPY = YesPlease
        NO_SETENV = YesPlease
@@@ -474,6 -487,7 +476,6 @@@ ifneq (,$(findstring MINGW,$(uname_S))
        NO_UNSETENV = YesPlease
        NO_STRCASESTR = YesPlease
        NO_STRLCPY = YesPlease
 -      NO_FNMATCH = YesPlease
        NO_MEMMEM = YesPlease
        NEEDS_LIBICONV = YesPlease
        NO_STRTOUMAX = YesPlease
        COMPAT_OBJS += compat/mingw.o compat/winansi.o \
                compat/win32/pthread.o compat/win32/syslog.o \
                compat/win32/dirent.o
+       BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
        BASIC_LDFLAGS += -Wl,--large-address-aware
        EXTLIBS += -lws2_32
        GITLIBS += git.res
@@@ -527,6 -542,7 +530,6 @@@ ifeq ($(uname_S),QNX
        EXPAT_NEEDS_XMLPARSE_H = YesPlease
        HAVE_STRINGS_H = YesPlease
        NEEDS_SOCKET = YesPlease
 -      NO_FNMATCH_CASEFOLD = YesPlease
        NO_GETPAGESIZE = YesPlease
        NO_ICONV = YesPlease
        NO_MEMMEM = YesPlease
diff --combined environment.c
index 5c4815dbe132fc358aa55eeb20d86d867c97c37f,39a8c6c3432d93c1cc6d04324d6632032773c30d..9daa0ba4a36ced9f63541203e7bcc2ab9e1eae56
@@@ -45,7 -45,7 +45,7 @@@ const char *editor_program
  const char *askpass_program;
  const char *excludes_file;
  enum auto_crlf auto_crlf = AUTO_CRLF_FALSE;
 -int read_replace_refs = 1; /* NEEDSWORK: rename to use_replace_refs */
 +int check_replace_refs = 1;
  enum eol core_eol = EOL_UNSET;
  enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
  unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
@@@ -64,6 -64,16 +64,16 @@@ int precomposed_unicode = -1; /* see pr
  struct startup_info *startup_info;
  unsigned long pack_size_limit_cfg;
  
+ #ifndef PROTECT_HFS_DEFAULT
+ #define PROTECT_HFS_DEFAULT 0
+ #endif
+ int protect_hfs = PROTECT_HFS_DEFAULT;
+ #ifndef PROTECT_NTFS_DEFAULT
+ #define PROTECT_NTFS_DEFAULT 0
+ #endif
+ int protect_ntfs = PROTECT_NTFS_DEFAULT;
  /*
   * The character that begins a commented line in user-editable file
   * that is subject to stripspace.
@@@ -147,7 -157,7 +157,7 @@@ static void setup_git_env(void
        if (!git_graft_file)
                git_graft_file = git_pathdup("info/grafts");
        if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
 -              read_replace_refs = 0;
 +              check_replace_refs = 0;
        namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
        namespace_len = strlen(namespace);
        shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
@@@ -237,7 -247,7 +247,7 @@@ int odb_mkstemp(char *template, size_t 
        return xmkstemp_mode(template, mode);
  }
  
 -int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1)
 +int odb_pack_keep(char *name, size_t namesz, const unsigned char *sha1)
  {
        int fd;
  
diff --combined fsck.c
index a7233c8d0b20c06f0aebffbec9c11dacb22b6769,ee7f531e36212a492851565f1bbb56c566039104..5e6723d410532a6e8eec100c524d4e4c24d4a4f4
--- 1/fsck.c
--- 2/fsck.c
+++ b/fsck.c
@@@ -6,6 -6,7 +6,7 @@@
  #include "commit.h"
  #include "tag.h"
  #include "fsck.h"
+ #include "utf8.h"
  
  static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)
  {
@@@ -165,12 -166,19 +166,14 @@@ static int fsck_tree(struct tree *item
  
                sha1 = tree_entry_extract(&desc, &name, &mode);
  
 -              if (is_null_sha1(sha1))
 -                      has_null_sha1 = 1;
 -              if (strchr(name, '/'))
 -                      has_full_path = 1;
 -              if (!*name)
 -                      has_empty_name = 1;
 -              if (!strcmp(name, "."))
 -                      has_dot = 1;
 -              if (!strcmp(name, ".."))
 -                      has_dotdot = 1;
 -              if (!strcasecmp(name, ".git") || is_hfs_dotgit(name) ||
 -                              is_ntfs_dotgit(name))
 -                      has_dotgit = 1;
 +              has_null_sha1 |= is_null_sha1(sha1);
 +              has_full_path |= !!strchr(name, '/');
 +              has_empty_name |= !*name;
 +              has_dot |= !strcmp(name, ".");
 +              has_dotdot |= !strcmp(name, "..");
-               has_dotgit |= !strcmp(name, ".git");
++              has_dotgit |= (!strcmp(name, ".git") ||
++                             is_hfs_dotgit(name) ||
++                             is_ntfs_dotgit(name));
                has_zero_pad |= *(char *)desc.buffer == '0';
                update_tree_entry(&desc);
  
        return retval;
  }
  
 -static int fsck_ident(char **ident, struct object *obj, fsck_error error_func)
 +static int fsck_ident(const char **ident, struct object *obj, fsck_error error_func)
  {
        char *end;
  
        return 0;
  }
  
 -static int fsck_commit(struct commit *commit, fsck_error error_func)
 +static int fsck_commit_buffer(struct commit *commit, const char *buffer,
 +                            fsck_error error_func)
  {
 -      char *buffer = commit->buffer;
 +      const char *tmp;
        unsigned char tree_sha1[20], sha1[20];
        struct commit_graft *graft;
        int parents = 0;
        int err;
  
 -      if (memcmp(buffer, "tree ", 5))
 +      buffer = skip_prefix(buffer, "tree ");
 +      if (!buffer)
                return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line");
 -      if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
 +      if (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\n')
                return error_func(&commit->object, FSCK_ERROR, "invalid 'tree' line format - bad sha1");
 -      buffer += 46;
 -      while (!memcmp(buffer, "parent ", 7)) {
 -              if (get_sha1_hex(buffer+7, sha1) || buffer[47] != '\n')
 +      buffer += 41;
 +      while ((tmp = skip_prefix(buffer, "parent "))) {
 +              buffer = tmp;
 +              if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n')
                        return error_func(&commit->object, FSCK_ERROR, "invalid 'parent' line format - bad sha1");
 -              buffer += 48;
 +              buffer += 41;
                parents++;
        }
        graft = lookup_commit_graft(commit->object.sha1);
                if (p || parents)
                        return error_func(&commit->object, FSCK_ERROR, "parent objects missing");
        }
 -      if (memcmp(buffer, "author ", 7))
 +      buffer = skip_prefix(buffer, "author ");
 +      if (!buffer)
                return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'author' line");
 -      buffer += 7;
        err = fsck_ident(&buffer, &commit->object, error_func);
        if (err)
                return err;
 -      if (memcmp(buffer, "committer ", strlen("committer ")))
 +      buffer = skip_prefix(buffer, "committer ");
 +      if (!buffer)
                return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'committer' line");
 -      buffer += strlen("committer ");
        err = fsck_ident(&buffer, &commit->object, error_func);
        if (err)
                return err;
        return 0;
  }
  
 +static int fsck_commit(struct commit *commit, fsck_error error_func)
 +{
 +      const char *buffer = get_commit_buffer(commit, NULL);
 +      int ret = fsck_commit_buffer(commit, buffer, error_func);
 +      unuse_commit_buffer(commit, buffer);
 +      return ret;
 +}
 +
  static int fsck_tag(struct tag *tag, fsck_error error_func)
  {
        struct object *tagged = tag->tagged;
diff --combined path.c
index c36f00393007dc2d48a7b7fd765d809fb224d8a4,dfd58f4367a2a400c37ceda7192845fd4ce427c1..f10c91a92708d2e9915aa2855658243d90a4b9df
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -249,7 -249,9 +249,7 @@@ int validate_headref(const char *path
  static struct passwd *getpw_str(const char *username, size_t len)
  {
        struct passwd *pw;
 -      char *username_z = xmalloc(len + 1);
 -      memcpy(username_z, username, len);
 -      username_z[len] = '\0';
 +      char *username_z = xmemdupz(username, len);
        pw = getpwnam(username_z);
        free(username_z);
        return pw;
@@@ -828,3 -830,36 +828,36 @@@ int offset_1st_component(const char *pa
                return 2 + is_dir_sep(path[2]);
        return is_dir_sep(path[0]);
  }
+ static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
+ {
+       if (len < skip)
+               return 0;
+       len -= skip;
+       path += skip;
+       while (len-- > 0) {
+               char c = *(path++);
+               if (c != ' ' && c != '.')
+                       return 0;
+       }
+       return 1;
+ }
+ int is_ntfs_dotgit(const char *name)
+ {
+       int len;
+       for (len = 0; ; len++)
+               if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
+                       if (only_spaces_and_periods(name, len, 4) &&
+                                       !strncasecmp(name, ".git", 4))
+                               return 1;
+                       if (only_spaces_and_periods(name, len, 5) &&
+                                       !strncasecmp(name, "git~1", 5))
+                               return 1;
+                       if (name[len] != '\\')
+                               return 0;
+                       name += len + 1;
+                       len = -1;
+               }
+ }
diff --combined read-cache.c
index 7f5645e74546e459efdb584dbf63e1fd75857317,ee07cd610ae5efebb0ad2bd233ab380a6d605f83..36a6f73fedc2fdb7c5dc2e1fb73ca5c5c76979c0
@@@ -14,6 -14,7 +14,7 @@@
  #include "resolve-undo.h"
  #include "strbuf.h"
  #include "varint.h"
+ #include "utf8.h"
  
  static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
                                               unsigned int options);
@@@ -48,7 -49,6 +49,7 @@@ static void replace_index_entry(struct 
        struct cache_entry *old = istate->cache[nr];
  
        remove_name_hash(istate, old);
 +      free(old);
        set_index_entry(istate, nr, ce);
        istate->cache_changed = 1;
  }
@@@ -60,7 -60,7 +61,7 @@@ void rename_index_entry_at(struct index
  
        new = xmalloc(cache_entry_size(namelen));
        copy_cache_entry(new, old);
 -      new->ce_flags &= ~CE_STATE_MASK;
 +      new->ce_flags &= ~CE_HASHED;
        new->ce_namelen = namelen;
        memcpy(new->name, new_name, namelen + 1);
  
@@@ -480,7 -480,6 +481,7 @@@ int remove_index_entry_at(struct index_
  
        record_resolve_undo(istate, ce);
        remove_name_hash(istate, ce);
 +      free(ce);
        istate->cache_changed = 1;
        istate->cache_nr--;
        if (pos >= istate->cache_nr)
@@@ -502,10 -501,8 +503,10 @@@ void remove_marked_cache_entries(struc
        unsigned int i, j;
  
        for (i = j = 0; i < istate->cache_nr; i++) {
 -              if (ce_array[i]->ce_flags & CE_REMOVE)
 +              if (ce_array[i]->ce_flags & CE_REMOVE) {
                        remove_name_hash(istate, ce_array[i]);
 +                      free(ce_array[i]);
 +              }
                else
                        ce_array[j++] = ce_array[i];
        }
@@@ -584,7 -581,7 +585,7 @@@ static struct cache_entry *create_alias
        return new;
  }
  
 -static void record_intent_to_add(struct cache_entry *ce)
 +void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
  {
        unsigned char sha1[20];
        if (write_sha1_file("", 0, blob_type, sha1))
@@@ -670,7 -667,7 +671,7 @@@ int add_to_index(struct index_state *is
                if (index_path(ce->sha1, path, st, HASH_WRITE_OBJECT))
                        return error("unable to index file %s", path);
        } else
 -              record_intent_to_add(ce);
 +              set_object_name_for_intent_to_add_entry(ce);
  
        if (ignore_case && alias && different_name(ce, alias))
                ce = create_alias_ce(ce, alias);
@@@ -756,9 -753,10 +757,10 @@@ static int verify_dotfile(const char *r
         * shares the path end test with the ".." case.
         */
        case 'g':
-               if (rest[1] != 'i')
+       case 'G':
+               if (rest[1] != 'i' && rest[1] != 'I')
                        break;
-               if (rest[2] != 't')
+               if (rest[2] != 't' && rest[2] != 'T')
                        break;
                rest += 2;
        /* fallthrough */
@@@ -782,6 -780,10 +784,10 @@@ int verify_path(const char *path
                        return 1;
                if (is_dir_sep(c)) {
  inside:
+                       if (protect_hfs && is_hfs_dotgit(path))
+                               return 0;
+                       if (protect_ntfs && is_ntfs_dotgit(path))
+                               return 0;
                        c = *path++;
                        if ((c == '.' && !verify_dotfile(path)) ||
                            is_dir_sep(c) || c == '\0')
@@@ -990,7 -992,11 +996,7 @@@ int add_index_entry(struct index_state 
        }
  
        /* Make sure the array is big enough .. */
 -      if (istate->cache_nr == istate->cache_alloc) {
 -              istate->cache_alloc = alloc_nr(istate->cache_alloc);
 -              istate->cache = xrealloc(istate->cache,
 -                                      istate->cache_alloc * sizeof(*istate->cache));
 -      }
 +      ALLOC_GROW(istate->cache, istate->cache_nr + 1, istate->cache_alloc);
  
        /* Add it in.. */
        istate->cache_nr++;
@@@ -1216,42 -1222,6 +1222,42 @@@ static struct cache_entry *refresh_cach
  
  #define INDEX_FORMAT_DEFAULT 3
  
 +static int index_format_config(const char *var, const char *value, void *cb)
 +{
 +      unsigned int *version = cb;
 +      if (!strcmp(var, "index.version")) {
 +              *version = git_config_int(var, value);
 +              return 0;
 +      }
 +      return 1;
 +}
 +
 +static unsigned int get_index_format_default(void)
 +{
 +      char *envversion = getenv("GIT_INDEX_VERSION");
 +      char *endp;
 +      unsigned int version = INDEX_FORMAT_DEFAULT;
 +
 +      if (!envversion) {
 +              git_config(index_format_config, &version);
 +              if (version < INDEX_FORMAT_LB || INDEX_FORMAT_UB < version) {
 +                      warning(_("index.version set, but the value is invalid.\n"
 +                                "Using version %i"), INDEX_FORMAT_DEFAULT);
 +                      return INDEX_FORMAT_DEFAULT;
 +              }
 +              return version;
 +      }
 +
 +      version = strtoul(envversion, &endp, 10);
 +      if (*endp ||
 +          version < INDEX_FORMAT_LB || INDEX_FORMAT_UB < version) {
 +              warning(_("GIT_INDEX_VERSION set, but the value is invalid.\n"
 +                        "Using version %i"), INDEX_FORMAT_DEFAULT);
 +              version = INDEX_FORMAT_DEFAULT;
 +      }
 +      return version;
 +}
 +
  /*
   * dev/ino/uid/gid/size are also just tracked to the low 32 bits
   * Again - this is just a (very strong in practice) heuristic that
@@@ -1346,6 -1316,26 +1352,6 @@@ int read_index(struct index_state *ista
        return read_index_from(istate, get_index_file());
  }
  
 -#ifndef NEEDS_ALIGNED_ACCESS
 -#define ntoh_s(var) ntohs(var)
 -#define ntoh_l(var) ntohl(var)
 -#else
 -static inline uint16_t ntoh_s_force_align(void *p)
 -{
 -      uint16_t x;
 -      memcpy(&x, p, sizeof(x));
 -      return ntohs(x);
 -}
 -static inline uint32_t ntoh_l_force_align(void *p)
 -{
 -      uint32_t x;
 -      memcpy(&x, p, sizeof(x));
 -      return ntohl(x);
 -}
 -#define ntoh_s(var) ntoh_s_force_align(&(var))
 -#define ntoh_l(var) ntoh_l_force_align(&(var))
 -#endif
 -
  static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *ondisk,
                                                   unsigned int flags,
                                                   const char *name,
  {
        struct cache_entry *ce = xmalloc(cache_entry_size(len));
  
 -      ce->ce_stat_data.sd_ctime.sec = ntoh_l(ondisk->ctime.sec);
 -      ce->ce_stat_data.sd_mtime.sec = ntoh_l(ondisk->mtime.sec);
 -      ce->ce_stat_data.sd_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
 -      ce->ce_stat_data.sd_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
 -      ce->ce_stat_data.sd_dev   = ntoh_l(ondisk->dev);
 -      ce->ce_stat_data.sd_ino   = ntoh_l(ondisk->ino);
 -      ce->ce_mode  = ntoh_l(ondisk->mode);
 -      ce->ce_stat_data.sd_uid   = ntoh_l(ondisk->uid);
 -      ce->ce_stat_data.sd_gid   = ntoh_l(ondisk->gid);
 -      ce->ce_stat_data.sd_size  = ntoh_l(ondisk->size);
 +      ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec);
 +      ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec);
 +      ce->ce_stat_data.sd_ctime.nsec = get_be32(&ondisk->ctime.nsec);
 +      ce->ce_stat_data.sd_mtime.nsec = get_be32(&ondisk->mtime.nsec);
 +      ce->ce_stat_data.sd_dev   = get_be32(&ondisk->dev);
 +      ce->ce_stat_data.sd_ino   = get_be32(&ondisk->ino);
 +      ce->ce_mode  = get_be32(&ondisk->mode);
 +      ce->ce_stat_data.sd_uid   = get_be32(&ondisk->uid);
 +      ce->ce_stat_data.sd_gid   = get_be32(&ondisk->gid);
 +      ce->ce_stat_data.sd_size  = get_be32(&ondisk->size);
        ce->ce_flags = flags & ~CE_NAMEMASK;
        ce->ce_namelen = len;
        hashcpy(ce->sha1, ondisk->sha1);
@@@ -1402,14 -1392,14 +1408,14 @@@ static struct cache_entry *create_from_
        unsigned int flags;
  
        /* On-disk flags are just 16 bits */
 -      flags = ntoh_s(ondisk->flags);
 +      flags = get_be16(&ondisk->flags);
        len = flags & CE_NAMEMASK;
  
        if (flags & CE_EXTENDED) {
                struct ondisk_cache_entry_extended *ondisk2;
                int extended_flags;
                ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
 -              extended_flags = ntoh_s(ondisk2->flags2) << 16;
 +              extended_flags = get_be16(&ondisk2->flags2) << 16;
                /* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
                if (extended_flags & ~CE_EXTENDED_FLAGS)
                        die("Unknown index entry format %08x", extended_flags);
@@@ -1477,7 -1467,6 +1483,7 @@@ int read_index_from(struct index_state 
        if (verify_hdr(hdr, mmap_size) < 0)
                goto unmap;
  
 +      hashcpy(istate->sha1, (unsigned char *)hdr + mmap_size - 20);
        istate->version = ntohl(hdr->hdr_version);
        istate->cache_nr = ntohl(hdr->hdr_entries);
        istate->cache_alloc = alloc_nr(istate->cache_nr);
@@@ -1761,50 -1750,6 +1767,50 @@@ static int ce_write_entry(git_SHA_CTX *
        return result;
  }
  
 +/*
 + * This function verifies if index_state has the correct sha1 of the
 + * index file.  Don't die if we have any other failure, just return 0.
 + */
 +static int verify_index_from(const struct index_state *istate, const char *path)
 +{
 +      int fd;
 +      ssize_t n;
 +      struct stat st;
 +      unsigned char sha1[20];
 +
 +      if (!istate->initialized)
 +              return 0;
 +
 +      fd = open(path, O_RDONLY);
 +      if (fd < 0)
 +              return 0;
 +
 +      if (fstat(fd, &st))
 +              goto out;
 +
 +      if (st.st_size < sizeof(struct cache_header) + 20)
 +              goto out;
 +
 +      n = pread_in_full(fd, sha1, 20, st.st_size - 20);
 +      if (n != 20)
 +              goto out;
 +
 +      if (hashcmp(istate->sha1, sha1))
 +              goto out;
 +
 +      close(fd);
 +      return 1;
 +
 +out:
 +      close(fd);
 +      return 0;
 +}
 +
 +static int verify_index(const struct index_state *istate)
 +{
 +      return verify_index_from(istate, get_index_file());
 +}
 +
  static int has_racy_timestamp(struct index_state *istate)
  {
        int entries = istate->cache_nr;
  void update_index_if_able(struct index_state *istate, struct lock_file *lockfile)
  {
        if ((istate->cache_changed || has_racy_timestamp(istate)) &&
 -          !write_index(istate, lockfile->fd))
 +          verify_index(istate) && !write_index(istate, lockfile->fd))
                commit_locked_index(lockfile);
        else
                rollback_lock_file(lockfile);
@@@ -1853,7 -1798,7 +1859,7 @@@ int write_index(struct index_state *ist
        }
  
        if (!istate->version)
 -              istate->version = INDEX_FORMAT_DEFAULT;
 +              istate->version = get_index_format_default();
  
        /* demote version 3 to version 2 when the latter suffices */
        if (istate->version == 3 || istate->version == 2)
@@@ -1952,7 -1897,7 +1958,7 @@@ int read_index_unmerged(struct index_st
                new_ce->ce_mode = ce->ce_mode;
                if (add_index_entry(istate, new_ce, 0))
                        return error("%s: cannot drop to stage #0",
 -                                   ce->name);
 +                                   new_ce->name);
                i = index_name_pos(istate, new_ce->name, len);
        }
        return unmerged;
diff --combined t/test-lib.sh
index c081668dfe16c85dda29bace564f7c1c3da77cb0,afa411e128d9778970b2b2d416605d3c7e86e21a..e0266f03570ad969ac0d82230b1be75c9ab3ff2b
@@@ -108,12 -108,6 +108,12 @@@ export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAM
  export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
  export EDITOR
  
 +if test -n "${TEST_GIT_INDEX_VERSION:+isset}"
 +then
 +      GIT_INDEX_VERSION="$TEST_GIT_INDEX_VERSION"
 +      export GIT_INDEX_VERSION
 +fi
 +
  # Add libc MALLOC and MALLOC_PERTURB test
  # only if we are not executing the test with valgrind
  if expr " $GIT_TEST_OPTS " : ".* --valgrind " >/dev/null ||
@@@ -164,7 -158,11 +164,11 @@@ _z40=0000000000000000000000000000000000
  LF='
  '
  
- export _x05 _x40 _z40 LF
+ # UTF-8 ZERO WIDTH NON-JOINER, which HFS+ ignores
+ # when case-folding filenames
+ u200c=$(printf '\342\200\214')
+ export _x05 _x40 _z40 LF u200c
  
  # Each test should start with something like this, after copyright notices:
  #
@@@ -655,6 -653,7 +659,6 @@@ else # normal case, use ../bin-wrapper
        fi
  fi
  GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt
 -unset GIT_CONFIG
  GIT_CONFIG_NOSYSTEM=1
  GIT_ATTR_NOSYSTEM=1
  export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_ATTR_NOSYSTEM
diff --combined unpack-trees.c
index 97fc9954672f2c374541cad827bb5c459abebcdc,ca7dd0fa5fee1baeb18f5134fced98c933f585b1..02f69aeea3dc2e60336fa4ac018ec14ae10f911e
@@@ -102,17 -102,18 +102,17 @@@ void setup_unpack_trees_porcelain(struc
                opts->unpack_rejects[i].strdup_strings = 1;
  }
  
- static void do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
+ static int do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
                         unsigned int set, unsigned int clear)
  {
 -      clear |= CE_HASHED | CE_UNHASHED;
 +      clear |= CE_HASHED;
  
        if (set & CE_REMOVE)
                set |= CE_WT_REMOVE;
  
 -      ce->next = NULL;
        ce->ce_flags = (ce->ce_flags & ~clear) | set;
-       add_index_entry(&o->result, ce,
-                       ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
+       return add_index_entry(&o->result, ce,
+                              ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
  }
  
  static struct cache_entry *dup_entry(const struct cache_entry *ce)
@@@ -203,7 -204,7 +203,7 @@@ static int check_updates(struct unpack_
                                total++;
                }
  
 -              progress = start_progress_delay("Checking out files",
 +              progress = start_progress_delay(_("Checking out files"),
                                                total, 50, 1);
                cnt = 0;
        }
@@@ -607,7 -608,9 +607,9 @@@ static int unpack_nondirectories(int n
  
        for (i = 0; i < n; i++)
                if (src[i] && src[i] != o->df_conflict_entry)
-                       do_add_entry(o, src[i], 0, 0);
+                       if (do_add_entry(o, src[i], 0, 0))
+                               return -1;
        return 0;
  }
  
diff --combined utf8.c
index 77c28d492cccfcbcb8a302d168a81b21d909ef12,015c8157eda650450f8dfc9bb744959056d10e9b..a89704017a0997fd93ba14f198eb3e199d0f4274
--- 1/utf8.c
--- 2/utf8.c
+++ b/utf8.c
@@@ -5,8 -5,8 +5,8 @@@
  /* This code is originally from http://www.cl.cam.ac.uk/~mgk25/ucs/ */
  
  struct interval {
 -  int first;
 -  int last;
 +      ucs_char_t first;
 +      ucs_char_t last;
  };
  
  size_t display_mode_esc_sequence_len(const char *s)
@@@ -528,7 -528,7 +528,7 @@@ char *reencode_string_iconv(const char 
        while (1) {
                size_t cnt = iconv(conv, &cp, &insz, &outpos, &outsz);
  
 -              if (cnt == -1) {
 +              if (cnt == (size_t) -1) {
                        size_t sofar;
                        if (errno != E2BIG) {
                                free(out);
@@@ -627,3 -627,67 +627,67 @@@ int mbs_chrlen(const char **text, size_
  
        return chrlen;
  }
+ /*
+  * Pick the next char from the stream, folding as an HFS+ filename comparison
+  * would. Note that this is _not_ complete by any means. It's just enough
+  * to make is_hfs_dotgit() work, and should not be used otherwise.
+  */
+ static ucs_char_t next_hfs_char(const char **in)
+ {
+       while (1) {
+               ucs_char_t out = pick_one_utf8_char(in, NULL);
+               /*
+                * check for malformed utf8. Technically this
+                * gets converted to a percent-sequence, but
+                * returning 0 is good enough for is_hfs_dotgit
+                * to realize it cannot be .git
+                */
+               if (!*in)
+                       return 0;
+               /* these code points are ignored completely */
+               switch (out) {
+               case 0x200c: /* ZERO WIDTH NON-JOINER */
+               case 0x200d: /* ZERO WIDTH JOINER */
+               case 0x200e: /* LEFT-TO-RIGHT MARK */
+               case 0x200f: /* RIGHT-TO-LEFT MARK */
+               case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */
+               case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */
+               case 0x202c: /* POP DIRECTIONAL FORMATTING */
+               case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */
+               case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */
+               case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */
+               case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */
+               case 0x206c: /* INHIBIT ARABIC FORM SHAPING */
+               case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */
+               case 0x206e: /* NATIONAL DIGIT SHAPES */
+               case 0x206f: /* NOMINAL DIGIT SHAPES */
+               case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */
+                       continue;
+               }
+               /*
+                * there's a great deal of other case-folding that occurs,
+                * but this is enough to catch anything that will convert
+                * to ".git"
+                */
+               return tolower(out);
+       }
+ }
+ int is_hfs_dotgit(const char *path)
+ {
+       ucs_char_t c;
+       if (next_hfs_char(&path) != '.' ||
+           next_hfs_char(&path) != 'g' ||
+           next_hfs_char(&path) != 'i' ||
+           next_hfs_char(&path) != 't')
+               return 0;
+       c = next_hfs_char(&path);
+       if (c && !is_dir_sep(c))
+               return 0;
+       return 1;
+ }