Code

Merge branch 'j6t/mingw'
authorJunio C Hamano <gitster@pobox.com>
Thu, 3 Jul 2008 04:57:52 +0000 (21:57 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 3 Jul 2008 04:57:52 +0000 (21:57 -0700)
* j6t/mingw: (38 commits)
  compat/pread.c: Add a forward declaration to fix a warning
  Windows: Fix ntohl() related warnings about printf formatting
  Windows: TMP and TEMP environment variables specify a temporary directory.
  Windows: Make 'git help -a' work.
  Windows: Work around an oddity when a pipe with no reader is written to.
  Windows: Make the pager work.
  When installing, be prepared that template_dir may be relative.
  Windows: Use a relative default template_dir and ETC_GITCONFIG
  Windows: Compute the fallback for exec_path from the program invocation.
  Turn builtin_exec_path into a function.
  Windows: Use a customized struct stat that also has the st_blocks member.
  Windows: Add a custom implementation for utime().
  Windows: Add a new lstat and fstat implementation based on Win32 API.
  Windows: Implement a custom spawnve().
  Windows: Implement wrappers for gethostbyname(), socket(), and connect().
  Windows: Work around incompatible sort and find.
  Windows: Implement asynchronous functions as threads.
  Windows: Disambiguate DOS style paths from SSH URLs.
  Windows: A rudimentary poll() emulation.
  Windows: Implement start_command().
  ...

1  2 
Documentation/git.txt
Makefile
cache.h
exec_cmd.c
git-compat-util.h
git.c
help.c
setup.c
sha1_file.c

diff --combined Documentation/git.txt
index 57a3a84be1a8ce00f845c8a095d58913d0a7ef03,4e4bd6ddb18597d86dec8c5893f62bfd97ed67f1..22702c260c6aaed53dd29e2b33bb40ae1b2b5faa
@@@ -20,11 -20,11 +20,11 @@@ Git is a fast, scalable, distributed re
  unusually rich command set that provides both high-level operations
  and full access to internals.
  
 -See this linkgit:gittutorial[7][tutorial] to get started, then see
 +See linkgit:gittutorial[7] to get started, then see
  link:everyday.html[Everyday Git] for a useful minimum set of commands, and
  "man git-commandname" for documentation of each command.  CVS users may
 -also want to read linkgit:gitcvs-migration[7][CVS migration].  See
 -link:user-manual.html[Git User's Manual] for a more in-depth
 +also want to read linkgit:gitcvs-migration[7].  See
 +the link:user-manual.html[Git User's Manual] for a more in-depth
  introduction.
  
  The COMMAND is either a name of a Git command (see below) or an alias
@@@ -43,13 -43,12 +43,13 @@@ unreleased) version of git, that is ava
  branch of the `git.git` repository.
  Documentation for older releases are available here:
  
 -* link:v1.5.6/git.html[documentation for release 1.5.6]
 +* link:v1.5.6.1/git.html[documentation for release 1.5.6.1]
  
  * release notes for
 -  link:RelNotes-1.5.6.txt[1.5.6],
 +  link:RelNotes-1.5.6.1.txt[1.5.6.1].
 +  link:RelNotes-1.5.6.txt[1.5.6].
  
 -* link:v1.5.5/git.html[documentation for release 1.5.5]
 +* link:v1.5.5.4/git.html[documentation for release 1.5.5.4]
  
  * release notes for
    link:RelNotes-1.5.5.4.txt[1.5.5.4],
@@@ -58,6 -57,8 +58,6 @@@
    link:RelNotes-1.5.5.1.txt[1.5.5.1],
    link:RelNotes-1.5.5.txt[1.5.5].
  
 -* link:v1.5.5.4/git.html[documentation for release 1.5.5.4]
 -
  * link:v1.5.4.5/git.html[documentation for release 1.5.4.5]
  
  * release notes for
@@@ -81,8 -82,6 +81,8 @@@
    link:RelNotes-1.5.3.1.txt[1.5.3.1],
    link:RelNotes-1.5.3.txt[1.5.3].
  
 +* link:v1.5.2.5/git.html[documentation for release 1.5.2.5]
 +
  * release notes for
    link:RelNotes-1.5.2.5.txt[1.5.2.5],
    link:RelNotes-1.5.2.4.txt[1.5.2.4],
@@@ -182,14 -181,13 +182,14 @@@ See the references above to get starte
  probably more detail than necessary for a first-time user.
  
  The link:user-manual.html#git-concepts[git concepts chapter of the
 -user-manual] and the linkgit:gitcore-tutorial[7][Core tutorial] both provide
 +user-manual] and linkgit:gitcore-tutorial[7] both provide
  introductions to the underlying git architecture.
  
  See also the link:howto-index.html[howto] documents for some useful
  examples.
  
 -The internals are documented link:technical/api-index.html[here].
 +The internals are documented in the
 +link:technical/api-index.html[GIT API documentation].
  
  GIT COMMANDS
  ------------
@@@ -373,9 -371,10 +373,9 @@@ For a more complete list of ways to spe
  File/Directory Structure
  ------------------------
  
 -Please see the linkgit:gitrepository-layout[5][repository layout]
 -document.
 +Please see the linkgit:gitrepository-layout[5] document.
  
 -Read linkgit:githooks[5][hooks] for more details about each hook.
 +Read linkgit:githooks[5] for more details about each hook.
  
  Higher level SCMs may provide and manage additional information in the
  `$GIT_DIR`.
  
  Terminology
  -----------
 -Please see the linkgit:gitglossary[7][glossary] document.
 +Please see linkgit:gitglossary[7].
  
  
  Environment Variables
@@@ -410,9 -409,9 +410,9 @@@ git so take care if using Cogito etc
  'GIT_ALTERNATE_OBJECT_DIRECTORIES'::
        Due to the immutable nature of git objects, old objects can be
        archived into shared, read-only directories. This variable
-       specifies a ":" separated list of git object directories which
-       can be used to search for git objects. New objects will not be
-       written to these directories.
+       specifies a ":" separated (on Windows ";" separated) list
+       of git object directories which can be used to search for git
+       objects. New objects will not be written to these directories.
  
  'GIT_DIR'::
        If the 'GIT_DIR' environment variable is set then it
@@@ -483,10 -482,10 +483,10 @@@ othe
        a pager.
  
  'GIT_SSH'::
 -      If this environment variable is set then linkgit:git-fetch[1]
 -      and linkgit:git-push[1] will use this command instead
 +      If this environment variable is set then `git-fetch`
 +      and `git-push` will use this command instead
        of `ssh` when they need to connect to a remote system.
 -      The 'GIT_SSH' command will be given exactly two arguments:
 +      The '$GIT_SSH' command will be given exactly two arguments:
        the 'username@host' (or just 'host') from the URL and the
        shell command to execute on that remote system.
  +
@@@ -500,8 -499,8 +500,8 @@@ for further details
  
  'GIT_FLUSH'::
        If this environment variable is set to "1", then commands such
 -      as git-blame (in incremental mode), git-rev-list, git-log,
 -      git-whatchanged, etc., will force a flush of the output stream
 +      as `git-blame` (in incremental mode), `git-rev-list`, `git-log`,
 +      and `git-whatchanged` will force a flush of the output stream
        after each commit-oriented record have been flushed.   If this
        variable is set to "0", the output of these commands will be done
        using completely buffered I/O.   If this environment variable is
@@@ -527,7 -526,7 +527,7 @@@ Discussion[[Discussion]
  
  More detail on the following is available from the
  link:user-manual.html#git-concepts[git concepts chapter of the
 -user-manual] and the linkgit:gitcore-tutorial[7][Core tutorial].
 +user-manual] and linkgit:gitcore-tutorial[7].
  
  A git project normally consists of a working directory with a ".git"
  subdirectory at the top level.  The .git directory contains, among other
@@@ -593,7 -592,7 +593,7 @@@ SEE ALS
  linkgit:gittutorial[7], linkgit:gittutorial-2[7],
  linkgit:giteveryday[7], linkgit:gitcvs-migration[7],
  linkgit:gitglossary[7], linkgit:gitcore-tutorial[7],
 -link:user-manual.html[The Git User's Manual]
 +linkgit:gitcli[7], link:user-manual.html[The Git User's Manual]
  
  GIT
  ---
diff --combined Makefile
index ba16f267932ef4bd270b17e789851c12ef70785e,a2a19a85f0188855aca5dbc09b67dc4d8fa399f5..78e08d37459bd3804b9c05e8b3bcf621e0cd0c5f
+++ b/Makefile
@@@ -174,7 -174,7 +174,7 @@@ prefix = $(HOME
  bindir = $(prefix)/bin
  mandir = $(prefix)/share/man
  infodir = $(prefix)/share/info
 -gitexecdir = $(bindir)
 +gitexecdir = $(prefix)/libexec/git-core
  sharedir = $(prefix)/share
  template_dir = $(sharedir)/git-core/templates
  htmldir=$(sharedir)/doc/git-doc
@@@ -205,7 -205,7 +205,7 @@@ GITWEB_FAVICON = git-favicon.pn
  GITWEB_SITE_HEADER =
  GITWEB_SITE_FOOTER =
  
- export prefix bindir gitexecdir sharedir template_dir htmldir sysconfdir
+ export prefix bindir gitexecdir sharedir htmldir sysconfdir
  
  CC = gcc
  AR = ar
@@@ -273,11 -273,9 +273,9 @@@ EXTRA_PROGRAMS 
  
  # ... and all the rest that could be moved out of bindir to gitexecdir
  PROGRAMS += $(EXTRA_PROGRAMS)
- PROGRAMS += git-daemon$X
  PROGRAMS += git-fast-import$X
  PROGRAMS += git-fetch-pack$X
  PROGRAMS += git-hash-object$X
- PROGRAMS += git-imap-send$X
  PROGRAMS += git-index-pack$X
  PROGRAMS += git-merge-index$X
  PROGRAMS += git-merge-tree$X
@@@ -337,6 -335,7 +335,7 @@@ LIB_H += builtin.
  LIB_H += cache.h
  LIB_H += cache-tree.h
  LIB_H += commit.h
+ LIB_H += compat/mingw.h
  LIB_H += csum-file.h
  LIB_H += decorate.h
  LIB_H += delta.h
@@@ -354,7 -353,6 +353,7 @@@ LIB_H += log-tree.
  LIB_H += mailmap.h
  LIB_H += object.h
  LIB_H += pack.h
 +LIB_H += pack-refs.h
  LIB_H += pack-revindex.h
  LIB_H += parse-options.h
  LIB_H += patch-ids.h
@@@ -378,7 -376,6 +377,7 @@@ LIB_H += unpack-trees.
  LIB_H += utf8.h
  LIB_H += wt-status.h
  
 +LIB_OBJS += abspath.o
  LIB_OBJS += alias.o
  LIB_OBJS += alloc.o
  LIB_OBJS += archive.o
@@@ -431,7 -428,6 +430,7 @@@ LIB_OBJS += merge-file.
  LIB_OBJS += name-hash.o
  LIB_OBJS += object.o
  LIB_OBJS += pack-check.o
 +LIB_OBJS += pack-refs.o
  LIB_OBJS += pack-revindex.o
  LIB_OBJS += pack-write.o
  LIB_OBJS += pager.o
@@@ -470,7 -466,6 +469,7 @@@ LIB_OBJS += unpack-trees.
  LIB_OBJS += usage.o
  LIB_OBJS += utf8.o
  LIB_OBJS += walker.o
 +LIB_OBJS += wrapper.o
  LIB_OBJS += write_or_die.o
  LIB_OBJS += ws.o
  LIB_OBJS += wt-status.o
@@@ -717,6 -712,36 +716,36 @@@ ifeq ($(uname_S),HP-UX
        NO_HSTRERROR = YesPlease
        NO_SYS_SELECT_H = YesPlease
  endif
+ ifneq (,$(findstring MINGW,$(uname_S)))
+       NO_MMAP = YesPlease
+       NO_PREAD = YesPlease
+       NO_OPENSSL = YesPlease
+       NO_CURL = YesPlease
+       NO_SYMLINK_HEAD = YesPlease
+       NO_IPV6 = YesPlease
+       NO_SETENV = YesPlease
+       NO_UNSETENV = YesPlease
+       NO_STRCASESTR = YesPlease
+       NO_STRLCPY = YesPlease
+       NO_MEMMEM = YesPlease
+       NEEDS_LIBICONV = YesPlease
+       OLD_ICONV = YesPlease
+       NO_C99_FORMAT = YesPlease
+       NO_STRTOUMAX = YesPlease
+       NO_MKDTEMP = YesPlease
+       SNPRINTF_RETURNS_BOGUS = YesPlease
+       NO_SVN_TESTS = YesPlease
+       NO_PERL_MAKEMAKER = YesPlease
+       NO_POSIX_ONLY_PROGRAMS = YesPlease
+       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat
+       COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
+       COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
+       COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o
+       EXTLIBS += -lws2_32
+       X = .exe
+       template_dir = ../share/git-core/templates/
+       ETC_GITCONFIG = ../etc/gitconfig
+ endif
  ifneq (,$(findstring arm,$(uname_M)))
        ARM_SHA1 = YesPlease
  endif
@@@ -777,6 -802,10 +806,10 @@@ ifdef ZLIB_PAT
  endif
  EXTLIBS += -lz
  
+ ifndef NO_POSIX_ONLY_PROGRAMS
+       PROGRAMS += git-daemon$X
+       PROGRAMS += git-imap-send$X
+ endif
  ifndef NO_OPENSSL
        OPENSSL_LIBSSL = -lssl
        ifdef OPENSSLDIR
@@@ -1268,11 -1297,18 +1301,18 @@@ remove-dashes
  
  ### Installation rules
  
+ ifeq ($(firstword $(subst /, ,$(template_dir))),..)
+ template_instdir = $(gitexecdir)/$(template_dir)
+ else
+ template_instdir = $(template_dir)
+ endif
+ export template_instdir
  install: all
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexecdir_SQ)'
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
 -      $(INSTALL) git$X '$(DESTDIR_SQ)$(bindir_SQ)'
 +      $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
  ifndef NO_TCLTK
@@@ -1290,14 -1326,10 +1330,14 @@@ endi
  ifneq (,$X)
        $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';)
  endif
 +      ./check_bindir 'z$(bindir_SQ)' 'z$(gitexecdir_SQ)' '$(DESTDIR_SQ)$(bindir_SQ)/git-shell$X'
  
  install-doc:
        $(MAKE) -C Documentation install
  
 +install-html:
 +      $(MAKE) -C Documentation install-html
 +
  install-info:
        $(MAKE) -C Documentation install-info
  
diff --combined cache.h
index 188428dd26b7d9bf8dc0b9cd1a02ebca6d459400,72544de4ce22c7b9b5cf6d0da786a243992ae5ce..35a91320a50d3f0f720e4fc3672f7667934e04f9
+++ b/cache.h
@@@ -311,6 -311,7 +311,6 @@@ extern char *git_work_tree_cfg
  extern int is_inside_work_tree(void);
  extern const char *get_git_dir(void);
  extern char *get_object_directory(void);
 -extern char *get_refs_directory(void);
  extern char *get_index_file(void);
  extern char *get_graft_file(void);
  extern int set_git_dir(const char *path);
@@@ -434,7 -435,6 +434,7 @@@ extern size_t packed_git_window_size
  extern size_t packed_git_limit;
  extern size_t delta_base_cache_limit;
  extern int auto_crlf;
 +extern int fsync_object_files;
  
  enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
@@@ -518,15 -518,13 +518,15 @@@ enum sharedrepo 
  int git_config_perm(const char *var, const char *value);
  int adjust_shared_perm(const char *path);
  int safe_create_leading_directories(char *path);
 +int safe_create_leading_directories_const(const char *path);
  char *enter_repo(char *path, int strict);
  static inline int is_absolute_path(const char *path)
  {
-       return path[0] == '/';
+       return path[0] == '/' || has_dos_drive_prefix(path);
  }
  const char *make_absolute_path(const char *path);
  const char *make_nonrelative_path(const char *path);
 +const char *make_relative_path(const char *abs, const char *base);
  
  /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
  extern int sha1_object_info(const unsigned char *, unsigned long *);
@@@ -643,8 -641,6 +643,8 @@@ extern struct packed_git 
        const void *index_data;
        size_t index_size;
        uint32_t num_objects;
 +      uint32_t num_bad_objects;
 +      unsigned char *bad_object_sha1;
        int index_version;
        time_t mtime;
        int pack_fd;
@@@ -713,7 -709,6 +713,7 @@@ extern void close_pack_windows(struct p
  extern void unuse_pack(struct pack_window **);
  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 *);
  extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
  extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
@@@ -740,6 -735,7 +740,6 @@@ 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, void *cb);
 -extern int git_env_bool(const char *, int);
  extern int git_config_system(void);
  extern int git_config_global(void);
  extern int config_error_nonbool(const char *);
@@@ -819,11 -815,11 +819,11 @@@ void shift_tree(const unsigned char *, 
  extern unsigned whitespace_rule_cfg;
  extern unsigned whitespace_rule(const char *);
  extern unsigned parse_whitespace_rule(const char *);
 -extern unsigned check_and_emit_line(const char *line, int len, unsigned ws_rule,
 -    FILE *stream, const char *set,
 -    const char *reset, const char *ws);
 +extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
 +extern void ws_check_emit(const char *line, int len, unsigned ws_rule, 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 *);
 +extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
  
  /* ls-files */
  int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
diff --combined exec_cmd.c
index 0f8f4b5b7d9dea4458b9944dd81cf98c2e9a1322,84db7ee664e78c012221026613c141e126b64bcf..da04efe951e5b8dc4e7b72246212d8ebabc09ec6
@@@ -4,9 -4,42 +4,42 @@@
  #define MAX_ARGS      32
  
  extern char **environ;
- static const char *builtin_exec_path = GIT_EXEC_PATH;
  static const char *argv_exec_path;
  
+ static const char *builtin_exec_path(void)
+ {
+ #ifndef __MINGW32__
+       return GIT_EXEC_PATH;
+ #else
+       int len;
+       char *p, *q, *sl;
+       static char *ep;
+       if (ep)
+               return ep;
+       len = strlen(_pgmptr);
+       if (len < 2)
+               return ep = ".";
+       p = ep = xmalloc(len+1);
+       q = _pgmptr;
+       sl = NULL;
+       /* copy program name, turn '\\' into '/', skip last part */
+       while ((*p = *q)) {
+               if (*q == '\\' || *q == '/') {
+                       *p = '/';
+                       sl = p;
+               }
+               p++, q++;
+       }
+       if (sl)
+               *sl = '\0';
+       else
+               ep[0] = '.', ep[1] = '\0';
+       return ep;
+ #endif
+ }
  void git_set_argv_exec_path(const char *exec_path)
  {
        argv_exec_path = exec_path;
@@@ -26,7 -59,7 +59,7 @@@ const char *git_exec_path(void
                return env;
        }
  
-       return builtin_exec_path;
+       return builtin_exec_path();
  }
  
  static void add_path(struct strbuf *out, const char *path)
@@@ -37,7 -70,7 +70,7 @@@
                else
                        strbuf_addstr(out, make_absolute_path(path));
  
-               strbuf_addch(out, ':');
+               strbuf_addch(out, PATH_SEP);
        }
  }
  
@@@ -50,7 -83,7 +83,7 @@@ void setup_path(const char *cmd_path
  
        add_path(&new_path, argv_exec_path);
        add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT));
-       add_path(&new_path, builtin_exec_path);
+       add_path(&new_path, builtin_exec_path());
        add_path(&new_path, cmd_path);
  
        if (old_path)
  
  int execv_git_cmd(const char **argv)
  {
 -      struct strbuf cmd;
 -      const char *tmp;
 -
 -      strbuf_init(&cmd, 0);
 -      strbuf_addf(&cmd, "git-%s", argv[0]);
 +      int argc;
 +      const char **nargv;
  
 -      /*
 -       * argv[0] must be the git command, but the argv array
 -       * belongs to the caller, and may be reused in
 -       * subsequent loop iterations. Save argv[0] and
 -       * restore it on error.
 -       */
 -      tmp = argv[0];
 -      argv[0] = cmd.buf;
 +      for (argc = 0; argv[argc]; argc++)
 +              ; /* just counting */
 +      nargv = xmalloc(sizeof(*nargv) * (argc + 2));
  
 -      trace_argv_printf(argv, "trace: exec:");
 +      nargv[0] = "git";
 +      for (argc = 0; argv[argc]; argc++)
 +              nargv[argc + 1] = argv[argc];
 +      nargv[argc + 1] = NULL;
 +      trace_argv_printf(nargv, "trace: exec:");
  
        /* execvp() can only ever return if it fails */
 -      execvp(cmd.buf, (char **)argv);
 +      execvp("git", (char **)nargv);
  
        trace_printf("trace: exec failed: %s\n", strerror(errno));
  
 -      argv[0] = tmp;
 -
 -      strbuf_release(&cmd);
 -
 +      free(nargv);
        return -1;
  }
  
diff --combined git-compat-util.h
index 6f94a8197f479f4e3fd93f8f80bdd125efd8a363,51823ae7afc032be0026e7029adeea76df63eb28..545df5924247c9d4ffe700db6c158715561f6a62
  #include <sys/time.h>
  #include <time.h>
  #include <signal.h>
- #include <sys/wait.h>
  #include <fnmatch.h>
+ #include <assert.h>
+ #include <regex.h>
+ #include <utime.h>
+ #ifndef __MINGW32__
+ #include <sys/wait.h>
  #include <sys/poll.h>
  #include <sys/socket.h>
  #include <sys/ioctl.h>
- #include <utime.h>
  #ifndef NO_SYS_SELECT_H
  #include <sys/select.h>
  #endif
- #include <assert.h>
- #include <regex.h>
  #include <netinet/in.h>
  #include <netinet/tcp.h>
  #include <arpa/inet.h>
  #include <grp.h>
  #define _ALL_SOURCE 1
  #endif
+ #else         /* __MINGW32__ */
+ /* pull in Windows compatibility stuff */
+ #include "compat/mingw.h"
+ #endif        /* __MINGW32__ */
  
  #ifndef NO_ICONV
  #include <iconv.h>
  #define PRIuMAX "llu"
  #endif
  
+ #ifndef PATH_SEP
+ #define PATH_SEP ':'
+ #endif
+ #ifndef STRIP_EXTENSION
+ #define STRIP_EXTENSION ""
+ #endif
+ #ifndef has_dos_drive_prefix
+ #define has_dos_drive_prefix(path) 0
+ #endif
+ #ifndef is_dir_sep
+ #define is_dir_sep(c) ((c) == '/')
+ #endif
  #ifdef __GNUC__
  #define NORETURN __attribute__((__noreturn__))
  #else
@@@ -126,6 -147,7 +147,7 @@@ extern void set_error_routine(void (*ro
  extern void set_warn_routine(void (*routine)(const char *warn, va_list params));
  
  extern int prefixcmp(const char *str, const char *prefix);
+ extern time_t tm_to_time_t(const struct tm *tm);
  
  #ifdef NO_MMAP
  
@@@ -163,6 -185,12 +185,12 @@@ extern int git_munmap(void *start, size
  #define pread git_pread
  extern ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
  #endif
+ /*
+  * Forward decl that will remind us if its twin in cache.h changes.
+  * This function is used in compat/pread.c.  But we can't include
+  * cache.h there.
+  */
+ extern ssize_t read_in_full(int fd, void *buf, size_t count);
  
  #ifdef NO_SETENV
  #define setenv gitsetenv
@@@ -240,18 -268,161 +268,18 @@@ static inline char *gitstrchrnul(const 
  
  extern void release_pack_memory(size_t, int);
  
 -static inline char* xstrdup(const char *str)
 -{
 -      char *ret = strdup(str);
 -      if (!ret) {
 -              release_pack_memory(strlen(str) + 1, -1);
 -              ret = strdup(str);
 -              if (!ret)
 -                      die("Out of memory, strdup failed");
 -      }
 -      return ret;
 -}
 -
 -static inline void *xmalloc(size_t size)
 -{
 -      void *ret = malloc(size);
 -      if (!ret && !size)
 -              ret = malloc(1);
 -      if (!ret) {
 -              release_pack_memory(size, -1);
 -              ret = malloc(size);
 -              if (!ret && !size)
 -                      ret = malloc(1);
 -              if (!ret)
 -                      die("Out of memory, malloc failed");
 -      }
 -#ifdef XMALLOC_POISON
 -      memset(ret, 0xA5, size);
 -#endif
 -      return ret;
 -}
 -
 -/*
 - * xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of
 - * "data" to the allocated memory, zero terminates the allocated memory,
 - * and returns a pointer to the allocated memory. If the allocation fails,
 - * the program dies.
 - */
 -static inline void *xmemdupz(const void *data, size_t len)
 -{
 -      char *p = xmalloc(len + 1);
 -      memcpy(p, data, len);
 -      p[len] = '\0';
 -      return p;
 -}
 -
 -static inline char *xstrndup(const char *str, size_t len)
 -{
 -      char *p = memchr(str, '\0', len);
 -      return xmemdupz(str, p ? p - str : len);
 -}
 -
 -static inline void *xrealloc(void *ptr, size_t size)
 -{
 -      void *ret = realloc(ptr, size);
 -      if (!ret && !size)
 -              ret = realloc(ptr, 1);
 -      if (!ret) {
 -              release_pack_memory(size, -1);
 -              ret = realloc(ptr, size);
 -              if (!ret && !size)
 -                      ret = realloc(ptr, 1);
 -              if (!ret)
 -                      die("Out of memory, realloc failed");
 -      }
 -      return ret;
 -}
 -
 -static inline void *xcalloc(size_t nmemb, size_t size)
 -{
 -      void *ret = calloc(nmemb, size);
 -      if (!ret && (!nmemb || !size))
 -              ret = calloc(1, 1);
 -      if (!ret) {
 -              release_pack_memory(nmemb * size, -1);
 -              ret = calloc(nmemb, size);
 -              if (!ret && (!nmemb || !size))
 -                      ret = calloc(1, 1);
 -              if (!ret)
 -                      die("Out of memory, calloc failed");
 -      }
 -      return ret;
 -}
 -
 -static inline void *xmmap(void *start, size_t length,
 -      int prot, int flags, int fd, off_t offset)
 -{
 -      void *ret = mmap(start, length, prot, flags, fd, offset);
 -      if (ret == MAP_FAILED) {
 -              if (!length)
 -                      return NULL;
 -              release_pack_memory(length, fd);
 -              ret = mmap(start, length, prot, flags, fd, offset);
 -              if (ret == MAP_FAILED)
 -                      die("Out of memory? mmap failed: %s", strerror(errno));
 -      }
 -      return ret;
 -}
 -
 -/*
 - * xread() is the same a read(), but it automatically restarts read()
 - * operations with a recoverable error (EAGAIN and EINTR). xread()
 - * DOES NOT GUARANTEE that "len" bytes is read even if the data is available.
 - */
 -static inline ssize_t xread(int fd, void *buf, size_t len)
 -{
 -      ssize_t nr;
 -      while (1) {
 -              nr = read(fd, buf, len);
 -              if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
 -                      continue;
 -              return nr;
 -      }
 -}
 -
 -/*
 - * xwrite() is the same a write(), but it automatically restarts write()
 - * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT
 - * GUARANTEE that "len" bytes is written even if the operation is successful.
 - */
 -static inline ssize_t xwrite(int fd, const void *buf, size_t len)
 -{
 -      ssize_t nr;
 -      while (1) {
 -              nr = write(fd, buf, len);
 -              if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
 -                      continue;
 -              return nr;
 -      }
 -}
 -
 -static inline int xdup(int fd)
 -{
 -      int ret = dup(fd);
 -      if (ret < 0)
 -              die("dup failed: %s", strerror(errno));
 -      return ret;
 -}
 -
 -static inline FILE *xfdopen(int fd, const char *mode)
 -{
 -      FILE *stream = fdopen(fd, mode);
 -      if (stream == NULL)
 -              die("Out of memory? fdopen failed: %s", strerror(errno));
 -      return stream;
 -}
 -
 -static inline int xmkstemp(char *template)
 -{
 -      int fd;
 -
 -      fd = mkstemp(template);
 -      if (fd < 0)
 -              die("Unable to create temporary file: %s", strerror(errno));
 -      return fd;
 -}
 +extern char *xstrdup(const char *str);
 +extern void *xmalloc(size_t size);
 +extern void *xmemdupz(const void *data, size_t len);
 +extern char *xstrndup(const char *str, size_t len);
 +extern void *xrealloc(void *ptr, size_t size);
 +extern void *xcalloc(size_t nmemb, size_t size);
 +extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
 +extern ssize_t xread(int fd, void *buf, size_t len);
 +extern ssize_t xwrite(int fd, const void *buf, size_t len);
 +extern int xdup(int fd);
 +extern FILE *xfdopen(int fd, const char *mode);
 +extern int xmkstemp(char *template);
  
  static inline size_t xsize_t(off_t len)
  {
diff --combined git.c
index 22ac5226def5c2dd4e699533255b84e18f630bfa,871b93ca7e5eb9e919f0d55e169f8864df5e8a6d..3307c078e539e150556f8b27264bbf29b05d9bb6
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -369,6 -369,16 +369,16 @@@ static void handle_internal_command(in
                { "pack-refs", cmd_pack_refs, RUN_SETUP },
        };
        int i;
+       static const char ext[] = STRIP_EXTENSION;
+       if (sizeof(ext) > 1) {
+               i = strlen(argv[0]) - strlen(ext);
+               if (i > 0 && !strcmp(argv[0] + i, ext)) {
+                       char *argv0 = strdup(argv[0]);
+                       argv[0] = cmd = argv0;
+                       argv0[i] = '\0';
+               }
+       }
  
        /* Turn "git cmd --help" into "git help cmd" */
        if (argc > 1 && !strcmp(argv[1], "--help")) {
        }
  }
  
 +static void execv_dashed_external(const char **argv)
 +{
 +      struct strbuf cmd;
 +      const char *tmp;
 +
 +      strbuf_init(&cmd, 0);
 +      strbuf_addf(&cmd, "git-%s", argv[0]);
 +
 +      /*
 +       * argv[0] must be the git command, but the argv array
 +       * belongs to the caller, and may be reused in
 +       * subsequent loop iterations. Save argv[0] and
 +       * restore it on error.
 +       */
 +      tmp = argv[0];
 +      argv[0] = cmd.buf;
 +
 +      trace_argv_printf(argv, "trace: exec:");
 +
 +      /* execvp() can only ever return if it fails */
 +      execvp(cmd.buf, (char **)argv);
 +
 +      trace_printf("trace: exec failed: %s\n", strerror(errno));
 +
 +      argv[0] = tmp;
 +
 +      strbuf_release(&cmd);
 +}
 +
 +
  int main(int argc, const char **argv)
  {
-       const char *cmd = argv[0] ? argv[0] : "git-help";
-       char *slash = strrchr(cmd, '/');
+       const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help";
+       char *slash = (char *)cmd + strlen(cmd);
        const char *cmd_path = NULL;
        int done_alias = 0;
  
         * name, and the dirname as the default exec_path
         * if we don't have anything better.
         */
-       if (slash) {
+       do
+               --slash;
+       while (cmd <= slash && !is_dir_sep(*slash));
+       if (cmd <= slash) {
                *slash++ = 0;
                cmd_path = cmd;
                cmd = slash;
                handle_internal_command(argc, argv);
  
                /* .. then try the external ones */
 -              execv_git_cmd(argv);
 +              execv_dashed_external(argv);
  
                /* It could be an alias -- this works around the insanity
                 * of overriding "git log" with "git show" by having
diff --combined help.c
index 5d1a773ad7f3ed811c35a6b9fa8afe1697615cc8,6c16fb4aa1406dd955c7ae1b0efd74288d72c239..ca9632b6c58267776086f48e87e2236ff1f6053f
--- 1/help.c
--- 2/help.c
+++ b/help.c
@@@ -391,6 -391,32 +391,32 @@@ static void pretty_print_string_list(st
        }
  }
  
+ static int is_executable(const char *name)
+ {
+       struct stat st;
+       if (stat(name, &st) || /* stat, not lstat */
+           !S_ISREG(st.st_mode))
+               return 0;
+ #ifdef __MINGW32__
+       /* cannot trust the executable bit, peek into the file instead */
+       char buf[3] = { 0 };
+       int n;
+       int fd = open(name, O_RDONLY);
+       st.st_mode &= ~S_IXUSR;
+       if (fd >= 0) {
+               n = read(fd, buf, 2);
+               if (n == 2)
+                       /* DOS executables start with "MZ" */
+                       if (!strcmp(buf, "#!") || !strcmp(buf, "MZ"))
+                               st.st_mode |= S_IXUSR;
+               close(fd);
+       }
+ #endif
+       return st.st_mode & S_IXUSR;
+ }
  static unsigned int list_commands_in_dir(struct cmdnames *cmds,
                                         const char *path)
  {
                return 0;
  
        while ((de = readdir(dir)) != NULL) {
-               struct stat st;
                int entlen;
  
                if (prefixcmp(de->d_name, prefix))
                        continue;
  
-               if (stat(de->d_name, &st) || /* stat, not lstat */
-                   !S_ISREG(st.st_mode) ||
-                   !(st.st_mode & S_IXUSR))
+               if (!is_executable(de->d_name))
                        continue;
  
                entlen = strlen(de->d_name) - prefix_len;
@@@ -447,7 -470,7 +470,7 @@@ static unsigned int load_command_list(v
  
        path = paths = xstrdup(env_path);
        while (1) {
-               if ((colon = strchr(path, ':')))
+               if ((colon = strchr(path, PATH_SEP)))
                        *colon = 0;
  
                len = list_commands_in_dir(&other_cmds, path);
@@@ -527,26 -550,20 +550,26 @@@ static int is_git_command(const char *s
                is_in_cmdlist(&other_cmds, s);
  }
  
 +static const char *prepend(const char *prefix, const char *cmd)
 +{
 +      size_t pre_len = strlen(prefix);
 +      size_t cmd_len = strlen(cmd);
 +      char *p = xmalloc(pre_len + cmd_len + 1);
 +      memcpy(p, prefix, pre_len);
 +      strcpy(p + pre_len, cmd);
 +      return p;
 +}
 +
  static const char *cmd_to_page(const char *git_cmd)
  {
        if (!git_cmd)
                return "git";
        else if (!prefixcmp(git_cmd, "git"))
                return git_cmd;
 -      else {
 -              int page_len = strlen(git_cmd) + 4;
 -              char *p = xmalloc(page_len + 1);
 -              strcpy(p, "git-");
 -              strcpy(p + 4, git_cmd);
 -              p[page_len] = 0;
 -              return p;
 -      }
 +      else if (is_git_command(git_cmd))
 +              return prepend("git-", git_cmd);
 +      else
 +              return prepend("git", git_cmd);
  }
  
  static void setup_man_path(void)
diff --combined setup.c
index 3b111ea7cf5f68ecca085f5a56421ea190e1771a,8bb7b10174fdb24f2a028d6404224109c600b7ca..cc3fb380c1f3471f0371ba54ca2f338f90a8d4fc
+++ b/setup.c
@@@ -6,11 -6,17 +6,17 @@@ static int inside_work_tree = -1
  
  static int sanitary_path_copy(char *dst, const char *src)
  {
-       char *dst0 = dst;
+       char *dst0;
  
-       if (*src == '/') {
+       if (has_dos_drive_prefix(src)) {
+               *dst++ = *src++;
+               *dst++ = *src++;
+       }
+       dst0 = dst;
+       if (is_dir_sep(*src)) {
                *dst++ = '/';
-               while (*src == '/')
+               while (is_dir_sep(*src))
                        src++;
        }
  
                 * (4) "../"          -- strip one, eat slash and continue.
                 */
                if (c == '.') {
-                       switch (src[1]) {
-                       case '\0':
+                       if (!src[1]) {
                                /* (1) */
                                src++;
-                               break;
-                       case '/':
+                       } else if (is_dir_sep(src[1])) {
                                /* (2) */
                                src += 2;
-                               while (*src == '/')
+                               while (is_dir_sep(*src))
                                        src++;
                                continue;
-                       case '.':
-                               switch (src[2]) {
-                               case '\0':
+                       } else if (src[1] == '.') {
+                               if (!src[2]) {
                                        /* (3) */
                                        src += 2;
                                        goto up_one;
-                               case '/':
+                               } else if (is_dir_sep(src[2])) {
                                        /* (4) */
                                        src += 3;
-                                       while (*src == '/')
+                                       while (is_dir_sep(*src))
                                                src++;
                                        goto up_one;
                                }
                }
  
                /* copy up to the next '/', and eat all '/' */
-               while ((c = *src++) != '\0' && c != '/')
+               while ((c = *src++) != '\0' && !is_dir_sep(c))
                        *dst++ = c;
-               if (c == '/') {
-                       *dst++ = c;
-                       while (c == '/')
+               if (is_dir_sep(c)) {
+                       *dst++ = '/';
+                       while (is_dir_sep(c))
                                c = *src++;
                        src--;
                } else if (!c)
@@@ -77,7 -80,7 +80,7 @@@
                        if (dst <= dst0)
                                break;
                        c = *dst--;
-                       if (c == '/') {
+                       if (c == '/') { /* MinGW: cannot be '\\' anymore */
                                dst += 2;
                                break;
                        }
@@@ -126,10 -129,23 +129,23 @@@ const char *prefix_path(const char *pre
  const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
  {
        static char path[PATH_MAX];
+ #ifndef __MINGW32__
        if (!pfx || !*pfx || is_absolute_path(arg))
                return arg;
        memcpy(path, pfx, pfx_len);
        strcpy(path + pfx_len, arg);
+ #else
+       char *p;
+       /* don't add prefix to absolute paths, but still replace '\' by '/' */
+       if (is_absolute_path(arg))
+               pfx_len = 0;
+       else
+               memcpy(path, pfx, pfx_len);
+       strcpy(path + pfx_len, arg);
+       for (p = path + pfx_len; *p; p++)
+               if (*p == '\\')
+                       *p = '/';
+ #endif
        return path;
  }
  
@@@ -292,10 -308,9 +308,10 @@@ void setup_work_tree(void
        work_tree = get_git_work_tree();
        git_dir = get_git_dir();
        if (!is_absolute_path(git_dir))
 -              set_git_dir(make_absolute_path(git_dir));
 +              git_dir = make_absolute_path(git_dir);
        if (!work_tree || chdir(work_tree))
                die("This operation must be run in a work tree");
 +      set_git_dir(make_relative_path(git_dir, work_tree));
        initialized = 1;
  }
  
@@@ -364,6 -379,7 +380,7 @@@ const char *setup_git_directory_gently(
        const char *gitdirenv;
        const char *gitfile_dir;
        int len, offset;
+       int minoffset = 0;
  
        /*
         * Let's assume that we are in a git repository.
  
        if (!getcwd(cwd, sizeof(cwd)-1))
                die("Unable to read current working directory");
+       if (has_dos_drive_prefix(cwd))
+               minoffset = 2;
  
        /*
         * Test in the following order (relative to the cwd):
                }
                chdir("..");
                do {
-                       if (!offset) {
+                       if (offset <= minoffset) {
                                if (nongit_ok) {
                                        if (chdir(cwd))
                                                die("Cannot come back to cwd");
                                }
                                die("Not a git repository");
                        }
-               } while (cwd[--offset] != '/');
+               } while (offset > minoffset && cwd[--offset] != '/');
        }
  
        inside_git_dir = 0;
diff --combined sha1_file.c
index dd8327c941dd012d890fcb20313a3f0e682d1fa2,0a54eed761f6836d48eb28dd160e94a779a4bfba..2187e6caedc7b1824291c3330ed92851fed69704
@@@ -83,14 -83,18 +83,18 @@@ int get_sha1_hex(const char *hex, unsig
        return 0;
  }
  
+ static inline int offset_1st_component(const char *path)
+ {
+       if (has_dos_drive_prefix(path))
+               return 2 + (path[2] == '/');
+       return *path == '/';
+ }
  int safe_create_leading_directories(char *path)
  {
-       char *pos = path;
+       char *pos = path + offset_1st_component(path);
        struct stat st;
  
-       if (is_absolute_path(path))
-               pos++;
        while (pos) {
                pos = strchr(pos, '/');
                if (!pos)
        return 0;
  }
  
 +int safe_create_leading_directories_const(const char *path)
 +{
 +      /* path points to cache entries, so xstrdup before messing with it */
 +      char *buf = xstrdup(path);
 +      int result = safe_create_leading_directories(buf);
 +      free(buf);
 +      return result;
 +}
 +
  char *sha1_to_hex(const unsigned char *sha1)
  {
        static int bufno;
@@@ -401,7 -396,7 +405,7 @@@ void prepare_alt_odb(void
        if (!alt) alt = "";
  
        alt_odb_tail = &alt_odb_list;
-       link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL, 0);
+       link_alt_odb_entries(alt, alt + strlen(alt), PATH_SEP, NULL, 0);
  
        read_info_alternates(get_object_directory(), 0);
  }
@@@ -801,28 -796,18 +805,28 @@@ unsigned char* use_pack(struct packed_g
        return win->base + offset;
  }
  
 +static struct packed_git *alloc_packed_git(int extra)
 +{
 +      struct packed_git *p = xmalloc(sizeof(*p) + extra);
 +      memset(p, 0, sizeof(*p));
 +      p->pack_fd = -1;
 +      return p;
 +}
 +
  struct packed_git *add_packed_git(const char *path, int path_len, int local)
  {
        struct stat st;
 -      struct packed_git *p = xmalloc(sizeof(*p) + path_len + 2);
 +      struct packed_git *p = alloc_packed_git(path_len + 2);
  
        /*
         * Make sure a corresponding .pack file exists and that
         * the index looks sane.
         */
        path_len -= strlen(".idx");
 -      if (path_len < 1)
 +      if (path_len < 1) {
 +              free(p);
                return NULL;
 +      }
        memcpy(p->pack_name, path, path_len);
        strcpy(p->pack_name + path_len, ".pack");
        if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
        /* ok, it looks sane as far as we can check without
         * actually mapping the pack file.
         */
 -      p->index_version = 0;
 -      p->index_data = NULL;
 -      p->index_size = 0;
 -      p->num_objects = 0;
        p->pack_size = st.st_size;
 -      p->next = NULL;
 -      p->windows = NULL;
 -      p->pack_fd = -1;
        p->pack_local = local;
        p->mtime = st.st_mtime;
        if (path_len < 40 || get_sha1_hex(path + path_len - 40, p->sha1))
@@@ -845,15 -837,19 +849,15 @@@ struct packed_git *parse_pack_index(uns
  {
        const char *idx_path = sha1_pack_index_name(sha1);
        const char *path = sha1_pack_name(sha1);
 -      struct packed_git *p = xmalloc(sizeof(*p) + strlen(path) + 2);
 +      struct packed_git *p = alloc_packed_git(strlen(path) + 1);
  
 +      strcpy(p->pack_name, path);
 +      hashcpy(p->sha1, sha1);
        if (check_packed_git_idx(idx_path, p)) {
                free(p);
                return NULL;
        }
  
 -      strcpy(p->pack_name, path);
 -      p->pack_size = 0;
 -      p->next = NULL;
 -      p->windows = NULL;
 -      p->pack_fd = -1;
 -      hashcpy(p->sha1, sha1);
        return p;
  }
  
@@@ -990,18 -986,6 +994,18 @@@ void reprepare_packed_git(void
        prepare_packed_git();
  }
  
 +static void mark_bad_packed_object(struct packed_git *p,
 +                                 const unsigned char *sha1)
 +{
 +      unsigned i;
 +      for (i = 0; i < p->num_bad_objects; i++)
 +              if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
 +                      return;
 +      p->bad_object_sha1 = xrealloc(p->bad_object_sha1, 20 * (p->num_bad_objects + 1));
 +      hashcpy(p->bad_object_sha1 + 20 * p->num_bad_objects, sha1);
 +      p->num_bad_objects++;
 +}
 +
  int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)
  {
        unsigned char real_sha1[20];
@@@ -1320,17 -1304,20 +1324,17 @@@ static off_t get_delta_base(struct pack
                while (c & 128) {
                        base_offset += 1;
                        if (!base_offset || MSB(base_offset, 7))
 -                              die("offset value overflow for delta base object");
 +                              return 0;  /* overflow */
                        c = base_info[used++];
                        base_offset = (base_offset << 7) + (c & 127);
                }
                base_offset = delta_obj_offset - base_offset;
                if (base_offset >= delta_obj_offset)
 -                      die("delta base offset out of bound");
 +                      return 0;  /* out of bound */
                *curpos += used;
        } else if (type == OBJ_REF_DELTA) {
                /* The base entry _must_ be in the same pack */
                base_offset = find_pack_entry_one(base_info, p);
 -              if (!base_offset)
 -                      die("failed to find delta-pack base object %s",
 -                              sha1_to_hex(base_info));
                *curpos += 20;
        } else
                die("I am totally screwed");
@@@ -1423,9 -1410,6 +1427,9 @@@ const char *packed_object_info_detail(s
                        return typename(type);
                case OBJ_OFS_DELTA:
                        obj_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
 +                      if (!obj_offset)
 +                              die("pack %s contains bad delta base reference of type %s",
 +                                  p->pack_name, typename(type));
                        if (*delta_chain_length == 0) {
                                revidx = find_pack_revindex(p, obj_offset);
                                hashcpy(base_sha1, nth_packed_object_sha1(p, revidx->nr));
@@@ -1620,41 -1604,17 +1624,41 @@@ static void *unpack_delta_entry(struct 
        off_t base_offset;
  
        base_offset = get_delta_base(p, w_curs, &curpos, *type, obj_offset);
 +      if (!base_offset) {
 +              error("failed to validate delta base reference "
 +                    "at offset %"PRIuMAX" from %s",
 +                    (uintmax_t)curpos, p->pack_name);
 +              return NULL;
 +      }
        base = cache_or_unpack_entry(p, base_offset, &base_size, type, 0);
 -      if (!base)
 -              die("failed to read delta base object"
 -                  " at %"PRIuMAX" from %s",
 -                  (uintmax_t)base_offset, p->pack_name);
 +      if (!base) {
 +              /*
 +               * We're probably in deep shit, but let's try to fetch
 +               * the required base anyway from another pack or loose.
 +               * This is costly but should happen only in the presence
 +               * of a corrupted pack, and is better than failing outright.
 +               */
 +              struct revindex_entry *revidx = find_pack_revindex(p, base_offset);
 +              const unsigned char *base_sha1 =
 +                                      nth_packed_object_sha1(p, revidx->nr);
 +              error("failed to read delta base object %s"
 +                    " at offset %"PRIuMAX" from %s",
 +                    sha1_to_hex(base_sha1), (uintmax_t)base_offset,
 +                    p->pack_name);
 +              mark_bad_packed_object(p, base_sha1);
 +              base = read_sha1_file(base_sha1, type, &base_size);
 +              if (!base)
 +                      return NULL;
 +      }
  
        delta_data = unpack_compressed_entry(p, w_curs, curpos, delta_size);
 -      if (!delta_data)
 -              die("failed to unpack compressed delta"
 -                  " at %"PRIuMAX" from %s",
 -                  (uintmax_t)curpos, p->pack_name);
 +      if (!delta_data) {
 +              error("failed to unpack compressed delta "
 +                    "at offset %"PRIuMAX" from %s",
 +                    (uintmax_t)curpos, p->pack_name);
 +              free(base);
 +              return NULL;
 +      }
        result = patch_delta(base, base_size,
                             delta_data, delta_size,
                             sizep);
@@@ -1686,9 -1646,7 +1690,9 @@@ void *unpack_entry(struct packed_git *p
                data = unpack_compressed_entry(p, &w_curs, curpos, *sizep);
                break;
        default:
 -              die("unknown object type %i in %s", *type, p->pack_name);
 +              data = NULL;
 +              error("unknown object type %i at offset %"PRIuMAX" in %s",
 +                    *type, (uintmax_t)obj_offset, p->pack_name);
        }
        unuse_pack(&w_curs);
        return data;
@@@ -1714,7 -1672,7 +1718,7 @@@ const unsigned char *nth_packed_object_
        }
  }
  
 -static off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
 +off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
  {
        const unsigned char *index = p->index_data;
        index += 4 * 256;
@@@ -1834,13 -1792,6 +1838,13 @@@ static int find_pack_entry(const unsign
                                goto next;
                }
  
 +              if (p->num_bad_objects) {
 +                      unsigned i;
 +                      for (i = 0; i < p->num_bad_objects; i++)
 +                              if (!hashcmp(sha1, p->bad_object_sha1 + 20 * i))
 +                                      goto next;
 +              }
 +
                offset = find_pack_entry_one(sha1, p);
                if (offset) {
                        /*
@@@ -1925,24 -1876,11 +1929,24 @@@ static void *read_packed_sha1(const uns
                              enum object_type *type, unsigned long *size)
  {
        struct pack_entry e;
 +      void *data;
  
        if (!find_pack_entry(sha1, &e, NULL))
                return NULL;
 -      else
 -              return cache_or_unpack_entry(e.p, e.offset, size, type, 1);
 +      data = cache_or_unpack_entry(e.p, e.offset, size, type, 1);
 +      if (!data) {
 +              /*
 +               * We're probably in deep shit, but let's try to fetch
 +               * the required object anyway from another pack or loose.
 +               * This should happen only in the presence of a corrupted
 +               * pack, and is better than failing outright.
 +               */
 +              error("failed to read object %s at offset %"PRIuMAX" from %s",
 +                    sha1_to_hex(sha1), (uintmax_t)e.offset, e.p->pack_name);
 +              mark_bad_packed_object(e.p, sha1);
 +              data = read_sha1_file(sha1, type, size);
 +      }
 +      return data;
  }
  
  /*
@@@ -2149,8 -2087,7 +2153,8 @@@ int hash_sha1_file(const void *buf, uns
  /* Finalize a file on disk, and close it. */
  static void close_sha1_file(int fd)
  {
 -      /* For safe-mode, we could fsync_or_die(fd, "sha1 file") here */
 +      if (fsync_object_files)
 +              fsync_or_die(fd, "sha1 file");
        fchmod(fd, 0444);
        if (close(fd) != 0)
                die("unable to write sha1 file");
@@@ -2185,7 -2122,6 +2189,7 @@@ static int create_tmpfile(char *buffer
        fd = mkstemp(buffer);
        if (fd < 0 && dirlen) {
                /* Make sure the directory exists */
 +              memcpy(buffer, filename, dirlen);
                buffer[dirlen-1] = 0;
                if (mkdir(buffer, 0777) || adjust_shared_perm(buffer))
                        return -1;