From: Junio C Hamano Date: Thu, 3 Jul 2008 04:57:52 +0000 (-0700) Subject: Merge branch 'j6t/mingw' X-Git-Tag: v1.6.0-rc0~159 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/bb1ab2db08f48add4236a5a9c08aa1a6aa4d5d48?ds=inline;hp=-c Merge branch 'j6t/mingw' * 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(). ... --- bb1ab2db08f48add4236a5a9c08aa1a6aa4d5d48 diff --combined Documentation/git.txt index 57a3a84be1,4e4bd6ddb1..22702c260c --- a/Documentation/git.txt +++ b/Documentation/git.txt @@@ -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`. @@@ -383,7 -382,7 +383,7 @@@ 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 ba16f26793,a2a19a85f0..78e08d3745 --- a/Makefile +++ 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 188428dd26,72544de4ce..35a91320a5 --- a/cache.h +++ 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 0f8f4b5b7d,84db7ee664..da04efe951 --- a/exec_cmd.c +++ b/exec_cmd.c @@@ -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) @@@ -65,25 -98,32 +98,25 @@@ 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 6f94a8197f,51823ae7af..545df59242 --- a/git-compat-util.h +++ b/git-compat-util.h @@@ -63,17 -63,18 +63,18 @@@ #include #include #include - #include #include + #include + #include + #include + #ifndef __MINGW32__ + #include #include #include #include - #include #ifndef NO_SYS_SELECT_H #include #endif - #include - #include #include #include #include @@@ -89,6 -90,10 +90,10 @@@ #include #define _ALL_SOURCE 1 #endif + #else /* __MINGW32__ */ + /* pull in Windows compatibility stuff */ + #include "compat/mingw.h" + #endif /* __MINGW32__ */ #ifndef NO_ICONV #include @@@ -105,6 -110,22 +110,22 @@@ #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 22ac5226de,871b93ca7e..3307c078e5 --- a/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")) { @@@ -384,40 -394,10 +394,40 @@@ } } +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; @@@ -426,7 -406,10 +436,10 @@@ * 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; @@@ -478,7 -461,7 +491,7 @@@ 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 5d1a773ad7,6c16fb4aa1..ca9632b6c5 --- a/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) { @@@ -404,15 -430,12 +430,12 @@@ 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 3b111ea7cf,8bb7b10174..cc3fb380c1 --- a/setup.c +++ 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++; } @@@ -26,27 -32,24 +32,24 @@@ * (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; } @@@ -54,11 -57,11 +57,11 @@@ } /* 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. @@@ -414,6 -430,8 +431,8 @@@ 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): @@@ -446,7 -464,7 +465,7 @@@ } chdir(".."); do { - if (!offset) { + if (offset <= minoffset) { if (nongit_ok) { if (chdir(cwd)) die("Cannot come back to cwd"); @@@ -455,7 -473,7 +474,7 @@@ } die("Not a git repository"); } - } while (cwd[--offset] != '/'); + } while (offset > minoffset && cwd[--offset] != '/'); } inside_git_dir = 0; diff --combined sha1_file.c index dd8327c941,0a54eed761..2187e6caed --- a/sha1_file.c +++ b/sha1_file.c @@@ -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) @@@ -116,15 -120,6 +120,15 @@@ 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)) { @@@ -833,7 -818,14 +837,7 @@@ /* 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;