Merge branch 'mk/maint-parse-careful'
authorJunio C Hamano <gitster@pobox.com>
Sun, 2 Mar 2008 23:11:07 +0000 (15:11 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 2 Mar 2008 23:11:07 +0000 (15:11 -0800)
* mk/maint-parse-careful:
receive-pack: use strict mode for unpacking objects
index-pack: introduce checking mode
unpack-objects: prevent writing of inconsistent objects
unpack-object: cache for non written objects
add common fsck error printing function
builtin-fsck: move common object checking code to fsck.c
builtin-fsck: reports missing parent commits
Remove unused object-ref code
builtin-fsck: move away from object-refs to fsck_walk
add generic, type aware object chain walker

Conflicts:

Makefile
builtin-fsck.c

12 files changed:
1  2 
Documentation/config.txt
Makefile
builtin-fetch-pack.c
builtin-fsck.c
builtin-pack-objects.c
builtin-rev-list.c
cache.h
commit.h
receive-pack.c
tag.c
tree.c
upload-pack.c
diff --combined Documentation/config.txt
index 4027726f2ee66ebad69412a5c8c6d1aef7f7463f,72d3a345ecbc43c0002011fd59ec6e3bb197dadd..2091caa111a5a25ee983198437fd25aa52999b51
@@@ -139,51 -139,6 +139,51 @@@ core.autocrlf:
        "text" (i.e. be subjected to the autocrlf mechanism) is
        decided purely based on the contents.
  
 +core.safecrlf::
 +      If true, makes git check if converting `CRLF` as controlled by
 +      `core.autocrlf` is reversible.  Git will verify if a command
 +      modifies a file in the work tree either directly or indirectly.
 +      For example, committing a file followed by checking out the
 +      same file should yield the original file in the work tree.  If
 +      this is not the case for the current setting of
 +      `core.autocrlf`, git will reject the file.  The variable can
 +      be set to "warn", in which case git will only warn about an
 +      irreversible conversion but continue the operation.
 ++
 +CRLF conversion bears a slight chance of corrupting data.
 +autocrlf=true will convert CRLF to LF during commit and LF to
 +CRLF during checkout.  A file that contains a mixture of LF and
 +CRLF before the commit cannot be recreated by git.  For text
 +files this is the right thing to do: it corrects line endings
 +such that we have only LF line endings in the repository.
 +But for binary files that are accidentally classified as text the
 +conversion can corrupt data.
 ++
 +If you recognize such corruption early you can easily fix it by
 +setting the conversion type explicitly in .gitattributes.  Right
 +after committing you still have the original file in your work
 +tree and this file is not yet corrupted.  You can explicitly tell
 +git that this file is binary and git will handle the file
 +appropriately.
 ++
 +Unfortunately, the desired effect of cleaning up text files with
 +mixed line endings and the undesired effect of corrupting binary
 +files cannot be distinguished.  In both cases CRLFs are removed
 +in an irreversible way.  For text files this is the right thing
 +to do because CRLFs are line endings, while for binary files
 +converting CRLFs corrupts data.
 ++
 +Note, this safety check does not mean that a checkout will generate a
 +file identical to the original file for a different setting of
 +`core.autocrlf`, but only for the current one.  For example, a text
 +file with `LF` would be accepted with `core.autocrlf=input` and could
 +later be checked out with `core.autocrlf=true`, in which case the
 +resulting file would contain `CRLF`, although the original file
 +contained `LF`.  However, in both work trees the line endings would be
 +consistent, that is either all `LF` or all `CRLF`, but never mixed.  A
 +file with mixed line endings would be reported by the `core.safecrlf`
 +mechanism.
 +
  core.symlinks::
        If false, symbolic links are checked out as small plain files that
        contain the link text. linkgit:git-update-index[1] and
@@@ -353,10 -308,6 +353,10 @@@ core.whitespace:
    error (enabled by default).
  * `indent-with-non-tab` treats a line that is indented with 8 or more
    space characters as an error (not enabled by default).
 +* `cr-at-eol` treats a carriage-return at the end of line as
 +  part of the line terminator, i.e. with it, `trailing-space`
 +  does not trigger if the character before such a carriage-return
 +  is not a whitespace (not enabled by default).
  
  alias.*::
        Command aliases for the linkgit:git[1] command wrapper - e.g.
@@@ -379,14 -330,10 +379,14 @@@ apply.whitespace:
  
  branch.autosetupmerge::
        Tells `git-branch` and `git-checkout` to setup new branches
 -      so that linkgit:git-pull[1] will appropriately merge from that
 -      remote branch.  Note that even if this option is not set,
 +      so that linkgit:git-pull[1] will appropriately merge from the
 +      starting point branch. Note that even if this option is not set,
        this behavior can be chosen per-branch using the `--track`
 -      and `--no-track` options.  This option defaults to true.
 +      and `--no-track` options. The valid settings are: `false` -- no
 +      automatic setup is done; `true` -- automatic setup is done when the
 +      starting point is a remote branch; `always` -- automatic setup is
 +      done when the starting point is either a local branch or remote
 +      branch. This option defaults to true.
  
  branch.<name>.remote::
        When in branch <name>, it tells `git fetch` which remote to fetch.
@@@ -497,13 -444,6 +497,13 @@@ color.status.<slot>:
  commit.template::
        Specify a file to use as the template for new commit messages.
  
 +color.ui::
 +      When set to `always`, always use colors in all git commands which
 +      are capable of colored output. When false (or `never`), never. When
 +      set to `true` or `auto`, use colors only when the output is to the
 +      terminal. When more specific variables of color.* are set, they always
 +      take precedence over this setting. Defaults to false.
 +
  diff.autorefreshindex::
        When using `git diff` to compare with work tree
        files, do not consider stat-only change as changed.
@@@ -816,8 -756,6 +816,8 @@@ pack.threads:
        warning. This is meant to reduce packing time on multiprocessor
        machines. The required amount of memory for the delta search window
        is however multiplied by the number of threads.
 +      Specifying 0 will cause git to auto-detect the number of CPU's
 +      and set the number of threads accordingly.
  
  pack.indexVersion::
        Specify the default pack index version.  Valid values are 1 for
        whenever the corresponding pack is larger than 2 GB.  Otherwise
        the default is 1.
  
 +pack.packSizeLimit:
 +      The default maximum size of a pack.  This setting only affects
 +      packing to a file, i.e. the git:// protocol is unaffected.  It
 +      can be overridden by the `\--max-pack-size` option of
 +      linkgit:git-repack[1].
 +
  pull.octopus::
        The default merge strategy to use when pulling multiple branches
        at once.
@@@ -903,17 -835,6 +903,17 @@@ tar.umask:
        archiving user's umask will be used instead.  See umask(2) and
        linkgit:git-archive[1].
  
 +url.<base>.insteadOf::
 +      Any URL that starts with this value will be rewritten to
 +      start, instead, with <base>. In cases where some site serves a
 +      large number of repositories, and serves them with multiple
 +      access methods, and some users need to use different access
 +      methods, this feature allows people to specify any of the
 +      equivalent URLs and have git automatically rewrite the URL to
 +      the best alternative for the particular user, even for a
 +      never-before-seen repository on the site.  When more than one
 +      insteadOf strings match a given URL, the longest match is used.
 +
  user.email::
        Your email address to be recorded in any newly created commits.
        Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
@@@ -939,6 -860,12 +939,12 @@@ imap:
        The configuration variables in the 'imap' section are described
        in linkgit:git-imap-send[1].
  
+ receive.fsckObjects::
+       If it is set to true, git-receive-pack will check all received
+       objects. It will abort in the case of a malformed object or a
+       broken link. The result of an abort are only dangling objects.
+       The default value is true.
  receive.unpackLimit::
        If the number of objects received in a push is below this
        limit then the objects will be unpacked into loose object
diff --combined Makefile
index c2f84a76f62171fe32ffe1770a10721f516f8169,8ff0c772f1f21e230f08175c2aff704b507e64b9..71f01d16b1a42d366b04f1fce621db4dac6b1976
+++ b/Makefile
@@@ -3,9 -3,6 +3,9 @@@ all:
  
  # Define V=1 to have a more verbose compile.
  #
 +# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds
 +# when attempting to read from an fopen'ed directory.
 +#
  # Define NO_OPENSSL environment variable if you do not have OpenSSL.
  # This also implies MOZILLA_SHA1.
  #
  # Define THREADED_DELTA_SEARCH if you have pthreads and wish to exploit
  # parallel delta searching when packing objects.
  #
 +# Define INTERNAL_QSORT to use Git's implementation of qsort(), which
 +# is a simplified version of the merge sort used in glibc. This is
 +# recommended if Git triggers O(n^2) behavior in your platform's qsort().
 +#
  
  GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@@ -226,7 -219,7 +226,7 @@@ BASIC_CFLAGS 
  BASIC_LDFLAGS =
  
  SCRIPT_SH = \
 -      git-bisect.sh git-checkout.sh \
 +      git-bisect.sh \
        git-clone.sh \
        git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
        git-pull.sh git-rebase.sh git-rebase--interactive.sh \
        git-lost-found.sh git-quiltimport.sh git-submodule.sh \
        git-filter-branch.sh \
        git-stash.sh \
 -      git-help--browse.sh
 +      git-web--browse.sh
  
  SCRIPT_PERL = \
        git-add--interactive.perl \
@@@ -265,23 -258,23 +265,23 @@@ PROGRAMS = 
        git-upload-pack$X \
        git-pack-redundant$X git-var$X \
        git-merge-tree$X git-imap-send$X \
 -      git-merge-recursive$X \
        $(EXTRA_PROGRAMS)
  
  # Empty...
  EXTRA_PROGRAMS =
  
 +# List built-in command $C whose implementation cmd_$C() is not in
 +# builtin-$C.o but is linked in as part of some other command.
  BUILT_INS = \
        git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
        git-get-tar-commit-id$X git-init$X git-repo-config$X \
        git-fsck-objects$X git-cherry-pick$X git-peek-remote$X git-status$X \
 +      git-merge-subtree$X \
        $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
  
  # what 'all' will build and 'install' will install, in gitexecdir
  ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
  
 -ALL_PROGRAMS += git-merge-subtree$X
 -
  # what 'all' will build but not install in gitexecdir
  OTHER_PROGRAMS = git$X gitweb/gitweb.cgi
  
@@@ -304,7 -297,7 +304,7 @@@ LIB_H = 
        run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
        tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
        utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
-       mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h
+       mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h fsck.h
  
  DIFF_OBJS = \
        diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@@ -319,7 -312,7 +319,7 @@@ LIB_OBJS = 
        patch-ids.o \
        object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \
        sideband.o reachable.o reflog-walk.o \
-       quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
+       quote.o read-cache.o refs.o run-command.o dir.o \
        server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
        tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
        revision.o pager.o tree-walk.o xdiff-interface.o \
        alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
        color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
        convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
 -      transport.o bundle.o walker.o parse-options.o ws.o archive.o fsck.o
 +      transport.o bundle.o walker.o parse-options.o ws.o archive.o branch.o \
-       alias.o
++      alias.o fsck.o
  
  BUILTIN_OBJS = \
        builtin-add.o \
        builtin-bundle.o \
        builtin-cat-file.o \
        builtin-check-attr.o \
 +      builtin-checkout.o \
        builtin-checkout-index.o \
        builtin-check-ref-format.o \
        builtin-clean.o \
        builtin-merge-base.o \
        builtin-merge-file.o \
        builtin-merge-ours.o \
 +      builtin-merge-recursive.o \
        builtin-mv.o \
        builtin-name-rev.o \
        builtin-pack-objects.o \
@@@ -628,10 -618,6 +628,10 @@@ endi
  ifdef NO_C99_FORMAT
        BASIC_CFLAGS += -DNO_C99_FORMAT
  endif
 +ifdef FREAD_READS_DIRECTORIES
 +      COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES
 +      COMPAT_OBJS += compat/fopen.o
 +endif
  ifdef NO_SYMLINK_HEAD
        BASIC_CFLAGS += -DNO_SYMLINK_HEAD
  endif
@@@ -736,15 -722,10 +736,15 @@@ ifdef NO_MEMME
        COMPAT_CFLAGS += -DNO_MEMMEM
        COMPAT_OBJS += compat/memmem.o
  endif
 +ifdef INTERNAL_QSORT
 +      COMPAT_CFLAGS += -DINTERNAL_QSORT
 +      COMPAT_OBJS += compat/qsort.o
 +endif
  
  ifdef THREADED_DELTA_SEARCH
        BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
        EXTLIBS += -lpthread
 +      LIB_OBJS += thread-utils.o
  endif
  
  ifeq ($(TCLTK_PATH),)
@@@ -812,7 -793,7 +812,7 @@@ export TAR INSTALL DESTDIR SHELL_PAT
  
  ### Build rules
  
 -all:: $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS)
 +all:: $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
  ifneq (,$X)
        $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$p';)
  endif
@@@ -838,10 -819,12 +838,10 @@@ git$X: git.o $(BUILTIN_OBJS) $(GITLIBS
  
  help.o: help.c common-cmds.h GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
 +              '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
                '-DGIT_MAN_PATH="$(mandir_SQ)"' \
                '-DGIT_INFO_PATH="$(infodir_SQ)"' $<
  
 -git-merge-subtree$X: git-merge-recursive$X
 -      $(QUIET_BUILT_IN)$(RM) $@ && ln git-merge-recursive$X $@
 -
  $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)$(RM) $@ && ln git$X $@
  
@@@ -856,6 -839,7 +856,6 @@@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %
            -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
 -          -e 's|@@HTMLDIR@@|$(htmldir_SQ)|g' \
            $@.sh >$@+ && \
        chmod +x $@+ && \
        mv $@+ $@
@@@ -1011,9 -995,6 +1011,9 @@@ GIT-CFLAGS: .FORCE-GIT-CFLAG
                echo "$$FLAGS" >GIT-CFLAGS; \
              fi
  
 +GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
 +      @echo SHELL_PATH=\''$(SHELL_PATH_SQ)'\' >$@
 +
  ### Detect Tck/Tk interpreter path changes
  ifndef NO_TCLTK
  TRACK_VARS = $(subst ','\'',-DTCLTK_PATH='$(TCLTK_PATH_SQ)')
@@@ -1106,7 -1087,7 +1106,7 @@@ git.spec: git.spec.i
        mv $@+ $@
  
  GIT_TARNAME=git-$(GIT_VERSION)
 -dist: git.spec git-archive configure
 +dist: git.spec git-archive$(X) configure
        ./git-archive --format=tar \
                --prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar
        @mkdir -p $(GIT_TARNAME)
@@@ -1169,11 -1150,10 +1169,11 @@@ ifndef NO_TCLT
        $(MAKE) -C gitk-git clean
        $(MAKE) -C git-gui clean
  endif
 -      $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
 +      $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
  
  .PHONY: all install clean strip
  .PHONY: .FORCE-GIT-VERSION-FILE TAGS tags cscope .FORCE-GIT-CFLAGS
 +.PHONY: .FORCE-GIT-BUILD-OPTIONS
  
  ### Check documentation
  #
diff --combined builtin-fetch-pack.c
index 5ea48ca7dbc22785f4e4f3c35f3dc48ee18f0d8a,410414d91fc5249a2152600f5a7fb5d90745942d..b23e886d757664208f22bdfebbc21e4af103f69e
@@@ -7,7 -7,6 +7,7 @@@
  #include "pack.h"
  #include "sideband.h"
  #include "fetch-pack.h"
 +#include "remote.h"
  #include "run-command.h"
  
  static int transfer_unpack_limit = -1;
@@@ -386,7 -385,6 +386,6 @@@ static int everything_local(struct ref 
        int retval;
        unsigned long cutoff = 0;
  
-       track_object_refs = 0;
        save_commit_buffer = 0;
  
        for (ref = *refs; ref; ref = ref->next) {
@@@ -538,10 -536,8 +537,10 @@@ static int get_pack(int xd[2], char **p
        cmd.git_cmd = 1;
        if (start_command(&cmd))
                die("fetch-pack: unable to fork off %s", argv[0]);
 -      if (do_keep && pack_lockfile)
 +      if (do_keep && pack_lockfile) {
                *pack_lockfile = index_pack_lockfile(cmd.out);
 +              close(cmd.out);
 +      }
  
        if (finish_command(&cmd))
                die("%s failed", argv[0]);
  }
  
  static struct ref *do_fetch_pack(int fd[2],
 +              const struct ref *orig_ref,
                int nr_match,
                char **match,
                char **pack_lockfile)
  {
 -      struct ref *ref;
 +      struct ref *ref = copy_ref_list(orig_ref);
        unsigned char sha1[20];
  
 -      get_remote_heads(fd[0], &ref, 0, NULL, 0);
        if (is_repository_shallow() && !server_supports("shallow"))
                die("Server does not support shallow clients");
        if (server_supports("multi_ack")) {
                        fprintf(stderr, "Server supports side-band\n");
                use_sideband = 1;
        }
 -      if (!ref) {
 -              packet_flush(fd[1]);
 -              die("no matching remote head");
 -      }
        if (everything_local(&ref, nr_match, match)) {
                packet_flush(fd[1]);
                goto all_done;
@@@ -649,10 -649,8 +648,10 @@@ static void fetch_pack_setup(void
  int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
  {
        int i, ret, nr_heads;
 -      struct ref *ref;
 +      struct ref *ref = NULL;
        char *dest = NULL, **heads;
 +      int fd[2];
 +      struct child_process *conn;
  
        nr_heads = 0;
        heads = NULL;
        if (!dest)
                usage(fetch_pack_usage);
  
 -      ref = fetch_pack(&args, dest, nr_heads, heads, NULL);
 +      conn = git_connect(fd, (char *)dest, args.uploadpack,
 +                         args.verbose ? CONNECT_VERBOSE : 0);
 +      if (conn) {
 +              get_remote_heads(fd[0], &ref, 0, NULL, 0);
 +
 +              ref = fetch_pack(&args, fd, conn, ref, dest, nr_heads, heads, NULL);
 +              close(fd[0]);
 +              close(fd[1]);
 +              if (finish_connect(conn))
 +                      ref = NULL;
 +      } else {
 +              ref = NULL;
 +      }
        ret = !ref;
  
 +      if (!ret && nr_heads) {
 +              /* If the heads to pull were given, we should have
 +               * consumed all of them by matching the remote.
 +               * Otherwise, 'git-fetch remote no-such-ref' would
 +               * silently succeed without issuing an error.
 +               */
 +              for (i = 0; i < nr_heads; i++)
 +                      if (heads[i] && heads[i][0]) {
 +                              error("no such remote ref %s", heads[i]);
 +                              ret = 1;
 +                      }
 +      }
        while (ref) {
                printf("%s %s\n",
                       sha1_to_hex(ref->old_sha1), ref->name);
  }
  
  struct ref *fetch_pack(struct fetch_pack_args *my_args,
 +                     int fd[], struct child_process *conn,
 +                     const struct ref *ref,
                const char *dest,
                int nr_heads,
                char **heads,
                char **pack_lockfile)
  {
 -      int i, ret;
 -      int fd[2];
 -      struct child_process *conn;
 -      struct ref *ref;
        struct stat st;
 +      struct ref *ref_cpy;
  
        fetch_pack_setup();
        memcpy(&args, my_args, sizeof(args));
                        st.st_mtime = 0;
        }
  
 -      conn = git_connect(fd, (char *)dest, args.uploadpack,
 -                          args.verbose ? CONNECT_VERBOSE : 0);
        if (heads && nr_heads)
                nr_heads = remove_duplicates(nr_heads, heads);
 -      ref = do_fetch_pack(fd, nr_heads, heads, pack_lockfile);
 -      close(fd[0]);
 -      close(fd[1]);
 -      ret = finish_connect(conn);
 -
 -      if (!ret && nr_heads) {
 -              /* If the heads to pull were given, we should have
 -               * consumed all of them by matching the remote.
 -               * Otherwise, 'git-fetch remote no-such-ref' would
 -               * silently succeed without issuing an error.
 -               */
 -              for (i = 0; i < nr_heads; i++)
 -                      if (heads[i] && heads[i][0]) {
 -                              error("no such remote ref %s", heads[i]);
 -                              ret = 1;
 -                      }
 +      if (!ref) {
 +              packet_flush(fd[1]);
 +              die("no matching remote head");
        }
 +      ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile);
  
 -      if (!ret && args.depth > 0) {
 +      if (args.depth > 0) {
                struct cache_time mtime;
                char *shallow = git_path("shallow");
                int fd;
                }
        }
  
 -      if (ret)
 -              ref = NULL;
 -
 -      return ref;
 +      return ref_cpy;
  }
diff --combined builtin-fsck.c
index cc7524be80f51bca45ef8a205900cf50135bfc5d,d545650c8ce51b0f1e7e3dd20c563e2cf4f89094..78a6e1ff7101f7bb616365e5d4c9d9cf7f8963ae
@@@ -8,6 -8,7 +8,7 @@@
  #include "pack.h"
  #include "cache-tree.h"
  #include "tree-walk.h"
+ #include "fsck.h"
  #include "parse-options.h"
  
  #define REACHABLE 0x0001
@@@ -54,13 -55,75 +55,75 @@@ static int objerror(struct object *obj
        return -1;
  }
  
- static int objwarning(struct object *obj, const char *err, ...)
+ static int fsck_error_func(struct object *obj, int type, const char *err, ...)
  {
        va_list params;
        va_start(params, err);
-       objreport(obj, "warning", err, params);
+       objreport(obj, (type == FSCK_WARN) ? "warning" : "error", err, params);
        va_end(params);
-       return -1;
+       return (type == FSCK_WARN) ? 0 : 1;
+ }
+ static int mark_object(struct object *obj, int type, void *data)
+ {
+       struct tree *tree = NULL;
+       struct object *parent = data;
+       int result;
+       if (!obj) {
+               printf("broken link from %7s %s\n",
+                          typename(parent->type), sha1_to_hex(parent->sha1));
+               printf("broken link from %7s %s\n",
+                          (type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
+               errors_found |= ERROR_REACHABLE;
+               return 1;
+       }
+       if (type != OBJ_ANY && obj->type != type)
+               objerror(parent, "wrong object type in link");
+       if (obj->flags & REACHABLE)
+               return 0;
+       obj->flags |= REACHABLE;
+       if (!obj->parsed) {
+               if (parent && !has_sha1_file(obj->sha1)) {
+                       printf("broken link from %7s %s\n",
+                                typename(parent->type), sha1_to_hex(parent->sha1));
+                       printf("              to %7s %s\n",
+                                typename(obj->type), sha1_to_hex(obj->sha1));
+                       errors_found |= ERROR_REACHABLE;
+               }
+               return 1;
+       }
+       if (obj->type == OBJ_TREE) {
+               obj->parsed = 0;
+               tree = (struct tree *)obj;
+               if (parse_tree(tree) < 0)
+                       return 1; /* error already displayed */
+       }
+       result = fsck_walk(obj, mark_object, obj);
+       if (tree) {
+               free(tree->buffer);
+               tree->buffer = NULL;
+       }
+       if (result < 0)
+               result = 1;
+       return result;
+ }
+ static void mark_object_reachable(struct object *obj)
+ {
+       mark_object(obj, OBJ_ANY, 0);
+ }
+ static int mark_used(struct object *obj, int type, void *data)
+ {
+       if (!obj)
+               return 1;
+       obj->used = 1;
+       return 0;
  }
  
  /*
   */
  static void check_reachable_object(struct object *obj)
  {
-       const struct object_refs *refs;
        /*
         * We obviously want the object to be parsed,
         * except if it was in a pack-file and we didn't
                errors_found |= ERROR_REACHABLE;
                return;
        }
-       /*
-        * Check that everything that we try to reference is also good.
-        */
-       refs = lookup_object_refs(obj);
-       if (refs) {
-               unsigned j;
-               for (j = 0; j < refs->count; j++) {
-                       struct object *ref = refs->ref[j];
-                       if (ref->parsed ||
-                           (has_sha1_file(ref->sha1)))
-                               continue;
-                       printf("broken link from %7s %s\n",
-                              typename(obj->type), sha1_to_hex(obj->sha1));
-                       printf("              to %7s %s\n",
-                              typename(ref->type), sha1_to_hex(ref->sha1));
-                       errors_found |= ERROR_REACHABLE;
-               }
-       }
  }
  
  /*
@@@ -204,230 -246,56 +246,56 @@@ static void check_connectivity(void
        }
  }
  
- /*
-  * The entries in a tree are ordered in the _path_ order,
-  * which means that a directory entry is ordered by adding
-  * a slash to the end of it.
-  *
-  * So a directory called "a" is ordered _after_ a file
-  * called "a.c", because "a/" sorts after "a.c".
-  */
- #define TREE_UNORDERED (-1)
- #define TREE_HAS_DUPS  (-2)
- static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, const char *name2)
+ static int fsck_sha1(const unsigned char *sha1)
  {
-       int len1 = strlen(name1);
-       int len2 = strlen(name2);
-       int len = len1 < len2 ? len1 : len2;
-       unsigned char c1, c2;
-       int cmp;
-       cmp = memcmp(name1, name2, len);
-       if (cmp < 0)
+       struct object *obj = parse_object(sha1);
+       if (!obj) {
+               errors_found |= ERROR_OBJECT;
+               return error("%s: object corrupt or missing",
+                            sha1_to_hex(sha1));
+       }
+       if (obj->flags & SEEN)
                return 0;
-       if (cmp > 0)
-               return TREE_UNORDERED;
-       /*
-        * Ok, the first <len> characters are the same.
-        * Now we need to order the next one, but turn
-        * a '\0' into a '/' for a directory entry.
-        */
-       c1 = name1[len];
-       c2 = name2[len];
-       if (!c1 && !c2)
-               /*
-                * git-write-tree used to write out a nonsense tree that has
-                * entries with the same name, one blob and one tree.  Make
-                * sure we do not have duplicate entries.
-                */
-               return TREE_HAS_DUPS;
-       if (!c1 && S_ISDIR(mode1))
-               c1 = '/';
-       if (!c2 && S_ISDIR(mode2))
-               c2 = '/';
-       return c1 < c2 ? 0 : TREE_UNORDERED;
- }
- static int fsck_tree(struct tree *item)
- {
-       int retval;
-       int has_full_path = 0;
-       int has_empty_name = 0;
-       int has_zero_pad = 0;
-       int has_bad_modes = 0;
-       int has_dup_entries = 0;
-       int not_properly_sorted = 0;
-       struct tree_desc desc;
-       unsigned o_mode;
-       const char *o_name;
-       const unsigned char *o_sha1;
+       obj->flags |= SEEN;
  
        if (verbose)
-               fprintf(stderr, "Checking tree %s\n",
-                               sha1_to_hex(item->object.sha1));
-       init_tree_desc(&desc, item->buffer, item->size);
-       o_mode = 0;
-       o_name = NULL;
-       o_sha1 = NULL;
-       while (desc.size) {
-               unsigned mode;
-               const char *name;
-               const unsigned char *sha1;
-               sha1 = tree_entry_extract(&desc, &name, &mode);
-               if (strchr(name, '/'))
-                       has_full_path = 1;
-               if (!*name)
-                       has_empty_name = 1;
-               has_zero_pad |= *(char *)desc.buffer == '0';
-               update_tree_entry(&desc);
-               switch (mode) {
-               /*
-                * Standard modes..
-                */
-               case S_IFREG | 0755:
-               case S_IFREG | 0644:
-               case S_IFLNK:
-               case S_IFDIR:
-               case S_IFGITLINK:
-                       break;
-               /*
-                * This is nonstandard, but we had a few of these
-                * early on when we honored the full set of mode
-                * bits..
-                */
-               case S_IFREG | 0664:
-                       if (!check_strict)
-                               break;
-               default:
-                       has_bad_modes = 1;
-               }
+               fprintf(stderr, "Checking %s %s\n",
+                       typename(obj->type), sha1_to_hex(obj->sha1));
  
-               if (o_name) {
-                       switch (verify_ordered(o_mode, o_name, mode, name)) {
-                       case TREE_UNORDERED:
-                               not_properly_sorted = 1;
-                               break;
-                       case TREE_HAS_DUPS:
-                               has_dup_entries = 1;
-                               break;
-                       default:
-                               break;
-                       }
-               }
+       if (fsck_walk(obj, mark_used, 0))
+               objerror(obj, "broken links");
+       if (fsck_object(obj, check_strict, fsck_error_func))
+               return -1;
  
-               o_mode = mode;
-               o_name = name;
-               o_sha1 = sha1;
-       }
-       free(item->buffer);
-       item->buffer = NULL;
+       if (obj->type == OBJ_TREE) {
+               struct tree *item = (struct tree *) obj;
  
-       retval = 0;
-       if (has_full_path) {
-               objwarning(&item->object, "contains full pathnames");
+               free(item->buffer);
+               item->buffer = NULL;
        }
-       if (has_empty_name) {
-               objwarning(&item->object, "contains empty pathname");
-       }
-       if (has_zero_pad) {
-               objwarning(&item->object, "contains zero-padded file modes");
-       }
-       if (has_bad_modes) {
-               objwarning(&item->object, "contains bad file modes");
-       }
-       if (has_dup_entries) {
-               retval = objerror(&item->object, "contains duplicate file entries");
-       }
-       if (not_properly_sorted) {
-               retval = objerror(&item->object, "not properly sorted");
-       }
-       return retval;
- }
  
- static int fsck_commit(struct commit *commit)
- {
-       char *buffer = commit->buffer;
-       unsigned char tree_sha1[20], sha1[20];
+       if (obj->type == OBJ_COMMIT) {
+               struct commit *commit = (struct commit *) obj;
  
-       if (verbose)
-               fprintf(stderr, "Checking commit %s\n",
-                       sha1_to_hex(commit->object.sha1));
-       if (!commit->date)
-               return objerror(&commit->object, "invalid author/committer line");
-       if (memcmp(buffer, "tree ", 5))
-               return objerror(&commit->object, "invalid format - expected 'tree' line");
-       if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n')
-               return objerror(&commit->object, "invalid 'tree' line format - bad sha1");
-       buffer += 46;
-       while (!memcmp(buffer, "parent ", 7)) {
-               if (get_sha1_hex(buffer+7, sha1) || buffer[47] != '\n')
-                       return objerror(&commit->object, "invalid 'parent' line format - bad sha1");
-               buffer += 48;
-       }
-       if (memcmp(buffer, "author ", 7))
-               return objerror(&commit->object, "invalid format - expected 'author' line");
-       free(commit->buffer);
-       commit->buffer = NULL;
-       if (!commit->tree)
-               return objerror(&commit->object, "could not load commit's tree %s", tree_sha1);
-       if (!commit->parents && show_root)
-               printf("root %s\n", sha1_to_hex(commit->object.sha1));
-       return 0;
- }
+               free(commit->buffer);
+               commit->buffer = NULL;
  
static int fsck_tag(struct tag *tag)
- {
-       struct object *tagged = tag->tagged;
              if (!commit->parents && show_root)
+                       printf("root %s\n", sha1_to_hex(commit->object.sha1));
+       }
  
-       if (verbose)
-               fprintf(stderr, "Checking tag %s\n",
-                       sha1_to_hex(tag->object.sha1));
+       if (obj->type == OBJ_TAG) {
+               struct tag *tag = (struct tag *) obj;
  
-       if (!tagged) {
-               return objerror(&tag->object, "could not load tagged object");
+               if (show_tags && tag->tagged) {
+                       printf("tagged %s %s", typename(tag->tagged->type), sha1_to_hex(tag->tagged->sha1));
+                       printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1));
+               }
        }
-       if (!show_tags)
-               return 0;
  
-       printf("tagged %s %s", typename(tagged->type), sha1_to_hex(tagged->sha1));
-       printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1));
        return 0;
  }
  
- static int fsck_sha1(const unsigned char *sha1)
- {
-       struct object *obj = parse_object(sha1);
-       if (!obj) {
-               errors_found |= ERROR_OBJECT;
-               return error("%s: object corrupt or missing",
-                            sha1_to_hex(sha1));
-       }
-       if (obj->flags & SEEN)
-               return 0;
-       obj->flags |= SEEN;
-       if (obj->type == OBJ_BLOB)
-               return 0;
-       if (obj->type == OBJ_TREE)
-               return fsck_tree((struct tree *) obj);
-       if (obj->type == OBJ_COMMIT)
-               return fsck_commit((struct commit *) obj);
-       if (obj->type == OBJ_TAG)
-               return fsck_tag((struct tag *) obj);
-       /* By now, parse_object() would've returned NULL instead. */
-       return objerror(obj, "unknown type '%d' (internal fsck error)",
-                       obj->type);
- }
  /*
   * This is the sorting chunk size: make it reasonably
   * big so that we can sort well..
@@@ -538,13 -406,13 +406,13 @@@ static int fsck_handle_reflog_ent(unsig
                obj = lookup_object(osha1);
                if (obj) {
                        obj->used = 1;
-                       mark_reachable(obj, REACHABLE);
+                       mark_object_reachable(obj);
                }
        }
        obj = lookup_object(nsha1);
        if (obj) {
                obj->used = 1;
-               mark_reachable(obj, REACHABLE);
+               mark_object_reachable(obj);
        }
        return 0;
  }
@@@ -574,7 -442,7 +442,7 @@@ static int fsck_handle_ref(const char *
                error("%s: not a commit", refname);
        default_refs++;
        obj->used = 1;
-       mark_reachable(obj, REACHABLE);
+       mark_object_reachable(obj);
  
        return 0;
  }
@@@ -660,7 -528,7 +528,7 @@@ static int fsck_cache_tree(struct cache
                              sha1_to_hex(it->sha1));
                        return 1;
                }
-               mark_reachable(obj, REACHABLE);
+               mark_object_reachable(obj);
                obj->used = 1;
                if (obj->type != OBJ_TREE)
                        err |= objerror(obj, "non-tree in cache-tree");
@@@ -693,7 -561,6 +561,6 @@@ int cmd_fsck(int argc, const char **arg
  {
        int i, heads;
  
-       track_object_refs = 1;
        errors_found = 0;
  
        argc = parse_options(argc, argv, fsck_opts, fsck_usage, 0);
                                continue;
  
                        obj->used = 1;
-                       mark_reachable(obj, REACHABLE);
+                       mark_object_reachable(obj);
                        heads++;
                        continue;
                }
                        struct blob *blob;
                        struct object *obj;
  
 -                      mode = ntohl(active_cache[i]->ce_mode);
 +                      mode = active_cache[i]->ce_mode;
                        if (S_ISGITLINK(mode))
                                continue;
                        blob = lookup_blob(active_cache[i]->sha1);
                                continue;
                        obj = &blob->object;
                        obj->used = 1;
-                       mark_reachable(obj, REACHABLE);
+                       mark_object_reachable(obj);
                }
                if (active_cache_tree)
                        fsck_cache_tree(active_cache_tree);
diff --combined builtin-pack-objects.c
index 1bba6e6a64220ef2121ce8817ccae123739f4e8e,6f8f388bdfbab6728f843802bd4e685510b1184c..6c8b662e7835501f7540c8323596d4e04e0b90d1
@@@ -16,7 -16,6 +16,7 @@@
  #include "progress.h"
  
  #ifdef THREADED_DELTA_SEARCH
 +#include "thread-utils.h"
  #include <pthread.h>
  #endif
  
@@@ -69,7 -68,7 +69,7 @@@ static int allow_ofs_delta
  static const char *base_name;
  static int progress = 1;
  static int window = 10;
 -static uint32_t pack_size_limit;
 +static uint32_t pack_size_limit, pack_size_limit_cfg;
  static int depth = 50;
  static int delta_search_threads = 1;
  static int pack_to_stdout;
@@@ -1429,7 -1428,8 +1429,7 @@@ static int try_delta(struct unpacked *t
         * accounting lock.  Compiler will optimize the strangeness
         * away when THREADED_DELTA_SEARCH is not defined.
         */
 -      if (trg_entry->delta_data)
 -              free(trg_entry->delta_data);
 +      free(trg_entry->delta_data);
        cache_lock();
        if (trg_entry->delta_data) {
                delta_cache_size -= trg_entry->delta_size;
@@@ -1852,11 -1852,11 +1852,11 @@@ static int git_pack_config(const char *
        }
        if (!strcmp(k, "pack.threads")) {
                delta_search_threads = git_config_int(k, v);
 -              if (delta_search_threads < 1)
 +              if (delta_search_threads < 0)
                        die("invalid number of threads specified (%d)",
                            delta_search_threads);
  #ifndef THREADED_DELTA_SEARCH
 -              if (delta_search_threads > 1)
 +              if (delta_search_threads != 1)
                        warning("no threads support, ignoring %s", k);
  #endif
                return 0;
                        die("bad pack.indexversion=%d", pack_idx_default_version);
                return 0;
        }
 +      if (!strcmp(k, "pack.packsizelimit")) {
 +              pack_size_limit_cfg = git_config_ulong(k, v);
 +              return 0;
 +      }
        return git_default_config(k, v);
  }
  
@@@ -2013,7 -2009,6 +2013,6 @@@ static void get_object_list(int ac, con
  
        init_revisions(&revs, NULL);
        save_commit_buffer = 0;
-       track_object_refs = 0;
        setup_revisions(ac, av, &revs, NULL);
  
        while (fgets(line, sizeof(line), stdin) != NULL) {
                        die("bad revision '%s'", line);
        }
  
 -      prepare_revision_walk(&revs);
 +      if (prepare_revision_walk(&revs))
 +              die("revision walk setup failed");
        mark_edges_uninteresting(revs.commits, &revs, show_edge);
        traverse_commit_list(&revs, show_commit, show_object);
  
@@@ -2101,7 -2095,6 +2100,7 @@@ int cmd_pack_objects(int argc, const ch
                }
                if (!prefixcmp(arg, "--max-pack-size=")) {
                        char *end;
 +                      pack_size_limit_cfg = 0;
                        pack_size_limit = strtoul(arg+16, &end, 0) * 1024 * 1024;
                        if (!arg[16] || *end)
                                usage(pack_usage);
                if (!prefixcmp(arg, "--threads=")) {
                        char *end;
                        delta_search_threads = strtoul(arg+10, &end, 0);
 -                      if (!arg[10] || *end || delta_search_threads < 1)
 +                      if (!arg[10] || *end || delta_search_threads < 0)
                                usage(pack_usage);
  #ifndef THREADED_DELTA_SEARCH
 -                      if (delta_search_threads > 1)
 +                      if (delta_search_threads != 1)
                                warning("no threads support, "
                                        "ignoring %s", arg);
  #endif
        if (pack_to_stdout != !base_name)
                usage(pack_usage);
  
 +      if (!pack_to_stdout && !pack_size_limit)
 +              pack_size_limit = pack_size_limit_cfg;
 +
        if (pack_to_stdout && pack_size_limit)
                die("--max-pack-size cannot be used to build a pack for transfer.");
  
        if (!pack_to_stdout && thin)
                die("--thin cannot be used to build an indexable pack.");
  
 +#ifdef THREADED_DELTA_SEARCH
 +      if (!delta_search_threads)      /* --threads=0 means autodetect */
 +              delta_search_threads = online_cpus();
 +#endif
 +
        prepare_packed_git();
  
        if (progress)
diff --combined builtin-rev-list.c
index 6754e7f124cd3baa263d4b4f2c7e7be453260112,14e86ceabc954eb979d56ef67eaccb6e47e56dbe..d0a14169211aa499c726a436de827ea4d328f6c2
@@@ -25,9 -25,6 +25,9 @@@ static const char rev_list_usage[] 
  "    --no-merges\n"
  "    --remove-empty\n"
  "    --all\n"
 +"    --branches\n"
 +"    --tags\n"
 +"    --remotes\n"
  "    --stdin\n"
  "    --quiet\n"
  "  ordering output:\n"
@@@ -63,8 -60,6 +63,8 @@@ static void show_commit(struct commit *
                fputs(header_prefix, stdout);
        if (commit->object.flags & BOUNDARY)
                putchar('-');
 +      else if (commit->object.flags & UNINTERESTING)
 +              putchar('^');
        else if (revs.left_right) {
                if (commit->object.flags & SYMMETRIC_LEFT)
                        putchar('<');
@@@ -89,7 -84,7 +89,7 @@@
        else
                putchar('\n');
  
 -      if (revs.verbose_header) {
 +      if (revs.verbose_header && commit->buffer) {
                struct strbuf buf;
                strbuf_init(&buf, 0);
                pretty_print_commit(revs.commit_format, commit,
@@@ -610,12 -605,10 +610,11 @@@ int cmd_rev_list(int argc, const char *
                usage(rev_list_usage);
  
        save_commit_buffer = revs.verbose_header || revs.grep_filter;
-       track_object_refs = 0;
        if (bisect_list)
                revs.limited = 1;
  
 -      prepare_revision_walk(&revs);
 +      if (prepare_revision_walk(&revs))
 +              die("revision walk setup failed");
        if (revs.tree_objects)
                mark_edges_uninteresting(revs.commits, &revs, show_edge);
  
diff --combined cache.h
index f16d341f5233b154c6fc1518ae0bd619c8f05c38,d3b898708b0c30e2de979e51e31c0a95918ea348..e23030262328761669f4704a3e3731a5e93d5b0c
+++ b/cache.h
@@@ -3,7 -3,6 +3,7 @@@
  
  #include "git-compat-util.h"
  #include "strbuf.h"
 +#include "hash.h"
  
  #include SHA1_HEADER
  #include <zlib.h>
@@@ -95,148 -94,66 +95,148 @@@ struct cache_time 
   * We save the fields in big-endian order to allow using the
   * index file over NFS transparently.
   */
 +struct ondisk_cache_entry {
 +      struct cache_time ctime;
 +      struct cache_time mtime;
 +      unsigned int dev;
 +      unsigned int ino;
 +      unsigned int mode;
 +      unsigned int uid;
 +      unsigned int gid;
 +      unsigned int size;
 +      unsigned char sha1[20];
 +      unsigned short flags;
 +      char name[FLEX_ARRAY]; /* more */
 +};
 +
  struct cache_entry {
 -      struct cache_time ce_ctime;
 -      struct cache_time ce_mtime;
 +      unsigned int ce_ctime;
 +      unsigned int ce_mtime;
        unsigned int ce_dev;
        unsigned int ce_ino;
        unsigned int ce_mode;
        unsigned int ce_uid;
        unsigned int ce_gid;
        unsigned int ce_size;
 +      unsigned int ce_flags;
        unsigned char sha1[20];
 -      unsigned short ce_flags;
 +      struct cache_entry *next;
        char name[FLEX_ARRAY]; /* more */
  };
  
  #define CE_NAMEMASK  (0x0fff)
  #define CE_STAGEMASK (0x3000)
 -#define CE_UPDATE    (0x4000)
  #define CE_VALID     (0x8000)
  #define CE_STAGESHIFT 12
  
 -#define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT))
 -#define ce_namelen(ce) (CE_NAMEMASK & ntohs((ce)->ce_flags))
 +/* In-memory only */
 +#define CE_UPDATE    (0x10000)
 +#define CE_REMOVE    (0x20000)
 +#define CE_UPTODATE  (0x40000)
 +
 +#define CE_HASHED    (0x100000)
 +#define CE_UNHASHED  (0x200000)
 +
 +/*
 + * 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, struct cache_entry *src)
 +{
 +      unsigned int state = dst->ce_flags & CE_STATE_MASK;
 +
 +      /* Don't copy hash chain and name */
 +      memcpy(dst, src, offsetof(struct cache_entry, next));
 +
 +      /* Restore the hash state */
 +      dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
 +}
 +
 +/*
 + * We don't actually *remove* it, we can just mark it invalid so that
 + * we won't find it in lookups.
 + *
 + * Not only would we have to search the lists (simple enough), but
 + * we'd also have to rehash other hash buckets in case this makes the
 + * hash bucket empty (common). So it's much better to just mark
 + * it.
 + */
 +static inline void remove_index_entry(struct cache_entry *ce)
 +{
 +      ce->ce_flags |= CE_UNHASHED;
 +}
 +
 +static inline unsigned create_ce_flags(size_t len, unsigned stage)
 +{
 +      if (len >= CE_NAMEMASK)
 +              len = CE_NAMEMASK;
 +      return (len | (stage << CE_STAGESHIFT));
 +}
 +
 +static inline size_t ce_namelen(const struct cache_entry *ce)
 +{
 +      size_t len = ce->ce_flags & CE_NAMEMASK;
 +      if (len < CE_NAMEMASK)
 +              return len;
 +      return strlen(ce->name + CE_NAMEMASK) + CE_NAMEMASK;
 +}
 +
  #define ce_size(ce) cache_entry_size(ce_namelen(ce))
 -#define ce_stage(ce) ((CE_STAGEMASK & ntohs((ce)->ce_flags)) >> CE_STAGESHIFT)
 +#define ondisk_ce_size(ce) ondisk_cache_entry_size(ce_namelen(ce))
 +#define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
 +#define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
 +#define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
  
  #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
  static inline unsigned int create_ce_mode(unsigned int mode)
  {
        if (S_ISLNK(mode))
 -              return htonl(S_IFLNK);
 +              return S_IFLNK;
        if (S_ISDIR(mode) || S_ISGITLINK(mode))
 -              return htonl(S_IFGITLINK);
 -      return htonl(S_IFREG | ce_permissions(mode));
 +              return S_IFGITLINK;
 +      return S_IFREG | ce_permissions(mode);
  }
  static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
  {
        extern int trust_executable_bit, has_symlinks;
        if (!has_symlinks && S_ISREG(mode) &&
 -          ce && S_ISLNK(ntohl(ce->ce_mode)))
 +          ce && S_ISLNK(ce->ce_mode))
                return ce->ce_mode;
        if (!trust_executable_bit && S_ISREG(mode)) {
 -              if (ce && S_ISREG(ntohl(ce->ce_mode)))
 +              if (ce && S_ISREG(ce->ce_mode))
                        return ce->ce_mode;
                return create_ce_mode(0666);
        }
        return create_ce_mode(mode);
  }
 +static inline int ce_to_dtype(const struct cache_entry *ce)
 +{
 +      unsigned ce_mode = ntohl(ce->ce_mode);
 +      if (S_ISREG(ce_mode))
 +              return DT_REG;
 +      else if (S_ISDIR(ce_mode) || S_ISGITLINK(ce_mode))
 +              return DT_DIR;
 +      else if (S_ISLNK(ce_mode))
 +              return DT_LNK;
 +      else
 +              return DT_UNKNOWN;
 +}
  #define canon_mode(mode) \
        (S_ISREG(mode) ? (S_IFREG | ce_permissions(mode)) : \
        S_ISLNK(mode) ? S_IFLNK : S_ISDIR(mode) ? S_IFDIR : S_IFGITLINK)
  
  #define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
 +#define ondisk_cache_entry_size(len) ((offsetof(struct ondisk_cache_entry,name) + (len) + 8) & ~7)
  
  struct index_state {
        struct cache_entry **cache;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct cache_tree *cache_tree;
        time_t timestamp;
 -      void *mmap;
 -      size_t mmap_size;
 +      void *alloc;
 +      unsigned name_hash_initialized : 1;
 +      struct hash_table name_hash;
  };
  
  extern struct index_state the_index;
  #define read_cache_from(path) read_index_from(&the_index, (path))
  #define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
  #define discard_cache() discard_index(&the_index)
 +#define unmerged_cache() unmerged_index(&the_index)
  #define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
  #define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))
  #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
  #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
  #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
 +#define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen))
  #endif
  
  enum object_type {
        /* 5 for future expansion */
        OBJ_OFS_DELTA = 6,
        OBJ_REF_DELTA = 7,
+       OBJ_ANY,
        OBJ_MAX,
  };
  
@@@ -347,9 -263,7 +348,9 @@@ extern int read_index(struct index_stat
  extern int read_index_from(struct index_state *, const char *path);
  extern int write_index(struct index_state *, int newfd);
  extern int discard_index(struct index_state *);
 +extern int unmerged_index(struct index_state *);
  extern int verify_path(const char *path);
 +extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
  extern int index_name_pos(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 */
@@@ -417,23 -331,6 +418,23 @@@ extern size_t packed_git_limit
  extern size_t delta_base_cache_limit;
  extern int auto_crlf;
  
 +enum safe_crlf {
 +      SAFE_CRLF_FALSE = 0,
 +      SAFE_CRLF_FAIL = 1,
 +      SAFE_CRLF_WARN = 2,
 +};
 +
 +extern enum safe_crlf safe_crlf;
 +
 +enum branch_track {
 +      BRANCH_TRACK_NEVER = 0,
 +      BRANCH_TRACK_REMOTE,
 +      BRANCH_TRACK_ALWAYS,
 +      BRANCH_TRACK_EXPLICIT,
 +};
 +
 +extern enum branch_track git_branch_track;
 +
  #define GIT_REPO_VERSION 0
  extern int repository_format_version;
  extern int check_repository_format(void);
@@@ -694,9 -591,6 +695,9 @@@ extern int git_config_set_multivar(cons
  extern int git_config_rename_section(const char *, const char *);
  extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value);
 +extern int git_env_bool(const char *, int);
 +extern int git_config_system(void);
 +extern int git_config_global(void);
  extern int config_error_nonbool(const char *);
  
  #define MAX_GITNAME (1000)
@@@ -709,7 -603,6 +710,7 @@@ extern const char *git_log_output_encod
  /* IO helper functions */
  extern void maybe_flush_or_die(FILE *, const char *);
  extern int copy_fd(int ifd, int ofd);
 +extern int copy_file(const char *dst, const char *src, int mode);
  extern int read_in_full(int fd, void *buf, size_t count);
  extern int write_in_full(int fd, const void *buf, size_t count);
  extern void write_or_die(int fd, const void *buf, size_t count);
@@@ -743,8 -636,7 +744,8 @@@ extern void trace_argv_printf(const cha
  
  /* convert.c */
  /* returns 1 if *dst was used */
 -extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst);
 +extern int convert_to_git(const char *path, const char *src, size_t len,
 +                          struct strbuf *dst, enum safe_crlf checksafe);
  extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
  
  /* add */
@@@ -763,7 -655,6 +764,7 @@@ void shift_tree(const unsigned char *, 
  #define WS_TRAILING_SPACE     01
  #define WS_SPACE_BEFORE_TAB   02
  #define WS_INDENT_WITH_NON_TAB        04
 +#define WS_CR_AT_EOL           010
  #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
  extern unsigned whitespace_rule_cfg;
  extern unsigned whitespace_rule(const char *);
@@@ -772,13 -663,10 +773,13 @@@ extern unsigned check_and_emit_line(con
      FILE *stream, const char *set,
      const char *reset, const char *ws);
  extern char *whitespace_error_string(unsigned ws);
 +extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
  
  /* ls-files */
  int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
  int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
  void overlay_tree_on_cache(const char *tree_name, const char *prefix);
  
 +char *alias_lookup(const char *alias);
 +
  #endif /* CACHE_H */
diff --combined commit.h
index 80d65b96dafe33b128e4444d81046360eeb105af,3ad3dd9af17215e2fb12c215911a863be573d50f..a1e95914263355e2021f54be4837ed25f1210757
+++ b/commit.h
@@@ -71,21 -71,6 +71,21 @@@ extern void pretty_print_commit(enum cm
                                  int abbrev, const char *subject,
                                  const char *after_subject, enum date_mode,
                                int non_ascii_present);
 +void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
 +                 const char *line, enum date_mode dmode,
 +                 const char *encoding);
 +void pp_title_line(enum cmit_fmt fmt,
 +                 const char **msg_p,
 +                 struct strbuf *sb,
 +                 const char *subject,
 +                 const char *after_subject,
 +                 const char *encoding,
 +                 int plain_non_ascii);
 +void pp_remainder(enum cmit_fmt fmt,
 +                const char **msg_p,
 +                struct strbuf *sb,
 +                int indent);
 +
  
  /** Removes the first commit from a list sorted by date, and adds all
   * of its parents.
@@@ -116,6 -101,7 +116,7 @@@ struct commit_graft 
  struct commit_graft *read_graft_line(char *buf, int len);
  int register_commit_graft(struct commit_graft *, int);
  int read_graft_file(const char *graft_file);
+ struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
  
  extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
  
diff --combined receive-pack.c
index a971433db155bbac162cece038b73dc64e682ac0,f5440ff4d439521edc39bc7a481911ebd7ce0d0c..0ca2a80bf00b76a2b07b749bb1ff7cb77d44f7ee
@@@ -10,6 -10,7 +10,7 @@@
  static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
  
  static int deny_non_fast_forwards = 0;
+ static int receive_fsck_objects = 1;
  static int receive_unpack_limit = -1;
  static int transfer_unpack_limit = -1;
  static int unpack_limit = 100;
@@@ -35,6 -36,11 +36,11 @@@ static int receive_pack_config(const ch
                return 0;
        }
  
+       if (strcmp(var, "receive.fsckobjects") == 0) {
+               receive_fsck_objects = git_config_bool(var, value);
+               return 0;
+       }
        return git_default_config(var, value);
  }
  
@@@ -132,7 -138,6 +138,7 @@@ static int run_hook(const char *hook_na
                                break;
                }
        }
 +      close(proc.in);
        return hook_status(finish_command(&proc), hook_name);
  }
  
@@@ -368,11 -373,13 +374,13 @@@ static const char *unpack(void
                        ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
  
        if (ntohl(hdr.hdr_entries) < unpack_limit) {
-               int code;
-               const char *unpacker[3];
-               unpacker[0] = "unpack-objects";
-               unpacker[1] = hdr_arg;
-               unpacker[2] = NULL;
+               int code, i = 0;
+               const char *unpacker[4];
+               unpacker[i++] = "unpack-objects";
+               if (receive_fsck_objects)
+                       unpacker[i++] = "--strict";
+               unpacker[i++] = hdr_arg;
+               unpacker[i++] = NULL;
                code = run_command_v_opt(unpacker, RUN_GIT_CMD);
                switch (code) {
                case 0:
                        return "unpacker exited with error code";
                }
        } else {
-               const char *keeper[6];
-               int s, status;
+               const char *keeper[7];
+               int s, status, i = 0;
                char keep_arg[256];
                struct child_process ip;
  
                if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
                        strcpy(keep_arg + s, "localhost");
  
-               keeper[0] = "index-pack";
-               keeper[1] = "--stdin";
-               keeper[2] = "--fix-thin";
-               keeper[3] = hdr_arg;
-               keeper[4] = keep_arg;
-               keeper[5] = NULL;
+               keeper[i++] = "index-pack";
+               keeper[i++] = "--stdin";
+               if (receive_fsck_objects)
+                       keeper[i++] = "--strict";
+               keeper[i++] = "--fix-thin";
+               keeper[i++] = hdr_arg;
+               keeper[i++] = keep_arg;
+               keeper[i++] = NULL;
                memset(&ip, 0, sizeof(ip));
                ip.argv = keeper;
                ip.out = -1;
                if (start_command(&ip))
                        return "index-pack fork failed";
                pack_lockfile = index_pack_lockfile(ip.out);
 +              close(ip.out);
                status = finish_command(&ip);
                if (!status) {
                        reprepare_packed_git();
diff --combined tag.c
index 990134fe7ab14043d40b97230571ffea109b9129,d19f56d60cb166600f9fe906a213f20a04afe22e..4470d2bf78e1fbb00d00e487f41daa4373cf48e1
--- 1/tag.c
--- 2/tag.c
+++ b/tag.c
@@@ -9,10 -9,7 +9,10 @@@ const char *tag_type = "tag"
  struct object *deref_tag(struct object *o, const char *warn, int warnlen)
  {
        while (o && o->type == OBJ_TAG)
 -              o = parse_object(((struct tag *)o)->tagged->sha1);
 +              if (((struct tag *)o)->tagged)
 +                      o = parse_object(((struct tag *)o)->tagged->sha1);
 +              else
 +                      o = NULL;
        if (!o && warn) {
                if (!warnlen)
                        warnlen = strlen(warn);
@@@ -87,12 -84,6 +87,6 @@@ int parse_tag_buffer(struct tag *item, 
                item->tagged = NULL;
        }
  
-       if (item->tagged && track_object_refs) {
-               struct object_refs *refs = alloc_object_refs(1);
-               refs->ref[0] = item->tagged;
-               set_object_refs(&item->object, refs);
-       }
        return 0;
  }
  
diff --combined tree.c
index 87708ef420aaa328c9a8dfcefbcf4aa7ba6d4b72,113e1bb0a4778f68779b010c00c26322b26db254..4b1825c2adac94ad806d729862c90cf8dfa37e9a
--- 1/tree.c
--- 2/tree.c
+++ b/tree.c
@@@ -142,8 -142,8 +142,8 @@@ static int cmp_cache_name_compare(cons
  
        ce1 = *((const struct cache_entry **)a_);
        ce2 = *((const struct cache_entry **)b_);
 -      return cache_name_compare(ce1->name, ntohs(ce1->ce_flags),
 -                                ce2->name, ntohs(ce2->ce_flags));
 +      return cache_name_compare(ce1->name, ce1->ce_flags,
 +                                ce2->name, ce2->ce_flags);
  }
  
  int read_tree(struct tree *tree, int stage, const char **match)
@@@ -202,52 -202,6 +202,6 @@@ struct tree *lookup_tree(const unsigne
        return (struct tree *) obj;
  }
  
- /*
-  * NOTE! Tree refs to external git repositories
-  * (ie gitlinks) do not count as real references.
-  *
-  * You don't have to have those repositories
-  * available at all, much less have the objects
-  * accessible from the current repository.
-  */
- static void track_tree_refs(struct tree *item)
- {
-       int n_refs = 0, i;
-       struct object_refs *refs;
-       struct tree_desc desc;
-       struct name_entry entry;
-       /* Count how many entries there are.. */
-       init_tree_desc(&desc, item->buffer, item->size);
-       while (tree_entry(&desc, &entry)) {
-               if (S_ISGITLINK(entry.mode))
-                       continue;
-               n_refs++;
-       }
-       /* Allocate object refs and walk it again.. */
-       i = 0;
-       refs = alloc_object_refs(n_refs);
-       init_tree_desc(&desc, item->buffer, item->size);
-       while (tree_entry(&desc, &entry)) {
-               struct object *obj;
-               if (S_ISGITLINK(entry.mode))
-                       continue;
-               if (S_ISDIR(entry.mode))
-                       obj = &lookup_tree(entry.sha1)->object;
-               else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
-                       obj = &lookup_blob(entry.sha1)->object;
-               else {
-                       warning("in tree %s: entry %s has bad mode %.6o\n",
-                            sha1_to_hex(item->object.sha1), entry.path, entry.mode);
-                       obj = lookup_unknown_object(entry.sha1);
-               }
-               refs->ref[i++] = obj;
-       }
-       set_object_refs(&item->object, refs);
- }
  int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
  {
        if (item->object.parsed)
        item->buffer = buffer;
        item->size = size;
  
-       if (track_object_refs)
-               track_tree_refs(item);
        return 0;
  }
  
diff --combined upload-pack.c
index b26d05331d7eeab4efea01594ecf196bc5fb50ed,ba9ba599768330838ff2f09887d93d70035bf5ca..e5421db9c52c267eb9908f8a94d28c0c601d5128
@@@ -129,8 -129,7 +129,8 @@@ static int do_rev_list(int fd, void *cr
                }
                setup_revisions(0, NULL, &revs, NULL);
        }
 -      prepare_revision_walk(&revs);
 +      if (prepare_revision_walk(&revs))
 +              die("revision walk setup failed");
        mark_edges_uninteresting(revs.commits, &revs, show_edge);
        traverse_commit_list(&revs, show_commit, show_object);
        return 0;
@@@ -393,7 -392,6 +393,6 @@@ static int get_common_commits(void
        char hex[41], last_hex[41];
        int len;
  
-       track_object_refs = 0;
        save_commit_buffer = 0;
  
        for(;;) {
@@@ -577,8 -575,7 +576,8 @@@ static int send_ref(const char *refname
        }
        if (o->type == OBJ_TAG) {
                o = deref_tag(o, refname, 0);
 -              packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
 +              if (o)
 +                      packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
        }
        return 0;
  }