diff --git a/.gitattributes b/.gitattributes index 9fa72ad450..b08a1416d8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,6 +5,7 @@ *.pl eof=lf diff=perl *.pm eol=lf diff=perl *.py eol=lf diff=python +*.bat eol=crlf /Documentation/**/*.txt eol=lf /command-list.txt eol=lf /GIT-VERSION-GEN eol=lf diff --git a/.gitignore b/.gitignore index 7374587f9d..d753b7e940 100644 --- a/.gitignore +++ b/.gitignore @@ -190,6 +190,42 @@ /gitweb/static/gitweb.js /gitweb/static/gitweb.min.* /command-list.h +/libgit +/test-chmtime +/test-ctype +/test-config +/test-date +/test-delta +/test-dump-cache-tree +/test-dump-split-index +/test-dump-untracked-cache +/test-fake-ssh +/test-scrap-cache-tree +/test-genrandom +/test-hashmap +/test-index-version +/test-line-buffer +/test-match-trees +/test-mergesort +/test-mktemp +/test-parse-options +/test-path-utils +/test-prio-queue +/test-read-cache +/test-regex +/test-revision-walking +/test-run-command +/test-sha1 +/test-sha1-array +/test-sigchain +/test-string-list +/test-submodule-config +/test-subprocess +/test-svn-fe +/test-urlmatch-normalization +/test-wildmatch +/vcs-svn_lib +/xdiff_lib *.tar.gz *.dsc *.deb @@ -227,6 +263,15 @@ *.user *.idb *.pdb -/Debug/ -/Release/ +*.ilk +*.iobj +*.ipdb +*.dll +.vs/ +*.manifest +Debug/ +Release/ +/UpgradeLog*.htm +/git.VC.VC.opendb +/git.VC.db *.dSYM diff --git a/Documentation/config.txt b/Documentation/config.txt index d87846faa6..7b3cdd1f8d 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -7,7 +7,9 @@ the Git commands' behavior. The files `.git/config` and optionally repository are used to store the configuration for that repository, and `$HOME/.gitconfig` is used to store a per-user configuration as fallback values for the `.git/config` file. The file `/etc/gitconfig` -can be used to store a system-wide default configuration. +can be used to store a system-wide default configuration. On Windows, +configuration can also be stored in `C:\ProgramData\Git\config`; This +file will be used also by libgit2-based software. The configuration variables are used by both the Git plumbing and the porcelains. The variables are divided into sections, wherein @@ -406,6 +408,8 @@ include::config/reset.txt[] include::config/sendemail.txt[] +include::config/sendpack.txt[] + include::config/sequencer.txt[] include::config/showbranch.txt[] diff --git a/Documentation/config/sendpack.txt b/Documentation/config/sendpack.txt new file mode 100644 index 0000000000..e306f657fb --- /dev/null +++ b/Documentation/config/sendpack.txt @@ -0,0 +1,5 @@ +sendpack.sideband:: + Allows to disable the side-band-64k capability for send-pack even + when it is advertised by the server. Makes it possible to work + around a limitation in the git for windows implementation together + with the dump git protocol. Defaults to true. diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 1bfe9f56a7..47084ebaf5 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -270,8 +270,16 @@ FILES If not set explicitly with `--file`, there are four files where 'git config' will search for configuration options: +$PROGRAMDATA/Git/config:: + (Windows-only) System-wide configuration file shared with other Git + implementations. Typically `$PROGRAMDATA` points to `C:\ProgramData`. + $(prefix)/etc/gitconfig:: System-wide configuration file. + (Windows-only) This file contains only the settings which are + specific for this installation of Git for Windows and which should + not be shared with other Git implementations like JGit, libgit2. + `--system` will select this file. $XDG_CONFIG_HOME/git/config:: Second user-specific configuration file. If $XDG_CONFIG_HOME is not set diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 132f8e55f6..5ac636ed82 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -10,6 +10,7 @@ SYNOPSIS [verse] 'git reset' [-q] [] [--] ... 'git reset' (--patch | -p) [] [--] [...] +EXPERIMENTAL: 'git reset' [-q] [--stdin [-z]] [] 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [] DESCRIPTION @@ -100,6 +101,15 @@ OPTIONS `reset.quiet` config option. `--quiet` and `--no-quiet` will override the default behavior. +--stdin:: + EXPERIMENTAL: Instead of taking list of paths from the + command line, read list of paths from the standard input. + Paths are separated by LF (i.e. one path per line) by + default. + +-z:: + EXPERIMENTAL: Only meaningful with `--stdin`; paths are + separated with NUL character instead of LF. EXAMPLES -------- diff --git a/Documentation/git.txt b/Documentation/git.txt index 00156d64aa..e350596286 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -567,7 +567,8 @@ for further details. `GIT_CONFIG_NOSYSTEM`:: Whether to skip reading settings from the system-wide - `$(prefix)/etc/gitconfig` file. This environment variable can + `$(prefix)/etc/gitconfig` file (and on Windows, also from the + `%PROGRAMDATA%\Git\config` file). This environment variable can be used along with `$HOME` and `$XDG_CONFIG_HOME` to create a predictable environment for a picky script, or you can set it temporarily to avoid using a buggy `/etc/gitconfig` file while diff --git a/Makefile b/Makefile index c5240942f2..77ad324763 100644 --- a/Makefile +++ b/Makefile @@ -1224,7 +1224,7 @@ endif ifdef SANE_TOOL_PATH SANE_TOOL_PATH_SQ = $(subst ','\'',$(SANE_TOOL_PATH)) -BROKEN_PATH_FIX = 's|^\# @@BROKEN_PATH_FIX@@$$|git_broken_path_fix $(SANE_TOOL_PATH_SQ)|' +BROKEN_PATH_FIX = 's|^\# @@BROKEN_PATH_FIX@@$$|git_broken_path_fix "$(SANE_TOOL_PATH_SQ)"|' PATH := $(SANE_TOOL_PATH):${PATH} else BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d' @@ -2692,7 +2692,7 @@ bin-wrappers/%: wrap-for-bin.sh @mkdir -p bin-wrappers $(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ -e 's|@@BUILD_DIR@@|$(shell pwd)|' \ - -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%,$(@F))|' < $< > $@ && \ + -e 's|@@PROG@@|$(patsubst test-%,t/helper/test-%$(X),$(@F))$(patsubst git%,$(X),$(filter $(@F),$(BINDIR_PROGRAMS_NEED_X)))|' < $< > $@ && \ chmod +x $@ # GNU make supports exporting all variables by "export" without parameters. @@ -2830,6 +2830,33 @@ install: all $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' $(INSTALL) -m 644 $(SCRIPT_LIB) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' $(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)' +ifdef MSVC + # We DO NOT install the individual foo.o.pdb files because they + # have already been rolled up into the exe's pdb file. + # We DO NOT have pdb files for the builtin commands (like git-status.exe) + # because it is just a copy/hardlink of git.exe, rather than a unique binary. + $(INSTALL) git.pdb '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) git-shell.pdb '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) git-upload-pack.pdb '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) git-credential-store.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' + $(INSTALL) git-daemon.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' + $(INSTALL) git-fast-import.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' + $(INSTALL) git-http-backend.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' + $(INSTALL) git-http-fetch.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' + $(INSTALL) git-http-push.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' + $(INSTALL) git-imap-send.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' + $(INSTALL) git-remote-http.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' + $(INSTALL) git-remote-testsvn.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' + $(INSTALL) git-sh-i18n--envsubst.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' + $(INSTALL) git-show-index.pdb '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' +ifndef DEBUG + $(INSTALL) $(vcpkg_rel_bin)/*.dll '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) $(vcpkg_rel_bin)/*.pdb '$(DESTDIR_SQ)$(bindir_SQ)' +else + $(INSTALL) $(vcpkg_dbg_bin)/*.dll '$(DESTDIR_SQ)$(bindir_SQ)' + $(INSTALL) $(vcpkg_dbg_bin)/*.pdb '$(DESTDIR_SQ)$(bindir_SQ)' +endif +endif $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mergetools_instdir_SQ)' $(INSTALL) -m 644 mergetools/* '$(DESTDIR_SQ)$(mergetools_instdir_SQ)' @@ -3041,6 +3068,19 @@ endif $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-BUILD-OPTIONS $(RM) GIT-USER-AGENT GIT-PREFIX $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS +ifdef MSVC + $(RM) $(patsubst %.o,%.o.pdb,$(OBJECTS)) + $(RM) $(patsubst %.exe,%.pdb,$(OTHER_PROGRAMS)) + $(RM) $(patsubst %.exe,%.iobj,$(OTHER_PROGRAMS)) + $(RM) $(patsubst %.exe,%.ipdb,$(OTHER_PROGRAMS)) + $(RM) $(patsubst %.exe,%.pdb,$(PROGRAMS)) + $(RM) $(patsubst %.exe,%.iobj,$(PROGRAMS)) + $(RM) $(patsubst %.exe,%.ipdb,$(PROGRAMS)) + $(RM) $(patsubst %.exe,%.pdb,$(TEST_PROGRAMS)) + $(RM) $(patsubst %.exe,%.iobj,$(TEST_PROGRAMS)) + $(RM) $(patsubst %.exe,%.ipdb,$(TEST_PROGRAMS)) + $(RM) compat/vcbuild/MSVC-DEFS-GEN +endif .PHONY: all install profile-clean cocciclean clean strip .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell diff --git a/archive-tar.c b/archive-tar.c index 4aabd566fb..5979ed14b7 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -17,6 +17,8 @@ static unsigned long offset; static int tar_umask = 002; +static gzFile gzip; + static int write_tar_filter_archive(const struct archiver *ar, struct archiver_args *args); @@ -38,11 +40,21 @@ static int write_tar_filter_archive(const struct archiver *ar, #define USTAR_MAX_MTIME 077777777777ULL #endif +/* writes out the whole block, or dies if fails */ +static void write_block_or_die(const char *block) { + if (gzip) { + if (gzwrite(gzip, block, (unsigned) BLOCKSIZE) != BLOCKSIZE) + die(_("gzwrite failed")); + } else { + write_or_die(1, block, BLOCKSIZE); + } +} + /* writes out the whole block, but only if it is full */ static void write_if_needed(void) { if (offset == BLOCKSIZE) { - write_or_die(1, block, BLOCKSIZE); + write_block_or_die(block); offset = 0; } } @@ -66,7 +78,7 @@ static void do_write_blocked(const void *data, unsigned long size) write_if_needed(); } while (size >= BLOCKSIZE) { - write_or_die(1, buf, BLOCKSIZE); + write_block_or_die(buf); size -= BLOCKSIZE; buf += BLOCKSIZE; } @@ -101,10 +113,10 @@ static void write_trailer(void) { int tail = BLOCKSIZE - offset; memset(block + offset, 0, tail); - write_or_die(1, block, BLOCKSIZE); + write_block_or_die(block); if (tail < 2 * RECORDSIZE) { memset(block, 0, offset); - write_or_die(1, block, BLOCKSIZE); + write_block_or_die(block); } } @@ -454,18 +466,34 @@ static int write_tar_filter_archive(const struct archiver *ar, filter.use_shell = 1; filter.in = -1; - if (start_command(&filter) < 0) - die_errno(_("unable to start '%s' filter"), argv[0]); - close(1); - if (dup2(filter.in, 1) < 0) - die_errno(_("unable to redirect descriptor")); - close(filter.in); + if (!strcmp("gzip -cn", ar->data)) { + char outmode[4] = "wb\0"; + + if (args->compression_level >= 0 && args->compression_level <= 9) + outmode[2] = '0' + args->compression_level; + + gzip = gzdopen(fileno(stdout), outmode); + if (!gzip) + die(_("Could not gzdopen stdout")); + } else { + if (start_command(&filter) < 0) + die_errno(_("unable to start '%s' filter"), argv[0]); + close(1); + if (dup2(filter.in, 1) < 0) + die_errno(_("unable to redirect descriptor")); + close(filter.in); + } r = write_tar_archive(ar, args); - close(1); - if (finish_command(&filter) != 0) - die(_("'%s' filter reported error"), argv[0]); + if (gzip) { + if (gzclose(gzip) != Z_OK) + die(_("gzclose failed")); + } else { + close(1); + if (finish_command(&filter) != 0) + die(_("'%s' filter reported error"), argv[0]); + } strbuf_release(&cmd); return r; diff --git a/builtin/clone.c b/builtin/clone.c index 50bde99618..4e0a16e300 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -1178,7 +1178,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } if (!is_local && !complete_refs_before_fetch) - transport_fetch_refs(transport, mapped_refs); + if (transport_fetch_refs(transport, mapped_refs)) + die(_("could not fetch refs from %s"), + transport->url); remote_head = find_ref_by_name(refs, "HEAD"); remote_head_points_at = diff --git a/builtin/init-db.c b/builtin/init-db.c index 93eff7618c..94df241ad5 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -155,6 +155,9 @@ static int git_init_db_config(const char *k, const char *v, void *cb) if (!strcmp(k, "init.templatedir")) return git_config_pathname(&init_db_template_dir, k, v); + if (starts_with(k, "core.")) + return platform_core_config(k, v, cb); + return 0; } @@ -361,6 +364,9 @@ int init_db(const char *git_dir, const char *real_git_dir, } startup_info->have_repository = 1; + /* Just look for `init.templatedir` and `core.hidedotfiles` */ + git_config(git_init_db_config, NULL); + safe_create_dir(git_dir, 0); init_is_bare_repository = is_bare_repository(); diff --git a/builtin/repack.c b/builtin/repack.c index 67f8978043..51c9a97588 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -421,6 +421,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) close_all_packs(the_repository->objects); + close_all_packs(the_repository->objects); + /* * Ok we have prepared all new packfiles. * First see if there are packs of the same name and if so diff --git a/builtin/reset.c b/builtin/reset.c index 4d18a461fa..023fe12be0 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -25,12 +25,15 @@ #include "cache-tree.h" #include "submodule.h" #include "submodule-config.h" +#include "strbuf.h" +#include "quote.h" #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000) static const char * const git_reset_usage[] = { N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] []"), N_("git reset [-q] [] [--] ..."), + N_("EXPERIMENTAL: git reset [-q] [--stdin [-z]] []"), N_("git reset --patch [] [--] [...]"), NULL }; @@ -284,7 +287,9 @@ static int git_reset_config(const char *var, const char *value, void *cb) int cmd_reset(int argc, const char **argv, const char *prefix) { int reset_type = NONE, update_ref_status = 0, quiet = 0; - int patch_mode = 0, unborn; + int patch_mode = 0, nul_term_line = 0, read_from_stdin = 0, unborn; + char **stdin_paths = NULL; + int stdin_nr = 0, stdin_alloc = 0; const char *rev; struct object_id oid; struct pathspec pathspec; @@ -306,6 +311,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that removed paths will be added later")), + OPT_BOOL('z', NULL, &nul_term_line, + N_("EXPERIMENTAL: paths are separated with NUL character")), + OPT_BOOL(0, "stdin", &read_from_stdin, + N_("EXPERIMENTAL: read paths from ")), OPT_END() }; @@ -316,6 +325,42 @@ int cmd_reset(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_DASHDASH); parse_args(&pathspec, argv, prefix, patch_mode, &rev); + if (read_from_stdin) { + strbuf_getline_fn getline_fn = nul_term_line ? + strbuf_getline_nul : strbuf_getline_lf; + int flags = PATHSPEC_PREFER_FULL; + struct strbuf buf = STRBUF_INIT; + struct strbuf unquoted = STRBUF_INIT; + + if (patch_mode) + die(_("--stdin is incompatible with --patch")); + + if (pathspec.nr) + die(_("--stdin is incompatible with path arguments")); + + while (getline_fn(&buf, stdin) != EOF) { + if (!nul_term_line && buf.buf[0] == '"') { + strbuf_reset(&unquoted); + if (unquote_c_style(&unquoted, buf.buf, NULL)) + die(_("line is badly quoted")); + strbuf_swap(&buf, &unquoted); + } + ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc); + stdin_paths[stdin_nr++] = xstrdup(buf.buf); + strbuf_reset(&buf); + } + strbuf_release(&unquoted); + strbuf_release(&buf); + + ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc); + stdin_paths[stdin_nr++] = NULL; + flags |= PATHSPEC_LITERAL_PATH; + parse_pathspec(&pathspec, 0, flags, prefix, + (const char **)stdin_paths); + + } else if (nul_term_line) + die(_("-z requires --stdin")); + unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ @@ -416,5 +461,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (!pathspec.nr) remove_branch_state(the_repository); + if (stdin_paths) { + while (stdin_nr) + free(stdin_paths[--stdin_nr]); + free(stdin_paths); + } + return update_ref_status; } diff --git a/cache-tree.c b/cache-tree.c index b13bfaf71e..706ffcf188 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -6,8 +6,8 @@ #include "object-store.h" #include "replace-object.h" -#ifndef DEBUG -#define DEBUG 0 +#ifndef DEBUG_CACHE_TREE +#define DEBUG_CACHE_TREE 0 #endif struct cache_tree *cache_tree(void) @@ -111,7 +111,7 @@ static int do_invalidate_path(struct cache_tree *it, const char *path) int namelen; struct cache_tree_sub *down; -#if DEBUG +#if DEBUG_CACHE_TREE fprintf(stderr, "cache-tree invalidate <%s>\n", path); #endif @@ -398,7 +398,7 @@ static int update_one(struct cache_tree *it, strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0'); strbuf_add(&buffer, oid->hash, the_hash_algo->rawsz); -#if DEBUG +#if DEBUG_CACHE_TREE fprintf(stderr, "cache-tree update-one %o %.*s\n", mode, entlen, path + baselen); #endif @@ -421,7 +421,7 @@ static int update_one(struct cache_tree *it, strbuf_release(&buffer); it->entry_count = to_invalidate ? -1 : i - *skip_count; -#if DEBUG +#if DEBUG_CACHE_TREE fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n", it->entry_count, it->subtree_nr, oid_to_hex(&it->oid)); @@ -462,7 +462,7 @@ static void write_one(struct strbuf *buffer, struct cache_tree *it, strbuf_add(buffer, path, pathlen); strbuf_addf(buffer, "%c%d %d\n", 0, it->entry_count, it->subtree_nr); -#if DEBUG +#if DEBUG_CACHE_TREE if (0 <= it->entry_count) fprintf(stderr, "cache-tree <%.*s> (%d ent, %d subtree) %s\n", pathlen, path, it->entry_count, it->subtree_nr, @@ -536,7 +536,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) size -= rawsz; } -#if DEBUG +#if DEBUG_CACHE_TREE if (0 <= it->entry_count) fprintf(stderr, "cache-tree <%s> (%d ent, %d subtree) %s\n", *buffer, it->entry_count, subtree_nr, diff --git a/compat/mingw.c b/compat/mingw.c index 8141f77189..35f0ee7f40 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -13,6 +13,19 @@ static const int delay[] = { 0, 1, 10, 20, 40 }; +void open_in_gdb(void) +{ + static struct child_process cp = CHILD_PROCESS_INIT; + extern char *_pgmptr; + + argv_array_pushl(&cp.args, "mintty", "gdb", NULL); + argv_array_pushf(&cp.args, "--pid=%d", getpid()); + cp.clean_on_exit = 1; + if (start_command(&cp) < 0) + die_errno("Could not start gdb"); + sleep(1); +} + int err_win_to_posix(DWORD winerr) { int error = ENOSYS; @@ -929,11 +942,19 @@ unsigned int sleep (unsigned int seconds) char *mingw_mktemp(char *template) { wchar_t wtemplate[MAX_PATH]; + int offset = 0; + if (xutftowcs_path(wtemplate, template) < 0) return NULL; + + if (is_dir_sep(template[0]) && !is_dir_sep(template[1]) && + iswalpha(wtemplate[0]) && wtemplate[1] == L':') { + /* We have an absolute path missing the drive prefix */ + offset = 2; + } if (!_wmktemp(wtemplate)) return NULL; - if (xwcstoutf(template, wtemplate, strlen(template) + 1) < 0) + if (xwcstoutf(template, wtemplate + offset, strlen(template) + 1) < 0) return NULL; return template; } @@ -1161,14 +1182,21 @@ static char *lookup_prog(const char *dir, int dirlen, const char *cmd, int isexe, int exe_only) { char path[MAX_PATH]; + wchar_t wpath[MAX_PATH]; snprintf(path, sizeof(path), "%.*s\\%s.exe", dirlen, dir, cmd); - if (!isexe && access(path, F_OK) == 0) + if (xutftowcs_path(wpath, path) < 0) + return NULL; + + if (!isexe && _waccess(wpath, F_OK) == 0) return xstrdup(path); - path[strlen(path)-4] = '\0'; - if ((!exe_only || isexe) && access(path, F_OK) == 0) - if (!(GetFileAttributes(path) & FILE_ATTRIBUTE_DIRECTORY)) + wpath[wcslen(wpath)-4] = '\0'; + if ((!exe_only || isexe) && _waccess(wpath, F_OK) == 0) { + if (!(GetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY)) { + path[strlen(path)-4] = '\0'; return xstrdup(path); + } + } return NULL; } @@ -1407,7 +1435,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen do_unset_environment_variables(); /* Determine whether or not we are associated to a console */ - cons = CreateFile("CONOUT$", GENERIC_WRITE, + cons = CreateFileA("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (cons == INVALID_HANDLE_VALUE) { @@ -1552,7 +1580,10 @@ static int try_shell_exec(const char *cmd, char *const *argv) prog = path_lookup(interpr, 1); if (prog) { int argc = 0; - const char **argv2; +#ifndef _MSC_VER + const +#endif + char **argv2; while (argv[argc]) argc++; ALLOC_ARRAY(argv2, argc + 1); argv2[0] = (char *)cmd; /* full path to the script file */ @@ -1705,142 +1736,10 @@ int mingw_putenv(const char *namevalue) return result ? 0 : -1; } -/* - * Note, this isn't a complete replacement for getaddrinfo. It assumes - * that service contains a numerical port, or that it is null. It - * does a simple search using gethostbyname, and returns one IPv4 host - * if one was found. - */ -static int WSAAPI getaddrinfo_stub(const char *node, const char *service, - const struct addrinfo *hints, - struct addrinfo **res) -{ - struct hostent *h = NULL; - struct addrinfo *ai; - struct sockaddr_in *sin; - - if (node) { - h = gethostbyname(node); - if (!h) - return WSAGetLastError(); - } - - ai = xmalloc(sizeof(struct addrinfo)); - *res = ai; - ai->ai_flags = 0; - ai->ai_family = AF_INET; - ai->ai_socktype = hints ? hints->ai_socktype : 0; - switch (ai->ai_socktype) { - case SOCK_STREAM: - ai->ai_protocol = IPPROTO_TCP; - break; - case SOCK_DGRAM: - ai->ai_protocol = IPPROTO_UDP; - break; - default: - ai->ai_protocol = 0; - break; - } - ai->ai_addrlen = sizeof(struct sockaddr_in); - if (hints && (hints->ai_flags & AI_CANONNAME)) - ai->ai_canonname = h ? xstrdup(h->h_name) : NULL; - else - ai->ai_canonname = NULL; - - sin = xcalloc(1, ai->ai_addrlen); - sin->sin_family = AF_INET; - /* Note: getaddrinfo is supposed to allow service to be a string, - * which should be looked up using getservbyname. This is - * currently not implemented */ - if (service) - sin->sin_port = htons(atoi(service)); - if (h) - sin->sin_addr = *(struct in_addr *)h->h_addr; - else if (hints && (hints->ai_flags & AI_PASSIVE)) - sin->sin_addr.s_addr = INADDR_ANY; - else - sin->sin_addr.s_addr = INADDR_LOOPBACK; - ai->ai_addr = (struct sockaddr *)sin; - ai->ai_next = NULL; - return 0; -} - -static void WSAAPI freeaddrinfo_stub(struct addrinfo *res) -{ - free(res->ai_canonname); - free(res->ai_addr); - free(res); -} - -static int WSAAPI getnameinfo_stub(const struct sockaddr *sa, socklen_t salen, - char *host, DWORD hostlen, - char *serv, DWORD servlen, int flags) -{ - const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; - if (sa->sa_family != AF_INET) - return EAI_FAMILY; - if (!host && !serv) - return EAI_NONAME; - - if (host && hostlen > 0) { - struct hostent *ent = NULL; - if (!(flags & NI_NUMERICHOST)) - ent = gethostbyaddr((const char *)&sin->sin_addr, - sizeof(sin->sin_addr), AF_INET); - - if (ent) - snprintf(host, hostlen, "%s", ent->h_name); - else if (flags & NI_NAMEREQD) - return EAI_NONAME; - else - snprintf(host, hostlen, "%s", inet_ntoa(sin->sin_addr)); - } - - if (serv && servlen > 0) { - struct servent *ent = NULL; - if (!(flags & NI_NUMERICSERV)) - ent = getservbyport(sin->sin_port, - flags & NI_DGRAM ? "udp" : "tcp"); - - if (ent) - snprintf(serv, servlen, "%s", ent->s_name); - else - snprintf(serv, servlen, "%d", ntohs(sin->sin_port)); - } - - return 0; -} - -static HMODULE ipv6_dll = NULL; -static void (WSAAPI *ipv6_freeaddrinfo)(struct addrinfo *res); -static int (WSAAPI *ipv6_getaddrinfo)(const char *node, const char *service, - const struct addrinfo *hints, - struct addrinfo **res); -static int (WSAAPI *ipv6_getnameinfo)(const struct sockaddr *sa, socklen_t salen, - char *host, DWORD hostlen, - char *serv, DWORD servlen, int flags); -/* - * gai_strerror is an inline function in the ws2tcpip.h header, so we - * don't need to try to load that one dynamically. - */ - -static void socket_cleanup(void) -{ - WSACleanup(); - if (ipv6_dll) - FreeLibrary(ipv6_dll); - ipv6_dll = NULL; - ipv6_freeaddrinfo = freeaddrinfo_stub; - ipv6_getaddrinfo = getaddrinfo_stub; - ipv6_getnameinfo = getnameinfo_stub; -} - static void ensure_socket_initialization(void) { WSADATA wsa; static int initialized = 0; - const char *libraries[] = { "ws2_32.dll", "wship6.dll", NULL }; - const char **name; if (initialized) return; @@ -1849,35 +1748,7 @@ static void ensure_socket_initialization(void) die("unable to initialize winsock subsystem, error %d", WSAGetLastError()); - for (name = libraries; *name; name++) { - ipv6_dll = LoadLibraryExA(*name, NULL, - LOAD_LIBRARY_SEARCH_SYSTEM32); - if (!ipv6_dll) - continue; - - ipv6_freeaddrinfo = (void (WSAAPI *)(struct addrinfo *)) - GetProcAddress(ipv6_dll, "freeaddrinfo"); - ipv6_getaddrinfo = (int (WSAAPI *)(const char *, const char *, - const struct addrinfo *, - struct addrinfo **)) - GetProcAddress(ipv6_dll, "getaddrinfo"); - ipv6_getnameinfo = (int (WSAAPI *)(const struct sockaddr *, - socklen_t, char *, DWORD, - char *, DWORD, int)) - GetProcAddress(ipv6_dll, "getnameinfo"); - if (!ipv6_freeaddrinfo || !ipv6_getaddrinfo || !ipv6_getnameinfo) { - FreeLibrary(ipv6_dll); - ipv6_dll = NULL; - } else - break; - } - if (!ipv6_freeaddrinfo || !ipv6_getaddrinfo || !ipv6_getnameinfo) { - ipv6_freeaddrinfo = freeaddrinfo_stub; - ipv6_getaddrinfo = getaddrinfo_stub; - ipv6_getnameinfo = getnameinfo_stub; - } - - atexit(socket_cleanup); + atexit((void(*)(void)) WSACleanup); initialized = 1; } @@ -1895,24 +1766,12 @@ struct hostent *mingw_gethostbyname(const char *host) return gethostbyname(host); } -void mingw_freeaddrinfo(struct addrinfo *res) -{ - ipv6_freeaddrinfo(res); -} - +#undef getaddrinfo int mingw_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { ensure_socket_initialization(); - return ipv6_getaddrinfo(node, service, hints, res); -} - -int mingw_getnameinfo(const struct sockaddr *sa, socklen_t salen, - char *host, DWORD hostlen, char *serv, DWORD servlen, - int flags) -{ - ensure_socket_initialization(); - return ipv6_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); + return getaddrinfo(node, service, hints, res); } int mingw_socket(int domain, int type, int protocol) @@ -2115,7 +1974,7 @@ struct passwd *getpwuid(int uid) return p; len = sizeof(user_name); - if (!GetUserName(user_name, &len)) { + if (!GetUserNameA(user_name, &len)) { initialized = 1; return NULL; } @@ -2279,8 +2138,34 @@ int mingw_raise(int sig) sigint_fn(SIGINT); return 0; +#if defined(_MSC_VER) + /* + * in the CRT defines 8 signals as being + * supported on the platform. Anything else causes + * an "Invalid signal or error" (which in DEBUG builds + * causes the Abort/Retry/Ignore dialog). We by-pass + * the CRT for things we already know will fail. + */ + /*case SIGINT:*/ + case SIGILL: + case SIGFPE: + case SIGSEGV: + case SIGTERM: + case SIGBREAK: + case SIGABRT: + case SIGABRT_COMPAT: + return raise(sig); + default: + errno = EINVAL; + return -1; + +#else + default: return raise(sig); + +#endif + } } @@ -2464,18 +2349,13 @@ static void setup_windows_environment(void) setenv("TERM", "cygwin", 1); } +#if !defined(_MSC_VER) /* * Disable MSVCRT command line wildcard expansion (__getmainargs called from * mingw startup code, see init.c in mingw runtime). */ int _CRT_glob = 0; - -typedef struct { - int newmode; -} _startupinfo; - -extern int __wgetmainargs(int *argc, wchar_t ***argv, wchar_t ***env, int glob, - _startupinfo *si); +#endif static NORETURN void die_startup(void) { @@ -2553,20 +2433,39 @@ static void maybe_redirect_std_handles(void) GENERIC_WRITE, FILE_FLAG_NO_BUFFERING); } -void mingw_startup(void) +#ifdef _MSC_VER +#ifdef _DEBUG +#include +#endif +#endif + +/* + * We implement wmain() and compile with -municode, which would + * normally ignore main(), but we call the latter from the former + * so that we can handle non-ASCII command-line parameters + * appropriately. + * + * To be more compatible with the core git code, we convert + * argv into UTF8 and pass them directly to main(). + */ +int wmain(int argc, const wchar_t **wargv) { - int i, maxlen, argc; - char *buffer; - wchar_t **wenv, **wargv; - _startupinfo si; + int i, maxlen, exit_status; + char *buffer, **save; + const char **argv; + +#ifdef _MSC_VER +#ifdef _DEBUG + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); +#endif + +#ifdef USE_MSVC_CRTDBG + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif +#endif maybe_redirect_std_handles(); - /* get wide char arguments and environment */ - si.newmode = 0; - if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0) - die_startup(); - /* determine size of argv and environ conversion buffer */ maxlen = wcslen(wargv[0]); for (i = 1; i < argc; i++) @@ -2576,9 +2475,16 @@ void mingw_startup(void) maxlen = 3 * maxlen + 1; buffer = malloc_startup(maxlen); - /* convert command line arguments and environment to UTF-8 */ + /* + * Create a UTF-8 version of w_argv. Also create a "save" copy + * to remember all the string pointers because parse_options() + * will remove claimed items from the argv that we pass down. + */ + ALLOC_ARRAY(argv, argc + 1); + ALLOC_ARRAY(save, argc + 1); for (i = 0; i < argc; i++) - __argv[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen); + argv[i] = save[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen); + argv[i] = save[i] = NULL; free(buffer); /* fix Windows specific environment settings */ @@ -2597,6 +2503,16 @@ void mingw_startup(void) /* initialize Unicode console */ winansi_init(); + + /* invoke the real main() using our utf8 version of argv. */ + exit_status = main(argc, argv); + + for (i = 0; i < argc; i++) + free(save[i]); + free(save); + free(argv); + + return exit_status; } int uname(struct utsname *buf) @@ -2611,3 +2527,17 @@ int uname(struct utsname *buf) "%u", (v >> 16) & 0x7fff); return 0; } + +const char *program_data_config(void) +{ + static struct strbuf path = STRBUF_INIT; + static unsigned initialized; + + if (!initialized) { + const char *env = mingw_getenv("PROGRAMDATA"); + if (env) + strbuf_addf(&path, "%s/Git/config", env); + initialized = 1; + } + return *path.buf ? path.buf : NULL; +} diff --git a/compat/mingw.h b/compat/mingw.h index 30d9fb3e36..5f52b13698 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -296,18 +296,10 @@ int mingw_gethostname(char *host, int namelen); struct hostent *mingw_gethostbyname(const char *host); #define gethostbyname mingw_gethostbyname -void mingw_freeaddrinfo(struct addrinfo *res); -#define freeaddrinfo mingw_freeaddrinfo - int mingw_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); #define getaddrinfo mingw_getaddrinfo -int mingw_getnameinfo(const struct sockaddr *sa, socklen_t salen, - char *host, DWORD hostlen, char *serv, DWORD servlen, - int flags); -#define getnameinfo mingw_getnameinfo - int mingw_socket(int domain, int type, int protocol); #define socket mingw_socket @@ -361,11 +353,13 @@ static inline int getrlimit(int resource, struct rlimit *rlp) #ifndef __MINGW64_VERSION_MAJOR #define off_t off64_t #define lseek _lseeki64 +#ifndef _MSC_VER struct timespec { time_t tv_sec; long tv_nsec; }; #endif +#endif struct mingw_stat { _dev_t st_dev; @@ -452,6 +446,8 @@ static inline void convert_slashes(char *path) #define PATH_SEP ';' extern char *mingw_query_user_email(void); #define query_user_email mingw_query_user_email +extern const char *program_data_config(void); +#define git_program_data_config program_data_config #if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800) #define PRIuMAX "I64u" #define PRId64 "I64d" @@ -571,18 +567,28 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen); extern CRITICAL_SECTION pinfo_cs; /* - * A replacement of main() that adds win32 specific initialization. + * Git, like most portable C applications, implements a main() function. On + * Windows, this main() function would receive parameters encoded in the + * current locale, but Git for Windows would prefer UTF-8 encoded parameters. + * + * To make that happen, we still declare main() here, and then declare and + * implement wmain() (which is the Unicode variant of main()) and compile with + * -municode. This wmain() function reencodes the parameters from UTF-16 to + * UTF-8 format, sets up a couple of other things as required on Windows, and + * then hands off to the main() function. */ +int wmain(int argc, const wchar_t **w_argv); +int main(int argc, const char **argv); -void mingw_startup(void); -#define main(c,v) dummy_decl_mingw_main(void); \ -static int mingw_main(c,v); \ -int main(int argc, const char **argv) \ -{ \ - mingw_startup(); \ - return mingw_main(__argc, (void *)__argv); \ -} \ -static int mingw_main(c,v) +/* + * For debugging: if a problem occurs, say, in a Git process that is spawned + * from another Git process which in turn is spawned from yet another Git + * process, it can be quite daunting to figure out what is going on. + * + * Call this function to open a new MinTTY (this assumes you are in Git for + * Windows' SDK) with a GDB that attaches to the current process right away. + */ +extern void open_in_gdb(void); /* * Used by Pthread API implementation for Windows diff --git a/compat/msvc.h b/compat/msvc.h index 29a8ce8204..1d7a8c6145 100644 --- a/compat/msvc.h +++ b/compat/msvc.h @@ -6,6 +6,10 @@ #include #include +#pragma warning(disable: 4018) /* signed/unsigned comparison */ +#pragma warning(disable: 4244) /* type conversion, possible loss of data */ +#pragma warning(disable: 4090) /* 'function' : different 'const' qualifiers (ALLOC_GROW etc.)*/ + /* porting function */ #define inline __inline #define __inline__ __inline @@ -18,6 +22,12 @@ #undef ERROR +#define ftello _ftelli64 + +typedef int sigset_t; +/* open for reading, writing, or both (not in fcntl.h) */ +#define O_ACCMODE (_O_RDONLY | _O_WRONLY | _O_RDWR) + #include "compat/mingw.h" #endif diff --git a/compat/obstack.h b/compat/obstack.h index ced94d0118..ae36ed6a66 100644 --- a/compat/obstack.h +++ b/compat/obstack.h @@ -496,7 +496,7 @@ __extension__ \ ( (h)->temp.tempint = (char *) (obj) - (char *) (h)->chunk, \ ((((h)->temp.tempint > 0 \ && (h)->temp.tempint < (h)->chunk_limit - (char *) (h)->chunk)) \ - ? (int) ((h)->next_free = (h)->object_base \ + ? (ptrdiff_t) ((h)->next_free = (h)->object_base \ = (h)->temp.tempint + (char *) (h)->chunk) \ : (((obstack_free) ((h), (h)->temp.tempint + (char *) (h)->chunk), 0), 0))) diff --git a/compat/poll/poll.c b/compat/poll/poll.c index 4459408c7d..a5e879ef30 100644 --- a/compat/poll/poll.c +++ b/compat/poll/poll.c @@ -150,7 +150,7 @@ win32_compute_revents (HANDLE h, int *p_sought) if (!once_only) { NtQueryInformationFile = (PNtQueryInformationFile) - GetProcAddress (GetModuleHandle ("ntdll.dll"), + GetProcAddress (GetModuleHandleA ("ntdll.dll"), "NtQueryInformationFile"); once_only = TRUE; } @@ -269,6 +269,20 @@ win32_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents) return happened; } +#include +#include "compat/win32/lazyload.h" + +static ULONGLONG CompatGetTickCount64(void) +{ + DECLARE_PROC_ADDR(kernel32.dll, ULONGLONG, GetTickCount64, void); + + if (!INIT_PROC_ADDR(GetTickCount64)) + return (ULONGLONG)GetTickCount(); + + return GetTickCount64(); +} +#define GetTickCount64 CompatGetTickCount64 + #else /* !MinGW */ /* Convert select(2) returned fd_sets into poll(2) revents values. */ diff --git a/compat/vcbuild/.gitignore b/compat/vcbuild/.gitignore new file mode 100644 index 0000000000..8f8b794ef3 --- /dev/null +++ b/compat/vcbuild/.gitignore @@ -0,0 +1,3 @@ +/vcpkg/ +/MSVC-DEFS-GEN +/VCPKG-DEFS diff --git a/compat/vcbuild/README b/compat/vcbuild/README index 60fd873fe8..81da36a93b 100644 --- a/compat/vcbuild/README +++ b/compat/vcbuild/README @@ -1,3 +1,54 @@ +The Steps to Build Git with VS2015 or VS2017 from the command line. + +1. Install the "vcpkg" open source package manager and build essential + third-party libraries. The steps for this have been captured in a + set of convenience scripts. These can be run from a stock Command + Prompt or from an SDK bash window: + + $ cd + $ ./compat/vcbuild/vcpkg_install.bat + + The vcpkg tools and all of the third-party sources will be installed + in this folder: + /compat/vcbuild/vcpkg/ + + A file will be created with a set of Makefile macros pointing to a + unified "include", "lib", and "bin" directory (release and debug) for + all of the required packages. This file will be included by the main + Makefile: + /compat/vcbuild/MSVC-DEFS-GEN + +2. OPTIONALLY copy the third-party *.dll and *.pdb files into the repo + root to make it easier to run and debug git.exe without having to + manipulate your PATH. This is especially true for debug sessions in + Visual Studio. + + Use ONE of the following forms which should match how you want to + compile git.exe. + + $ ./compat/vcbuild/vcpkg_copy_packages.bat debug + $ ./compat/vcbuild/vcpkg_copy_packages.bat release + +3. Build git using MSVC from an SDK bash window using one of the + following commands: + + $ make MSVC=1 + $ make MSVC=1 DEBUG=1 + +================================================================ + +Alternatively, run `make MSVC=1 vcxproj` and then load the generated +git.sln in Visual Studio. The initial build will install the vcpkg +system and build the dependencies automatically. This will take a while. + +Note that this will automatically add and commit the generated +.sln and .vcxproj files to the repo. You may want to drop this +commit before submitting a Pull Request.... + +Or maybe we should put the .sln/.vcxproj files in the .gitignore file +and not do this. I'm not sure. + +================================================================ The Steps of Build Git with VS2008 1. You need the build environment, which contains the Git dependencies diff --git a/compat/vcbuild/find_vs_env.bat b/compat/vcbuild/find_vs_env.bat new file mode 100644 index 0000000000..1232f200f7 --- /dev/null +++ b/compat/vcbuild/find_vs_env.bat @@ -0,0 +1,169 @@ +@ECHO OFF +REM ================================================================ +REM You can use either GCC (the default) or MSVC to build git +REM using the GIT-SDK command line tools. +REM $ make +REM $ make MSVC=1 +REM +REM GIT-SDK BASH windows inherit environment variables with all of +REM the bin/lib/include paths for GCC. It DOES NOT inherit values +REM for the corresponding MSVC tools. +REM +REM During normal (non-git) Windows development, you launch one +REM of the provided "developer command prompts" to set environment +REM variables for the MSVC tools. +REM +REM Therefore, to allow MSVC command line builds of git from BASH +REM and MAKE, we must blend these two different worlds. This script +REM attempts to do that. +REM ================================================================ +REM This BAT file starts in a plain (non-developer) command prompt, +REM searches for the "best" commmand prompt setup script, installs +REM it into the current CMD process, and exports the various MSVC +REM environment variables for use by MAKE. +REM +REM The output of this script should be written to a make "include +REM file" and referenced by the top-level Makefile. +REM +REM See "config.mak.uname" (look for compat/vcbuild/MSVC-DEFS-GEN). +REM ================================================================ +REM The provided command prompts are custom to each VS release and +REM filled with lots of internal knowledge (such as Registry settings); +REM even their names vary by release, so it is not appropriate for us +REM to look inside them. Rather, just run them in a subordinate +REM process and extract the settings we need. +REM ================================================================ +REM +REM Current (VS2017 and beyond) +REM ------------------- +REM Visual Studio 2017 introduced a new installation layout and +REM support for side-by-side installation of multiple versions of +REM VS2017. Furthermore, these can all coexist with installations +REM of previous versions of VS (which have a completely different +REM layout on disk). +REM +REM VS2017 Update 2 introduced a "vswhere.exe" command: +REM https://github.com/Microsoft/vswhere +REM https://blogs.msdn.microsoft.com/heaths/2017/02/25/vswhere-available/ +REM https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/ +REM +REM VS2015 +REM ------ +REM Visual Studio 2015 uses the traditional VcVarsAll. +REM +REM Earlier Versions +REM ---------------- +REM TODO +REM +REM ================================================================ +REM Note: Throughout this script we use "dir && " rather +REM than "if exist " because of script problems with pathnames +REM containing spaces. +REM ================================================================ + +REM Sanitize PATH to prevent git-sdk paths from confusing "wmic.exe" +REM (called internally in some of the system BAT files). +SET PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem; + +REM ================================================================ + +:current + SET vs_where=C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe + dir "%vs_where%" >nul 2>nul && GOTO have_vs_where + GOTO not_2017 + +:have_vs_where + REM Try to use VsWhere to get the location of VsDevCmd. + + REM Keep VsDevCmd from cd'ing away. + SET VSCMD_START_DIR=. + + REM Get the root of the VS product installation. + FOR /F "usebackq tokens=*" %%i IN (`"%vs_where%" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath`) DO @SET vs_ip=%%i + + SET vs_devcmd=%vs_ip%\Common7\Tools\VsDevCmd.bat + dir "%vs_devcmd%" >nul 2>nul && GOTO have_vs_devcmd + GOTO not_2017 + +:have_vs_devcmd + REM Use VsDevCmd to setup the environment of this process. + REM Setup CL for building 64-bit apps using 64-bit tools. + @call "%vs_devcmd%" -no_logo -arch=x64 -host_arch=x64 + + SET tgt=%VSCMD_ARG_TGT_ARCH% + + SET mn=%VCToolsInstallDir% + SET msvc_includes=-I"%mn%INCLUDE" + SET msvc_libs=-L"%mn%lib\%tgt%" + SET msvc_bin_dir=%mn%bin\Host%VSCMD_ARG_HOST_ARCH%\%tgt% + + SET sdk_dir=%WindowsSdkDir% + SET sdk_ver=%WindowsSDKVersion% + SET si=%sdk_dir%Include\%sdk_ver% + SET sdk_includes=-I"%si%ucrt" -I"%si%um" -I"%si%shared" + SET sl=%sdk_dir%lib\%sdk_ver% + SET sdk_libs=-L"%sl%ucrt\%tgt%" -L"%sl%um\%tgt%" + + SET vs_ver=%VisualStudioVersion% + + GOTO print_vars + +REM ================================================================ + +:not_2017 + REM See if VS2015 is installed. + + SET vs_2015_bat=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat + dir "%vs_2015_bat%" >nul 2>nul && GOTO have_vs_2015 + GOTO not_2015 + +:have_vs_2015 + REM Use VcVarsAll like the "x64 Native" command prompt. + REM Setup CL for building 64-bit apps using 64-bit tools. + @call "%vs_2015_bat%" amd64 + + REM Note that in VS2015 they use "x64" in some contexts and "amd64" in others. + SET mn=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\ + SET msvc_includes=-I"%mn%INCLUDE" + SET msvc_libs=-L"%mn%lib\amd64" + SET msvc_bin_dir=%mn%bin\amd64 + + SET sdk_dir=%WindowsSdkDir% + SET sdk_ver=%WindowsSDKVersion% + SET si=%sdk_dir%Include\%sdk_ver% + SET sdk_includes=-I"%si%ucrt" -I"%si%um" -I"%si%shared" -I"%si%winrt" + SET sl=%sdk_dir%lib\%sdk_ver% + SET sdk_libs=-L"%sl%ucrt\x64" -L"%sl%um\x64" + + SET vs_ver=%VisualStudioVersion% + + GOTO print_vars + +REM ================================================================ + +:not_2015 + REM TODO.... + echo TODO support older versions of VS. >&2 + EXIT /B 1 + +REM ================================================================ + +:print_vars + REM Dump the essential vars to stdout to allow the main + REM Makefile to include it. See config.mak.uname. + REM Include DOS-style and BASH-style path for bin dir. + + echo msvc_bin_dir=%msvc_bin_dir% + SET X1=%msvc_bin_dir:C:=/C% + SET X2=%X1:\=/% + echo msvc_bin_dir_msys=%X2% + + echo msvc_includes=%msvc_includes% + echo msvc_libs=%msvc_libs% + + echo sdk_includes=%sdk_includes% + echo sdk_libs=%sdk_libs% + + echo vs_ver=%vs_ver% + + EXIT /B 0 diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl index a87d0da512..3d6fa21c1e 100755 --- a/compat/vcbuild/scripts/clink.pl +++ b/compat/vcbuild/scripts/clink.pl @@ -12,32 +12,49 @@ use strict; my @args = (); my @cflags = (); +my @lflags = (); my $is_linking = 0; +my $is_debug = 0; while (@ARGV) { my $arg = shift @ARGV; - if ("$arg" =~ /^-[DIMGO]/) { + if ("$arg" eq "-DDEBUG") { + # Some vcpkg-based libraries have different names for release + # and debug versions. This hack assumes that -DDEBUG comes + # before any "-l*" flags. + $is_debug = 1; + } + if ("$arg" =~ /^-[DIMGOZ]/) { push(@cflags, $arg); } elsif ("$arg" eq "-o") { my $file_out = shift @ARGV; if ("$file_out" =~ /exe$/) { $is_linking = 1; + # Create foo.exe and foo.pdb push(@args, "-OUT:$file_out"); } else { + # Create foo.o and foo.o.pdb push(@args, "-Fo$file_out"); + push(@args, "-Fd$file_out.pdb"); } } elsif ("$arg" eq "-lz") { + if ($is_debug) { + push(@args, "zlibd.lib"); + } else{ push(@args, "zlib.lib"); + } } elsif ("$arg" eq "-liconv") { - push(@args, "iconv.lib"); + push(@args, "libiconv.lib"); } elsif ("$arg" eq "-lcrypto") { push(@args, "libeay32.lib"); } elsif ("$arg" eq "-lssl") { push(@args, "ssleay32.lib"); } elsif ("$arg" eq "-lcurl") { push(@args, "libcurl.lib"); + } elsif ("$arg" eq "-lexpat") { + push(@args, "expat.lib"); } elsif ("$arg" =~ /^-L/ && "$arg" ne "-LTCG") { $arg =~ s/^-L/-LIBPATH:/; - push(@args, $arg); + push(@lflags, $arg); } elsif ("$arg" =~ /^-R/) { # eat } else { @@ -45,10 +62,11 @@ while (@ARGV) { } } if ($is_linking) { + push(@args, @lflags); unshift(@args, "link.exe"); } else { unshift(@args, "cl.exe"); push(@args, @cflags); } -#printf("**** @args\n"); +printf(STDERR "**** @args\n\n\n") if (!defined($ENV{'QUIET_GEN'})); exit (system(@args) != 0); diff --git a/compat/vcbuild/vcpkg_copy_dlls.bat b/compat/vcbuild/vcpkg_copy_dlls.bat new file mode 100644 index 0000000000..13661c14f8 --- /dev/null +++ b/compat/vcbuild/vcpkg_copy_dlls.bat @@ -0,0 +1,39 @@ +@ECHO OFF +REM ================================================================ +REM This script is an optional step. It copies the *.dll and *.pdb +REM files (created by vcpkg_install.bat) into the top-level directory +REM of the repo so that you can type "./git.exe" and find them without +REM having to fixup your PATH. +REM +REM NOTE: Because the names of some DLL files change between DEBUG and +REM NOTE: RELEASE builds when built using "vcpkg.exe", you will need +REM NOTE: to copy up the corresponding version. +REM ================================================================ + + SETLOCAL EnableDelayedExpansion + + @FOR /F "delims=" %%D IN ("%~dp0") DO @SET cwd=%%~fD + cd %cwd% + + SET arch=x64-windows + SET inst=%cwd%vcpkg\installed\%arch% + + IF [%1]==[release] ( + echo Copying RELEASE mode DLLs to repo root... + ) ELSE IF [%1]==[debug] ( + SET inst=%inst%\debug + echo Copying DEBUG mode DLLs to repo root... + ) ELSE ( + echo ERROR: Invalid argument. + echo Usage: %~0 release + echo Usage: %~0 debug + EXIT /B 1 + ) + + xcopy /e/s/v/y %inst%\bin\*.dll ..\..\ + xcopy /e/s/v/y %inst%\bin\*.pdb ..\..\ + + xcopy /e/s/v/y %inst%\bin\*.dll ..\..\t\helper\ + xcopy /e/s/v/y %inst%\bin\*.pdb ..\..\t\helper\ + + EXIT /B 0 diff --git a/compat/vcbuild/vcpkg_install.bat b/compat/vcbuild/vcpkg_install.bat new file mode 100644 index 0000000000..3d086c39c3 --- /dev/null +++ b/compat/vcbuild/vcpkg_install.bat @@ -0,0 +1,81 @@ +@ECHO OFF +REM ================================================================ +REM This script installs the "vcpkg" source package manager and uses +REM it to build the third-party libraries that git requires when it +REM is built using MSVC. +REM +REM [1] Install VCPKG. +REM [a] Create /compat/vcbuild/vcpkg/ +REM [b] Download "vcpkg". +REM [c] Compile using the currently installed version of VS. +REM [d] Create /compat/vcbuild/vcpkg/vcpkg.exe +REM +REM [2] Install third-party libraries. +REM [a] Download each (which may also install CMAKE). +REM [b] Compile in RELEASE mode and install in: +REM vcpkg/installed//{bin,lib} +REM [c] Compile in DEBUG mode and install in: +REM vcpkg/installed//debug/{bin,lib} +REM [d] Install headers in: +REM vcpkg/installed//include +REM +REM [3] Create a set of MAKE definitions for the top-level +REM Makefile to allow "make MSVC=1" to find the above +REM third-party libraries. +REM [a] Write vcpkg/VCPGK-DEFS +REM +REM https://blogs.msdn.microsoft.com/vcblog/2016/09/19/vcpkg-a-tool-to-acquire-and-build-c-open-source-libraries-on-windows/ +REM https://github.com/Microsoft/vcpkg +REM https://vcpkg.readthedocs.io/en/latest/ +REM ================================================================ + + SETLOCAL EnableDelayedExpansion + + @FOR /F "delims=" %%D IN ("%~dp0") DO @SET cwd=%%~fD + cd %cwd% + + dir vcpkg\vcpkg.exe >nul 2>nul && GOTO :install_libraries + + echo Fetching vcpkg in %cwd%vcpkg + git.exe clone https://github.com/Microsoft/vcpkg vcpkg + IF ERRORLEVEL 1 ( EXIT /B 1 ) + + cd vcpkg + echo Building vcpkg + powershell -exec bypass scripts\bootstrap.ps1 + IF ERRORLEVEL 1 ( EXIT /B 1 ) + + echo Successfully installed %cwd%vcpkg\vcpkg.exe + +:install_libraries + SET arch=x64-windows + + echo Installing third-party libraries... + FOR %%i IN (zlib expat libiconv openssl libssh2 curl) DO ( + cd %cwd%vcpkg + SET p="packages\%%i_%arch%" + IF NOT EXIST "%p%" CALL :sub__install_one %%i + IF ERRORLEVEL 1 ( EXIT /B 1 ) + ) + +:install_defines + cd %cwd% + SET inst=%cwd%vcpkg\installed\%arch% + + echo vcpkg_inc=-I"%inst%\include">VCPKG-DEFS + echo vcpkg_rel_lib=-L"%inst%\lib">>VCPKG-DEFS + echo vcpkg_rel_bin="%inst%\bin">>VCPKG-DEFS + echo vcpkg_dbg_lib=-L"%inst%\debug\lib">>VCPKG-DEFS + echo vcpkg_dbg_bin="%inst%\debug\bin">>VCPKG-DEFS + + EXIT /B 0 + + +:sub__install_one + echo Installing package %1... + + .\vcpkg.exe install %1:%arch% + IF ERRORLEVEL 1 ( EXIT /B 1 ) + + echo Finished %1 + goto :EOF diff --git a/compat/winansi.c b/compat/winansi.c index f4f08237f9..0f44146985 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -544,7 +544,20 @@ static HANDLE swap_osfhnd(int fd, HANDLE new_handle) #ifdef DETECT_MSYS_TTY #include + +#if defined(_MSC_VER) + +typedef struct _OBJECT_NAME_INFORMATION +{ + UNICODE_STRING Name; + WCHAR NameBuffer[0]; +} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; + +#define ObjectNameInformation 1 + +#else #include +#endif static void detect_msys_tty(int fd) { @@ -620,12 +633,12 @@ void winansi_init(void) /* create a named pipe to communicate with the console thread */ xsnprintf(name, sizeof(name), "\\\\.\\pipe\\winansi%lu", GetCurrentProcessId()); - hwrite = CreateNamedPipe(name, PIPE_ACCESS_OUTBOUND, + hwrite = CreateNamedPipeA(name, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE | PIPE_WAIT, 1, BUFFER_SIZE, 0, 0, NULL); if (hwrite == INVALID_HANDLE_VALUE) die_lasterr("CreateNamedPipe failed"); - hread = CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); + hread = CreateFileA(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); if (hread == INVALID_HANDLE_VALUE) die_lasterr("CreateFile for named pipe failed"); diff --git a/config.c b/config.c index 24ad1a9854..279b9e8b18 100644 --- a/config.c +++ b/config.c @@ -1674,9 +1674,16 @@ static int do_git_config_sequence(const struct config_options *opts, repo_config = NULL; current_parsing_scope = CONFIG_SCOPE_SYSTEM; - if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) - ret += git_config_from_file(fn, git_etc_gitconfig(), - data); + if (git_config_system()) { + if (git_program_data_config() && + !access_or_die(git_program_data_config(), R_OK, 0)) + ret += git_config_from_file(fn, + git_program_data_config(), + data); + if (!access_or_die(git_etc_gitconfig(), R_OK, 0)) + ret += git_config_from_file(fn, git_etc_gitconfig(), + data); + } current_parsing_scope = CONFIG_SCOPE_GLOBAL; if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) diff --git a/config.mak.uname b/config.mak.uname index b37fa8424c..ab40fb7022 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -1,5 +1,9 @@ # Platform specific Makefile tweaks based on uname detection +# Define NO_SAFESEH if you need MSVC/Visual Studio to ignore the lack of +# Microsoft's Safe Exception Handling in libraries (such as zlib). +# Typically required for VS2013+/32-bit compilation on Vista+ versions. + uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not') uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') @@ -11,6 +15,21 @@ ifdef MSVC # avoid the MingW and Cygwin configuration sections uname_S := Windows uname_O := Windows + + # Generate and include makefile variables that point to the + # currently installed set of MSVC command line tools. +compat/vcbuild/MSVC-DEFS-GEN: compat/vcbuild/find_vs_env.bat + @"$<" | tr '\\' / >"$@" +include compat/vcbuild/MSVC-DEFS-GEN + + # See if vcpkg and the vcpkg-build versions of the third-party + # libraries that we use are installed. We include the result + # to get $(vcpkg_*) variables defined for the Makefile. +ifeq (,$(SKIP_VCPKG)) +compat/vcbuild/VCPKG-DEFS: compat/vcbuild/vcpkg_install.bat + @"$<" +include compat/vcbuild/VCPKG-DEFS +endif endif # We choose to avoid "if .. else if .. else .. endif endif" @@ -352,6 +371,19 @@ endif ifeq ($(uname_S),Windows) GIT_VERSION := $(GIT_VERSION).MSVC pathsep = ; + # Assume that this is built in Git for Windows' SDK + ifeq (MINGW32,$(MSYSTEM)) + prefix = /mingw32 + else + prefix = /mingw64 + endif + # Prepend MSVC 64-bit tool-chain to PATH. + # + # A regular Git Bash *does not* have cl.exe in its $PATH. As there is a + # link.exe next to, and required by, cl.exe, we have to prepend this + # onto the existing $PATH. + # + SANE_TOOL_PATH ?= $(msvc_bin_dir_msys) HAVE_ALLOCA_H = YesPlease NO_PREAD = YesPlease NEEDS_CRYPTO_WITH_SSL = YesPlease @@ -364,11 +396,14 @@ ifeq ($(uname_S),Windows) NO_STRCASESTR = YesPlease NO_STRLCPY = YesPlease NO_MEMMEM = YesPlease - # NEEDS_LIBICONV = YesPlease - NO_ICONV = YesPlease + NEEDS_LIBICONV = YesPlease NO_STRTOUMAX = YesPlease NO_MKDTEMP = YesPlease - SNPRINTF_RETURNS_BOGUS = YesPlease + NO_INTTYPES_H = YesPlease + # VS2015 with UCRT claims that snprintf and friends are C99 compliant, + # so we don't need this: + # + # SNPRINTF_RETURNS_BOGUS = YesPlease NO_SVN_TESTS = YesPlease RUNTIME_PREFIX = YesPlease HAVE_WPGMPTR = YesWeDo @@ -381,7 +416,6 @@ ifeq ($(uname_S),Windows) NO_REGEX = YesPlease NO_GETTEXT = YesPlease NO_PYTHON = YesPlease - BLK_SHA1 = YesPlease ETAGS_TARGET = ETAGS NO_POSIX_GOODIES = UnfortunatelyYes NATIVE_CRLF = YesPlease @@ -390,24 +424,48 @@ ifeq ($(uname_S),Windows) CC = compat/vcbuild/scripts/clink.pl AR = compat/vcbuild/scripts/lib.pl CFLAGS = - BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE + BASIC_CFLAGS = -nologo -I. -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE COMPAT_OBJS = compat/msvc.o compat/winansi.o \ compat/win32/pthread.o compat/win32/syslog.o \ compat/win32/dirent.o - COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\" - BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE - EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj + COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DDETECT_MSYS_TTY -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\" + BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -ENTRY:wmainCRTStartup -SUBSYSTEM:CONSOLE + # invalidcontinue.obj allows Git's source code to close the same file + # handle twice, or to access the osfhandle of an already-closed stdout + # See https://msdn.microsoft.com/en-us/library/ms235330.aspx + EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj kernel32.lib ntdll.lib PTHREAD_LIBS = lib = - BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1 + BASIC_CFLAGS += $(vcpkg_inc) $(sdk_includes) $(msvc_includes) ifndef DEBUG - BASIC_CFLAGS += -GL -Os -MD - BASIC_LDFLAGS += -LTCG + BASIC_CFLAGS += $(vcpkg_rel_lib) +else + BASIC_CFLAGS += $(vcpkg_dbg_lib) +endif + BASIC_CFLAGS += $(sdk_libs) $(msvc_libs) + + # Optionally enable memory leak reporting. + # BASIC_CLFAGS += -DUSE_MSVC_CRTDBG + BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1 + # Always give "-Zi" to the compiler and "-debug" to linker (even in + # release mode) to force a PDB to be generated (like RelWithDebInfo). + BASIC_CFLAGS += -Zi + BASIC_LDFLAGS += -debug + +ifdef NO_SAFESEH + LDFLAGS += -SAFESEH:NO +endif + +ifndef DEBUG + BASIC_CFLAGS += -GL -Gy -O2 -Oy- -MD -DNDEBUG + BASIC_LDFLAGS += -release -LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO /DEBUGTYPE:CV,FIXUP AR += -LTCG else - BASIC_CFLAGS += -Zi -MDd + BASIC_CFLAGS += -MDd -DDEBUG -D_DEBUG endif X = .exe + +compat/msvc.o: compat/msvc.c compat/mingw.c GIT-CFLAGS endif ifeq ($(uname_S),Interix) NO_INITGROUPS = YesPlease @@ -529,7 +587,6 @@ ifneq (,$(findstring MINGW,$(uname_S))) NO_STRTOUMAX = YesPlease NO_MKDTEMP = YesPlease NO_SVN_TESTS = YesPlease - NO_PERL_MAKEMAKER = YesPlease RUNTIME_PREFIX = YesPlease HAVE_WPGMPTR = YesWeDo NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease @@ -543,6 +600,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) ETAGS_TARGET = ETAGS NO_POSIX_GOODIES = UnfortunatelyYes DEFAULT_HELP_FORMAT = html + BASIC_LDFLAGS += -municode COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" COMPAT_OBJS += compat/mingw.o compat/winansi.o \ @@ -570,13 +628,21 @@ else ifeq ($(shell expr "$(uname_R)" : '2\.'),2) # MSys2 prefix = /usr/ + # Enable DEP + BASIC_LDFLAGS += -Wl,--nxcompat + # Enable ASLR (unless debugging) + ifneq (,$(findstring -O,$(CFLAGS))) + BASIC_LDFLAGS += -Wl,--dynamicbase + endif ifeq (MINGW32,$(MSYSTEM)) prefix = /mingw32 HOST_CPU = i686 + BASIC_LDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup endif ifeq (MINGW64,$(MSYSTEM)) prefix = /mingw64 HOST_CPU = x86_64 + BASIC_LDFLAGS += -Wl,--pic-executable,-e,mainCRTStartup else COMPAT_CFLAGS += -D_USE_32BIT_TIME_T BASIC_LDFLAGS += -Wl,--large-address-aware @@ -615,3 +681,77 @@ ifeq ($(uname_S),QNX) NO_STRCASESTR = YesPlease NO_STRLCPY = YesPlease endif + +vcxproj: + # Require clean work tree + git update-index -q --refresh && \ + git diff-files --quiet && \ + git diff-index --cached --quiet HEAD -- + + # Make .vcxproj files and add them + unset QUIET_GEN QUIET_BUILT_IN; \ + perl contrib/buildsystems/generate -g Vcxproj + git add -f git.sln {*,*/lib,t/helper/*}/*.vcxproj + + # Generate the LinkOrCopyBuiltins.targets file + (echo '' && \ + echo ' ' && \ + for name in $(BUILT_INS);\ + do \ + echo ' '; \ + done && \ + for name in $(REMOTE_CURL_ALIASES); \ + do \ + echo ' '; \ + done && \ + echo ' ' && \ + echo '') >git/LinkOrCopyBuiltins.targets + git add -f git/LinkOrCopyBuiltins.targets + + # Add command-list.h + $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 command-list.h + git add -f command-list.h + + # Add scripts + rm -f perl/perl.mak + $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 \ + $(SCRIPT_LIB) $(SCRIPT_SH_GEN) $(SCRIPT_PERL_GEN) + # Strip out the sane tool path, needed only for building + sed -i '/^git_broken_path_fix ".*/d' git-sh-setup + git add -f $(SCRIPT_LIB) $(SCRIPT_SH_GEN) $(SCRIPT_PERL_GEN) + + # Add Perl module + $(MAKE) $(LIB_PERL_GEN) + git add -f perl/build + + # Add bin-wrappers, for testing + rm -rf bin-wrappers/ + $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 $(test_bindir_programs) + # Ensure that the GIT_EXEC_PATH is a Unix-y one, and that the absolute + # path of the repository is not hard-coded (GIT_EXEC_PATH will be set + # by test-lib.sh according to the current setup) + sed -i -e 's/^\(GIT_EXEC_PATH\)=.*/test -n "$${\1##*:*}" ||\ + \1="$$(cygpath -u "$$\1")"/' \ + -e "s|'$$(pwd)|\"\$$GIT_EXEC_PATH\"'|g" bin-wrappers/* + # Ensure that test-* helpers find the .dll files copied to top-level + sed -i 's|^PATH=.*|&:"$$GIT_EXEC_PATH"|' bin-wrappers/test-* + # We do not want to force hard-linking builtins + sed -i 's|\(git\)-\([-a-z]*\)\.exe"|\1.exe" \2|g' \ + bin-wrappers/git-{receive-pack,upload-archive} + git add -f $(test_bindir_programs) + # remote-ext is a builtin, but invoked as if it were external + sed 's|receive-pack|remote-ext|g' \ + bin-wrappers/git-remote-ext + git add -f bin-wrappers/git-remote-ext + + # Add templates + $(MAKE) -C templates + git add -f templates/boilerplates.made templates/blt/ + + # Add build options + $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 GIT-BUILD-OPTIONS + git add -f GIT-BUILD-OPTIONS + + # Commit the whole shebang + git commit -m "Generate Visual Studio solution" \ + -m "Auto-generated by \`$(MAKE)$(MAKEFLAGS) $@\`" diff --git a/connect.c b/connect.c index 4813f005ab..e937b8ca04 100644 --- a/connect.c +++ b/connect.c @@ -915,6 +915,10 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host, if (protocol == PROTO_LOCAL) path = end; + else if (protocol == PROTO_FILE && *host != '/' && + !has_dos_drive_prefix(host) && + offset_1st_component(host - 2) > 1) + path = host - 2; /* include the leading "//" */ else if (protocol == PROTO_FILE && has_dos_drive_prefix(end)) path = end; /* "file://$(pwd)" may be "file://C:/projects/repo" */ else diff --git a/contrib/buildsystems/Generators.pm b/contrib/buildsystems/Generators.pm index 408ef714b8..aa4cbaa2ad 100644 --- a/contrib/buildsystems/Generators.pm +++ b/contrib/buildsystems/Generators.pm @@ -17,7 +17,7 @@ BEGIN { $me = dirname($me); if (opendir(D,"$me/Generators")) { foreach my $gen (readdir(D)) { - next if ($gen =~ /^\.\.?$/); + next unless ($gen =~ /\.pm$/); require "${me}/Generators/$gen"; $gen =~ s,\.pm,,; push(@AVAILABLE, $gen); diff --git a/contrib/buildsystems/Generators/Vcproj.pm b/contrib/buildsystems/Generators/Vcproj.pm index cfa74adcc2..737647e76a 100644 --- a/contrib/buildsystems/Generators/Vcproj.pm +++ b/contrib/buildsystems/Generators/Vcproj.pm @@ -3,6 +3,7 @@ require Exporter; use strict; use vars qw($VERSION); +use Digest::SHA qw(sha256_hex); our $VERSION = '1.00'; our(@ISA, @EXPORT, @EXPORT_OK, @AVAILABLE); @@ -12,59 +13,12 @@ BEGIN { push @EXPORT_OK, qw(generate); } -my $guid_index = 0; -my @GUIDS = ( - "{E07B9989-2BF7-4F21-8918-BE22BA467AC3}", - "{278FFB51-0296-4A44-A81A-22B87B7C3592}", - "{7346A2C4-F0FD-444F-9EBE-1AF23B2B5650}", - "{67F421AC-EB34-4D49-820B-3196807B423F}", - "{385DCFE1-CC8C-4211-A451-80FCFC31CA51}", - "{97CC46C5-D2CC-4D26-B634-E75792B79916}", - "{C7CE21FE-6EF8-4012-A5C7-A22BCEDFBA11}", - "{51575134-3FDF-42D1-BABD-3FB12669C6C9}", - "{0AE195E4-9823-4B87-8E6F-20C5614AF2FF}", - "{4B918255-67CA-43BB-A46C-26704B666E6B}", - "{18CCFEEF-C8EE-4CC1-A265-26F95C9F4649}", - "{5D5D90FA-01B7-4973-AFE5-CA88C53AC197}", - "{1F054320-036D-49E1-B384-FB5DF0BC8AC0}", - "{7CED65EE-F2D9-4171-825B-C7D561FE5786}", - "{8D341679-0F07-4664-9A56-3BA0DE88B9BC}", - "{C189FEDC-2957-4BD7-9FA4-7622241EA145}", - "{66844203-1B9F-4C53-9274-164FFF95B847}", - "{E4FEA145-DECC-440D-AEEA-598CF381FD43}", - "{73300A8E-C8AC-41B0-B555-4F596B681BA7}", - "{873FDEB1-D01D-40BF-A1BF-8BBC58EC0F51}", - "{7922C8BE-76C5-4AC6-8BF7-885C0F93B782}", - "{E245D370-308B-4A49-BFC1-1E527827975F}", - "{F6FA957B-66FC-4ED7-B260-E59BBE4FE813}", - "{E6055070-0198-431A-BC49-8DB6CEE770AE}", - "{54159234-C3EB-43DA-906B-CE5DA5C74654}", - "{594CFC35-0B60-46F6-B8EF-9983ACC1187D}", - "{D93FCAB7-1F01-48D2-B832-F761B83231A5}", - "{DBA5E6AC-E7BE-42D3-8703-4E787141526E}", - "{6171953F-DD26-44C7-A3BE-CC45F86FC11F}", - "{9E19DDBE-F5E4-4A26-A2FE-0616E04879B8}", - "{AE81A615-99E3-4885-9CE0-D9CAA193E867}", - "{FBF4067E-1855-4F6C-8BCD-4D62E801A04D}", - "{17007948-6593-4AEB-8106-F7884B4F2C19}", - "{199D4C8D-8639-4DA6-82EF-08668C35DEE0}", - "{E085E50E-C140-4CF3-BE4B-094B14F0DDD6}", - "{00785268-A9CC-4E40-AC29-BAC0019159CE}", - "{4C06F56A-DCDB-46A6-B67C-02339935CF12}", - "{3A62D3FD-519E-4EC9-8171-D2C1BFEA022F}", - "{3A62D3FD-519E-4EC9-8171-D2C1BFEA022F}", - "{9392EB58-D7BA-410B-B1F0-B2FAA6BC89A7}", - "{2ACAB2D5-E0CE-4027-BCA0-D78B2D7A6C66}", - "{86E216C3-43CE-481A-BCB2-BE5E62850635}", - "{FB631291-7923-4B91-9A57-7B18FDBB7A42}", - "{0A176EC9-E934-45B8-B87F-16C7F4C80039}", - "{DF55CA80-46E8-4C53-B65B-4990A23DD444}", - "{3A0F9895-55D2-4710-BE5E-AD7498B5BF44}", - "{294BDC5A-F448-48B6-8110-DD0A81820F8C}", - "{4B9F66E9-FAC9-47AB-B1EF-C16756FBFD06}", - "{72EA49C6-2806-48BD-B81B-D4905102E19C}", - "{5728EB7E-8929-486C-8CD5-3238D060E768}" -); +sub generate_guid ($) { + my $hex = sha256_hex($_[0]); + $hex =~ s/^(.{8})(.{4})(.{4})(.{4})(.{12}).*/{$1-$2-$3-$4-$5}/; + $hex =~ tr/a-z/A-Z/; + return $hex; +} sub generate { my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_; @@ -92,9 +46,8 @@ sub createLibProject { $target =~ s/\//_/g; $target =~ s/\.a//; - my $uuid = $GUIDS[$guid_index]; + my $uuid = generate_guid($libname); $$build_structure{"LIBS_${target}_GUID"} = $uuid; - $guid_index += 1; my @srcs = sort(map("$rel_dir\\$_", @{$$build_structure{"LIBS_${libname}_SOURCES"}})); my @sources; @@ -106,6 +59,8 @@ sub createLibProject { my $includes= join(";", sort(map(""$rel_dir\\$_"", @{$$build_structure{"LIBS_${libname}_INCLUDES"}}))); my $cflags = join(" ", sort(@{$$build_structure{"LIBS_${libname}_CFLAGS"}})); $cflags =~ s/\"/"/g; + $cflags =~ s//>/g; my $cflags_debug = $cflags; $cflags_debug =~ s/-MT/-MTd/; @@ -127,6 +82,8 @@ sub createLibProject { $defines =~ s/-D//g; $defines =~ s/\"/\\"/g; + $defines =~ s//>/g; $defines =~ s/\'//g; $includes =~ s/-I//g; mkdir "$target" || die "Could not create the directory $target for lib project!\n"; @@ -162,9 +119,6 @@ sub createLibProject { - @@ -228,9 +182,6 @@ sub createLibProject { - @@ -311,9 +262,8 @@ sub createAppProject { $target =~ s/\//_/g; $target =~ s/\.exe//; - my $uuid = $GUIDS[$guid_index]; + my $uuid = generate_guid($appname); $$build_structure{"APPS_${target}_GUID"} = $uuid; - $guid_index += 1; my @srcs = sort(map("$rel_dir\\$_", @{$$build_structure{"APPS_${appname}_SOURCES"}})); my @sources; @@ -325,6 +275,8 @@ sub createAppProject { my $includes= join(";", sort(map(""$rel_dir\\$_"", @{$$build_structure{"APPS_${appname}_INCLUDES"}}))); my $cflags = join(" ", sort(@{$$build_structure{"APPS_${appname}_CFLAGS"}})); $cflags =~ s/\"/"/g; + $cflags =~ s//>/g; my $cflags_debug = $cflags; $cflags_debug =~ s/-MT/-MTd/; @@ -351,6 +303,8 @@ sub createAppProject { $defines =~ s/-D//g; $defines =~ s/\"/\\"/g; + $defines =~ s//>/g; $defines =~ s/\'//g; $defines =~ s/\\\\/\\/g; $includes =~ s/-I//g; @@ -387,9 +341,6 @@ sub createAppProject { - @@ -458,9 +409,6 @@ sub createAppProject { - @@ -561,20 +509,18 @@ sub createGlueProject { foreach (@apps) { $_ =~ s/\//_/g; $_ =~ s/\.exe//; - push(@tmp, $_); + if ($_ eq "git" ) { + unshift(@tmp, $_); + } else { + push(@tmp, $_); + } } @apps = @tmp; open F, ">git.sln" || die "Could not open git.sln for writing!\n"; binmode F, ":crlf"; print F "$SLN_HEAD"; - foreach (@libs) { - my $libname = $_; - my $uuid = $build_structure{"LIBS_${libname}_GUID"}; - print F "$SLN_PRE"; - print F "\"${libname}\", \"${libname}\\${libname}.vcproj\", \"${uuid}\""; - print F "$SLN_POST"; - } + my $uuid_libgit = $build_structure{"LIBS_libgit_GUID"}; my $uuid_xdiff_lib = $build_structure{"LIBS_xdiff_lib_GUID"}; foreach (@apps) { @@ -588,6 +534,13 @@ sub createGlueProject { print F " EndProjectSection"; print F "$SLN_POST"; } + foreach (@libs) { + my $libname = $_; + my $uuid = $build_structure{"LIBS_${libname}_GUID"}; + print F "$SLN_PRE"; + print F "\"${libname}\", \"${libname}\\${libname}.vcproj\", \"${uuid}\""; + print F "$SLN_POST"; + } print F << "EOM"; Global @@ -599,17 +552,17 @@ EOM print F << "EOM"; GlobalSection(ProjectConfigurationPlatforms) = postSolution EOM - foreach (@libs) { - my $libname = $_; - my $uuid = $build_structure{"LIBS_${libname}_GUID"}; + foreach (@apps) { + my $appname = $_; + my $uuid = $build_structure{"APPS_${appname}_GUID"}; print F "\t\t${uuid}.Debug|Win32.ActiveCfg = Debug|Win32\n"; print F "\t\t${uuid}.Debug|Win32.Build.0 = Debug|Win32\n"; print F "\t\t${uuid}.Release|Win32.ActiveCfg = Release|Win32\n"; print F "\t\t${uuid}.Release|Win32.Build.0 = Release|Win32\n"; } - foreach (@apps) { - my $appname = $_; - my $uuid = $build_structure{"APPS_${appname}_GUID"}; + foreach (@libs) { + my $libname = $_; + my $uuid = $build_structure{"LIBS_${libname}_GUID"}; print F "\t\t${uuid}.Debug|Win32.ActiveCfg = Debug|Win32\n"; print F "\t\t${uuid}.Debug|Win32.Build.0 = Debug|Win32\n"; print F "\t\t${uuid}.Release|Win32.ActiveCfg = Release|Win32\n"; diff --git a/contrib/buildsystems/Generators/Vcxproj.pm b/contrib/buildsystems/Generators/Vcxproj.pm new file mode 100644 index 0000000000..00ef26fddd --- /dev/null +++ b/contrib/buildsystems/Generators/Vcxproj.pm @@ -0,0 +1,383 @@ +package Generators::Vcxproj; +require Exporter; + +use strict; +use vars qw($VERSION); +use Digest::SHA qw(sha256_hex); + +our $VERSION = '1.00'; +our(@ISA, @EXPORT, @EXPORT_OK, @AVAILABLE); +@ISA = qw(Exporter); + +BEGIN { + push @EXPORT_OK, qw(generate); +} + +sub generate_guid ($) { + my $hex = sha256_hex($_[0]); + $hex =~ s/^(.{8})(.{4})(.{4})(.{4})(.{12}).*/{$1-$2-$3-$4-$5}/; + $hex =~ tr/a-z/A-Z/; + return $hex; +} + +sub generate { + my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_; + my @libs = @{$build_structure{"LIBS"}}; + foreach (@libs) { + createProject($_, $git_dir, $out_dir, $rel_dir, \%build_structure, 1); + } + + my @apps = @{$build_structure{"APPS"}}; + foreach (@apps) { + createProject($_, $git_dir, $out_dir, $rel_dir, \%build_structure, 0); + } + + createGlueProject($git_dir, $out_dir, $rel_dir, %build_structure); + return 0; +} + +sub createProject { + my ($name, $git_dir, $out_dir, $rel_dir, $build_structure, $static_library) = @_; + my $label = $static_library ? "lib" : "app"; + my $prefix = $static_library ? "LIBS_" : "APPS_"; + my $config_type = $static_library ? "StaticLibrary" : "Application"; + print "Generate $name vcxproj $label project\n"; + my $cdup = $name; + $cdup =~ s/[^\/]+/../g; + $cdup =~ s/\//\\/g; + $rel_dir = $rel_dir eq "." ? $cdup : "$cdup\\$rel_dir"; + $rel_dir =~ s/\//\\/g; + + my $target = $name; + if ($static_library) { + $target =~ s/\.a//; + } else { + $target =~ s/\.exe//; + } + + my $uuid = generate_guid($name); + $$build_structure{"$prefix${target}_GUID"} = $uuid; + my $vcxproj = $target; + $vcxproj =~ s/(.*\/)?(.*)/$&\/$2.vcxproj/; + $vcxproj =~ s/([^\/]*)(\/lib)\/(lib.vcxproj)/$1$2\/$1_$3/; + $$build_structure{"$prefix${target}_VCXPROJ"} = $vcxproj; + + my @srcs = sort(map("$rel_dir\\$_", @{$$build_structure{"$prefix${name}_SOURCES"}})); + my @sources; + foreach (@srcs) { + $_ =~ s/\//\\/g; + push(@sources, $_); + } + my $defines = join(";", sort(@{$$build_structure{"$prefix${name}_DEFINES"}})); + my $includes= join(";", sort(map { s/^-I//; s/\//\\/g; File::Spec->file_name_is_absolute($_) ? $_ : "$rel_dir\\$_" } @{$$build_structure{"$prefix${name}_INCLUDES"}})); + my $cflags = join(" ", sort(map { s/^-[GLMOWZ].*//; s/.* .*/"$&"/; $_; } @{$$build_structure{"$prefix${name}_CFLAGS"}})); + $cflags =~ s//>/g; + + my $libs_release = "\n "; + my $libs_debug = "\n "; + if (!$static_library) { + $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}})); + $libs_debug = $libs_release; + $libs_debug =~ s/zlib\.lib/zlibd\.lib/; + } + + $defines =~ s/-D//g; + $defines =~ s//>/g; + $defines =~ s/\'//g; + + die "Could not create the directory $target for $label project!\n" unless (-d "$target" || mkdir "$target"); + + open F, ">$vcxproj" or die "Could not open $vcxproj for writing!\n"; + binmode F, ":crlf :utf8"; + print F chr(0xFEFF); + print F << "EOM"; + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + $uuid + Win32Proj + x86-windows + x64-windows + $cdup\\compat\\vcbuild\\vcpkg\\installed\\\$(VCPKGArch) + \$(VCPKGArchDirectory)\\debug\\bin + \$(VCPKGArchDirectory)\\debug\\lib + \$(VCPKGArchDirectory)\\bin + \$(VCPKGArchDirectory)\\lib + \$(VCPKGArchDirectory)\\include + $libs_debug + $libs_release + + + + true + true + + + false + true + + + $config_type + v140 + + ..\\ + + + + + + + + + + + + + false + true + + + + $cflags %(AdditionalOptions) + $cdup;$cdup\\compat;$cdup\\compat\\regex;$cdup\\compat\\win32;$cdup\\compat\\poll;$cdup\\compat\\vcbuild\\include;\$(VCPKGIncludeDirectory);%(AdditionalIncludeDirectories) + + OnlyExplicitInline + + ProgramDatabase + + + true + + + \$(VCPKGLibDirectory);%(AdditionalLibraryDirectories) + \$(VCPKGLibs);\$(AdditionalDependencies) + invalidcontinue.obj %(AdditionalOptions) + wmainCRTStartup + $cdup\\compat\\win32\\git.manifest + Console + +EOM + if ($target eq 'libgit') { + print F << "EOM"; + + Initialize VCPKG + del "$cdup\\compat\\vcbuild\\vcpkg" + call "$cdup\\compat\\vcbuild\\vcpkg_install.bat" + +EOM + } + print F << "EOM"; + + + + MachineX86 + + + + + Disabled + WIN32;_DEBUG;$defines;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + + + true + + + + + MaxSpeed + true + WIN32;NDEBUG;$defines;%(PreprocessorDefinitions) + MultiThreadedDLL + true + Speed + + + true + true + true + + + +EOM + foreach(@sources) { + print F << "EOM"; + +EOM + } + print F << "EOM"; + +EOM + if (!$static_library || $target =~ 'vcs-svn') { + my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"}; + my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"}; + + print F << "EOM"; + + + $uuid_libgit + false + + + $uuid_xdiff_lib + false + +EOM + if ($name =~ /(test-(line-buffer|svn-fe)|^git-remote-testsvn)\.exe$/) { + my $uuid_vcs_svn_lib = $$build_structure{"LIBS_vcs-svn/lib_GUID"}; + print F << "EOM"; + + $uuid_vcs_svn_lib + false + +EOM + } + print F << "EOM"; + +EOM + } + print F << "EOM"; + +EOM + if (!$static_library) { + print F << "EOM"; + + + + + + +EOM + } + if ($target eq 'git') { + print F " \n"; + } + print F << "EOM"; + +EOM + close F; +} + +sub createGlueProject { + my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_; + print "Generate solutions file\n"; + $rel_dir = "..\\$rel_dir"; + $rel_dir =~ s/\//\\/g; + my $SLN_HEAD = "Microsoft Visual Studio Solution File, Format Version 11.00\n# Visual Studio 2010\n"; + my $SLN_PRE = "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = "; + my $SLN_POST = "\nEndProject\n"; + + my @libs = @{$build_structure{"LIBS"}}; + my @tmp; + foreach (@libs) { + $_ =~ s/\.a//; + push(@tmp, $_); + } + @libs = @tmp; + + my @apps = @{$build_structure{"APPS"}}; + @tmp = (); + foreach (@apps) { + $_ =~ s/\.exe//; + if ($_ eq "git" ) { + unshift(@tmp, $_); + } else { + push(@tmp, $_); + } + } + @apps = @tmp; + + open F, ">git.sln" || die "Could not open git.sln for writing!\n"; + binmode F, ":crlf :utf8"; + print F chr(0xFEFF); + print F "$SLN_HEAD"; + + foreach (@apps) { + my $appname = $_; + my $uuid = $build_structure{"APPS_${appname}_GUID"}; + print F "$SLN_PRE"; + my $vcxproj = $build_structure{"APPS_${appname}_VCXPROJ"}; + $vcxproj =~ s/\//\\/g; + $appname =~ s/.*\///; + print F "\"${appname}\", \"${vcxproj}\", \"${uuid}\""; + print F "$SLN_POST"; + } + foreach (@libs) { + my $libname = $_; + my $uuid = $build_structure{"LIBS_${libname}_GUID"}; + print F "$SLN_PRE"; + my $vcxproj = $build_structure{"LIBS_${libname}_VCXPROJ"}; + $vcxproj =~ s/\//\\/g; + $libname =~ s/\//_/g; + print F "\"${libname}\", \"${vcxproj}\", \"${uuid}\""; + print F "$SLN_POST"; + } + + print F << "EOM"; +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection +EOM + print F << "EOM"; + GlobalSection(ProjectConfigurationPlatforms) = postSolution +EOM + foreach (@apps) { + my $appname = $_; + my $uuid = $build_structure{"APPS_${appname}_GUID"}; + print F "\t\t${uuid}.Debug|x64.ActiveCfg = Debug|x64\n"; + print F "\t\t${uuid}.Debug|x64.Build.0 = Debug|x64\n"; + print F "\t\t${uuid}.Debug|x86.ActiveCfg = Debug|Win32\n"; + print F "\t\t${uuid}.Debug|x86.Build.0 = Debug|Win32\n"; + print F "\t\t${uuid}.Release|x64.ActiveCfg = Release|x64\n"; + print F "\t\t${uuid}.Release|x64.Build.0 = Release|x64\n"; + print F "\t\t${uuid}.Release|x86.ActiveCfg = Release|Win32\n"; + print F "\t\t${uuid}.Release|x86.Build.0 = Release|Win32\n"; + } + foreach (@libs) { + my $libname = $_; + my $uuid = $build_structure{"LIBS_${libname}_GUID"}; + print F "\t\t${uuid}.Debug|x64.ActiveCfg = Debug|x64\n"; + print F "\t\t${uuid}.Debug|x64.Build.0 = Debug|x64\n"; + print F "\t\t${uuid}.Debug|x86.ActiveCfg = Debug|Win32\n"; + print F "\t\t${uuid}.Debug|x86.Build.0 = Debug|Win32\n"; + print F "\t\t${uuid}.Release|x64.ActiveCfg = Release|x64\n"; + print F "\t\t${uuid}.Release|x64.Build.0 = Release|x64\n"; + print F "\t\t${uuid}.Release|x86.ActiveCfg = Release|Win32\n"; + print F "\t\t${uuid}.Release|x86.Build.0 = Release|Win32\n"; + } + + print F << "EOM"; + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal +EOM + close F; +} + +1; diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl index 23da787dc5..fba8a3f056 100755 --- a/contrib/buildsystems/engine.pl +++ b/contrib/buildsystems/engine.pl @@ -12,6 +12,7 @@ use File::Basename; use File::Spec; use Cwd; use Generators; +use Text::ParseWords; my (%build_structure, %compile_options, @makedry); my $out_dir = getcwd(); @@ -31,6 +32,7 @@ generate usage: -g --gen Specify the buildsystem generator (default: $gen) Available: $genlist -o --out Specify output directory generation (default: .) + --make-out Write the output of GNU Make into a file -i --in Specify input file, instead of running GNU Make -h,-? --help This help EOM @@ -38,6 +40,7 @@ EOM } # Parse command-line options +my $make_out; while (@ARGV) { my $arg = shift @ARGV; if ("$arg" eq "-h" || "$arg" eq "--help" || "$arg" eq "-?") { @@ -45,6 +48,8 @@ while (@ARGV) { exit(0); } elsif("$arg" eq "--out" || "$arg" eq "-o") { $out_dir = shift @ARGV; + } elsif("$arg" eq "--make-out") { + $make_out = shift @ARGV; } elsif("$arg" eq "--gen" || "$arg" eq "-g") { $gen = shift @ARGV; } elsif("$arg" eq "--in" || "$arg" eq "-i") { @@ -52,6 +57,8 @@ while (@ARGV) { open(F, "<$infile") || die "Couldn't open file $infile"; @makedry = ; close(F); + } else { + die "Unknown option: " . $arg; } } @@ -72,7 +79,19 @@ Running GNU Make to figure out build structure... EOM # Pipe a make --dry-run into a variable, if not already loaded from file -@makedry = `cd $git_dir && make -n MSVC=1 V=1 2>/dev/null` if !@makedry; +# Capture the make dry stderr to file for review (will be empty for a release build). + +my $ErrsFile = "msvc-build-makedryerrors.txt"; +@makedry = `make -C $git_dir -n MSVC=1 SKIP_VCPKG=1 V=1 2>$ErrsFile` +if !@makedry; +# test for an empty Errors file and remove it +unlink $ErrsFile if -f -z $ErrsFile; + +if (defined $make_out) { + open OUT, ">" . $make_out; + print OUT @makedry; + close OUT; +} # Parse the make output into usable info parseMakeOutput(); @@ -140,6 +159,12 @@ sub parseMakeOutput next; } + if ($text =~ /^(mkdir|msgfmt) /) { + # options to the Portable Object translations + # the line "mkdir ... && msgfmt ..." contains no linker options + next; + } + if($text =~ / -c /) { # compilation handleCompileLine($text, $line); @@ -231,7 +256,7 @@ sub removeDuplicates sub handleCompileLine { my ($line, $lineno) = @_; - my @parts = split(' ', $line); + my @parts = shellwords($line); my $sourcefile; shift(@parts); # ignore cmd while (my $part = shift @parts) { @@ -265,7 +290,7 @@ sub handleLibLine my (@objfiles, @lflags, $libout, $part); # kill cmd and rm 'prefix' $line =~ s/^rm -f .* && .* rcs //; - my @parts = split(' ', $line); + my @parts = shellwords($line); while ($part = shift @parts) { if ($part =~ /^-/) { push(@lflags, $part); @@ -282,7 +307,7 @@ sub handleLibLine # exit(1); foreach (@objfiles) { my $sourcefile = $_; - $sourcefile =~ s/\.o/.c/; + $sourcefile =~ s/\.o$/.c/; push(@sources, $sourcefile); push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}}); push(@defines, @{$compile_options{"${sourcefile}_DEFINES"}}); @@ -306,7 +331,7 @@ sub handleLinkLine { my ($line, $lineno) = @_; my (@objfiles, @lflags, @libs, $appout, $part); - my @parts = split(' ', $line); + my @parts = shellwords($line); shift(@parts); # ignore cmd while ($part = shift @parts) { if ($part =~ /^-IGNORE/) { @@ -317,26 +342,36 @@ sub handleLinkLine $appout = shift @parts; } elsif ("$part" eq "-lz") { push(@libs, "zlib.lib"); - } elsif ("$part" eq "-lcrypto") { + } elsif ("$part" eq "-lcrypto") { push(@libs, "libeay32.lib"); } elsif ("$part" eq "-lssl") { push(@libs, "ssleay32.lib"); - } elsif ($part =~ /^-/) { + } elsif ("$part" eq "-lcurl") { + push(@libs, "libcurl.lib"); + } elsif ("$part" eq "-lexpat") { + push(@libs, "expat.lib"); + } elsif ("$part" eq "-liconv") { + push(@libs, "libiconv.lib"); + } elsif ($part =~ /^[-\/]/) { push(@lflags, $part); } elsif ($part =~ /\.(a|lib)$/) { $part =~ s/\.a$/.lib/; push(@libs, $part); - } elsif ($part =~ /\.(o|obj)$/) { + } elsif ($part eq 'invalidcontinue.obj') { + # ignore - known to MSVC + } elsif ($part =~ /\.o$/) { push(@objfiles, $part); + } elsif ($part =~ /\.obj$/) { + # do nothing, 'make' should not be producing .obj, only .o files } else { - die "Unhandled lib option @ line $lineno: $part"; + die "Unhandled link option @ line $lineno: $part"; } } # print "AppOut: '$appout'\nLFlags: @lflags\nLibs : @libs\nOfiles: @objfiles\n"; # exit(1); foreach (@objfiles) { my $sourcefile = $_; - $sourcefile =~ s/\.o/.c/; + $sourcefile =~ s/\.o$/.c/; push(@sources, $sourcefile); push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}}); push(@defines, @{$compile_options{"${sourcefile}_DEFINES"}}); diff --git a/credential.c b/credential.c index 62be651b03..e9108a9e8a 100644 --- a/credential.c +++ b/credential.c @@ -136,7 +136,9 @@ static void credential_getpass(struct credential *c) { if (!c->username) c->username = credential_ask_one("Username", c, - PROMPT_ASKPASS|PROMPT_ECHO); + (getenv("GIT_ASKPASS") ? + PROMPT_ASKPASS : 0) | + PROMPT_ECHO); if (!c->password) c->password = credential_ask_one("Password", c, PROMPT_ASKPASS); diff --git a/diffcore-rename.c b/diffcore-rename.c index 07bd34b631..5bfc5f6c22 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -82,6 +82,18 @@ static struct diff_rename_src *register_rename_src(struct diff_filepair *p) first = 0; last = rename_src_nr; + + if (last > 0) { + struct diff_rename_src *src = &(rename_src[last-1]); + int cmp = strcmp(one->path, src->p->one->path); + if (!cmp) + return src; + if (cmp > 0) { + first = last; + goto append_it; + } + } + while (last > first) { int next = (last + first) >> 1; struct diff_rename_src *src = &(rename_src[next]); @@ -95,6 +107,7 @@ static struct diff_rename_src *register_rename_src(struct diff_filepair *p) first = next+1; } +append_it: /* insert to make it at "first" */ ALLOC_GROW(rename_src, rename_src_nr + 1, rename_src_alloc); rename_src_nr++; diff --git a/git-compat-util.h b/git-compat-util.h index 6573808ebd..99d7ac3fa3 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -1,6 +1,15 @@ #ifndef GIT_COMPAT_UTIL_H #define GIT_COMPAT_UTIL_H +#ifdef USE_MSVC_CRTDBG +/* + * For these to work they must appear very early in each + * file -- before most of the standard header files. + */ +#include +#include +#endif + #define _FILE_OFFSET_BITS 64 @@ -411,6 +420,10 @@ static inline char *git_find_last_dir_sep(const char *path) #endif #endif +#ifndef git_program_data_config +#define git_program_data_config() NULL +#endif + #if defined(__HP_cc) && (__HP_cc >= 61000) #define NORETURN __attribute__((noreturn)) #define NORETURN_PTR diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index d13f02da95..fc00d5946a 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -431,6 +431,7 @@ END sub safe_pipe_capture { my @output; if (my $pid = open my $child, '-|') { + binmode($child, ":crlf"); @output = (<$child>); close $child or die join(' ',@_).": $! $?"; } else { diff --git a/git.c b/git.c index 2dd588674f..ea599d79b7 100644 --- a/git.c +++ b/git.c @@ -700,6 +700,31 @@ static int run_argv(int *argcp, const char ***argv) */ if (!done_alias) handle_builtin(*argcp, *argv); + else if (get_builtin(**argv)) { + struct argv_array args = ARGV_ARRAY_INIT; + int i; + + if (get_super_prefix()) + die("%s doesn't support --super-prefix", **argv); + + commit_pager_choice(); + + argv_array_push(&args, "git"); + for (i = 0; i < *argcp; i++) + argv_array_push(&args, (*argv)[i]); + + trace_argv_printf(args.argv, "trace: exec:"); + + /* + * if we fail because the command is not found, it is + * OK to return. Otherwise, we just pass along the status code. + */ + i = run_command_v_opt(args.argv, RUN_SILENT_EXEC_FAILURE | + RUN_CLEAN_ON_EXIT); + if (i >= 0 || errno != ENOENT) + exit(i); + die("could not execute builtin %s", **argv); + } /* .. then try the external ones */ execv_dashed_external(*argv); diff --git a/path.c b/path.c index 03ab712839..2585842ed5 100644 --- a/path.c +++ b/path.c @@ -11,6 +11,7 @@ #include "path.h" #include "packfile.h" #include "object-store.h" +#include "exec-cmd.h" static int get_st_mode_bits(const char *path, int *mode) { @@ -711,6 +712,10 @@ char *expand_user_path(const char *path, int real_home) if (path == NULL) goto return_null; +#ifdef __MINGW32__ + if (path[0] == '/') + return system_path(path + 1); +#endif if (path[0] == '~') { const char *first_slash = strchrnul(path, '/'); const char *username = path + 1; diff --git a/send-pack.c b/send-pack.c index 6dc16c3211..fa9e8cf1fc 100644 --- a/send-pack.c +++ b/send-pack.c @@ -38,6 +38,16 @@ int option_parse_push_signed(const struct option *opt, die("bad %s argument: %s", opt->long_name, arg); } +static int config_use_sideband = 1; + +static int send_pack_config(const char *var, const char *value, void *unused) +{ + if (!strcmp("sendpack.sideband", var)) + config_use_sideband = git_config_bool(var, value); + + return 0; +} + static void feed_object(const struct object_id *oid, FILE *fh, int negative) { if (negative && !has_object_file(oid)) @@ -390,6 +400,8 @@ int send_pack(struct send_pack_args *args, const char *push_cert_nonce = NULL; struct packet_reader reader; + git_config(send_pack_config, NULL); + /* Does the other end support the reporting? */ if (server_supports("report-status")) status_report = 1; @@ -397,7 +409,7 @@ int send_pack(struct send_pack_args *args, allow_deleting_refs = 1; if (server_supports("ofs-delta")) args->use_ofs_delta = 1; - if (server_supports("side-band-64k")) + if (config_use_sideband && server_supports("side-band-64k")) use_sideband = 1; if (server_supports("quiet")) quiet_supported = 1; diff --git a/setup.c b/setup.c index ca9e8a949e..c8c6bf3f49 100644 --- a/setup.c +++ b/setup.c @@ -784,7 +784,7 @@ static const char *setup_discovered_git_dir(const char *gitdir, set_git_dir(gitdir); inside_git_dir = 0; inside_work_tree = 1; - if (offset == cwd->len) + if (offset >= cwd->len) return NULL; /* Make "offset" point past the '/' (already the case for root dirs) */ @@ -906,7 +906,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT); struct string_list ceiling_dirs = STRING_LIST_INIT_DUP; const char *gitdirenv; - int ceil_offset = -1, min_offset = has_dos_drive_prefix(dir->buf) ? 3 : 1; + int ceil_offset = -1, min_offset = offset_1st_component(dir->buf); dev_t current_device = 0; int one_filesystem = 1; @@ -934,6 +934,12 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, if (ceil_offset < 0) ceil_offset = min_offset - 2; + if (min_offset && min_offset == dir->len && + !is_dir_sep(dir->buf[min_offset - 1])) { + strbuf_addch(dir, '/'); + min_offset++; + } + /* * Test in the following order (relative to the dir): * - .git (file containing "gitdir: ") diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 42a263cada..4d04e6a863 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -307,10 +307,20 @@ test_expect_success 'init prefers command line to GIT_DIR' ' test_path_is_missing otherdir/refs ' +downcase_on_case_insensitive_fs () { + test false = "$(git config --get core.filemode)" || return 0 + for f + do + tr A-Z a-z <"$f" >"$f".downcased && + mv -f "$f".downcased "$f" || return 1 + done +} + test_expect_success 'init with separate gitdir' ' rm -rf newdir && git init --separate-git-dir realgitdir newdir && echo "gitdir: $(pwd)/realgitdir" >expected && + downcase_on_case_insensitive_fs expected newdir/.git && test_cmp expected newdir/.git && test_path_is_dir realgitdir/refs ' @@ -365,6 +375,7 @@ test_expect_success 're-init to update git link' ' git init --separate-git-dir ../surrealgitdir ) && echo "gitdir: $(pwd)/surrealgitdir" >expected && + downcase_on_case_insensitive_fs expected newdir/.git && test_cmp expected newdir/.git && test_path_is_dir surrealgitdir/refs && test_path_is_missing realgitdir/refs @@ -378,6 +389,7 @@ test_expect_success 're-init to move gitdir' ' git init --separate-git-dir ../realgitdir ) && echo "gitdir: $(pwd)/realgitdir" >expected && + downcase_on_case_insensitive_fs expected newdir/.git && test_cmp expected newdir/.git && test_path_is_dir realgitdir/refs ' @@ -453,6 +465,18 @@ test_expect_success 're-init from a linked worktree' ' ) ' +test_expect_success MINGW 'core.hidedotfiles = false' ' + git config --global core.hidedotfiles false && + rm -rf newdir && + ( + sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG && + mkdir newdir && + cd newdir && + git init + ) && + ! is_hidden newdir/.git +' + test_expect_success MINGW 'redirect std handles' ' GIT_REDIRECT_STDOUT=output.txt git rev-parse --git-dir && test .git = "$(cat output.txt)" && @@ -462,7 +486,8 @@ test_expect_success MINGW 'redirect std handles' ' GIT_REDIRECT_STDERR="2>&1" \ git rev-parse --git-dir --verify refs/invalid && printf ".git\nfatal: Needed a single revision\n" >expect && - test_cmp expect output.txt + sort output.sorted && + test_cmp expect output.sorted ' test_done diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 49c540b1e1..029a99acc5 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -706,13 +706,22 @@ do # file with scheme for p in file do - test_expect_success "fetch-pack --diag-url $p://$h/$r" ' + test_expect_success !MINGW "fetch-pack --diag-url $p://$h/$r" ' check_prot_path $p://$h/$r $p "/$r" ' + test_expect_success MINGW "fetch-pack --diag-url $p://$h/$r" ' + check_prot_path $p://$h/$r $p "//$h/$r" + ' + test_expect_success MINGW "fetch-pack --diag-url $p:///$r" ' + check_prot_path $p:///$r $p "/$r" + ' # No "/~" -> "~" conversion for file - test_expect_success "fetch-pack --diag-url $p://$h/~$r" ' + test_expect_success !MINGW "fetch-pack --diag-url $p://$h/~$r" ' check_prot_path $p://$h/~$r $p "/~$r" ' + test_expect_success MINGW "fetch-pack --diag-url $p://$h/~$r" ' + check_prot_path $p://$h/~$r $p "//$h/~$r" + ' done # file without scheme for h in nohost nohost:12 [::1] [::1]:23 [ [:aa diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 883b32efa0..1132964044 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -824,6 +824,7 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' ' ( cd six && git remote rm origin && + mkdir -p .git/branches && echo "$origin_url" >.git/branches/origin && git remote rename origin origin && test_path_is_missing .git/branches/origin && @@ -838,6 +839,7 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)' ( cd seven && git remote rm origin && + mkdir -p .git/branches && echo "quux#foom" > .git/branches/origin && git remote rename origin origin && test_path_is_missing .git/branches/origin && diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 37e8e80893..d6ad36f7af 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -866,6 +866,7 @@ test_expect_success 'fetch with branches' ' mk_empty testrepo && git branch second $the_first_commit && git checkout second && + mkdir -p testrepo/.git/branches && echo ".." > testrepo/.git/branches/branch1 && ( cd testrepo && @@ -879,6 +880,7 @@ test_expect_success 'fetch with branches' ' test_expect_success 'fetch with branches containing #' ' mk_empty testrepo && + mkdir -p testrepo/.git/branches && echo "..#second" > testrepo/.git/branches/branch2 && ( cd testrepo && @@ -893,6 +895,7 @@ test_expect_success 'fetch with branches containing #' ' test_expect_success 'push with branches' ' mk_empty testrepo && git checkout second && + mkdir -p .git/branches && echo "testrepo" > .git/branches/branch1 && git push branch1 && ( @@ -905,6 +908,7 @@ test_expect_success 'push with branches' ' test_expect_success 'push with branches containing #' ' mk_empty testrepo && + mkdir -p .git/branches && echo "testrepo#branch3" > .git/branches/branch2 && git push branch2 && ( diff --git a/t/t5580-clone-push-unc.sh b/t/t5580-unc-paths.sh similarity index 59% rename from t/t5580-clone-push-unc.sh rename to t/t5580-unc-paths.sh index 217adf3a63..f9f0ef6b2d 100755 --- a/t/t5580-clone-push-unc.sh +++ b/t/t5580-unc-paths.sh @@ -17,14 +17,11 @@ fi UNCPATH="$(winpwd)" case "$UNCPATH" in [A-Z]:*) + WITHOUTDRIVE="${UNCPATH#?:}" # Use administrative share e.g. \\localhost\C$\git-sdk-64\usr\src\git # (we use forward slashes here because MSYS2 and Git accept them, and # they are easier on the eyes) - UNCPATH="//localhost/${UNCPATH%%:*}\$/${UNCPATH#?:}" - test -d "$UNCPATH" || { - skip_all='could not access administrative share; skipping' - test_done - } + UNCPATH="//localhost/${UNCPATH%%:*}\$$WITHOUTDRIVE" ;; *) skip_all='skipping UNC path tests, cannot determine current path as UNC' @@ -32,6 +29,18 @@ case "$UNCPATH" in ;; esac +test_expect_success 'clone into absolute path lacking a drive prefix' ' + USINGBACKSLASHES="$(echo "$WITHOUTDRIVE"/without-drive-prefix | + tr / \\\\)" && + git clone . "$USINGBACKSLASHES" && + test -f without-drive-prefix/.git/HEAD +' + +test -d "$UNCPATH" || { + skip_all='could not access administrative share; skipping' + test_done +} + test_expect_success setup ' test_commit initial ' @@ -40,11 +49,23 @@ test_expect_success clone ' git clone "file://$UNCPATH" clone ' +test_expect_success 'clone without file://' ' + git clone "$UNCPATH" clone-without-file +' + test_expect_success 'clone with backslashed path' ' BACKSLASHED="$(echo "$UNCPATH" | tr / \\\\)" && git clone "$BACKSLASHED" backslashed ' +test_expect_success fetch ' + git init to-fetch && + ( + cd to-fetch && + git fetch "$UNCPATH" master + ) +' + test_expect_success push ' ( cd clone && @@ -62,4 +83,16 @@ test_expect_success MINGW 'remote nick cannot contain backslashes' ' test_i18ngrep ! "unable to access" err ' +test_expect_success 'unc alternates' ' + tree="$(git rev-parse HEAD:)" && + mkdir test-unc-alternate && + ( + cd test-unc-alternate && + git init && + test_must_fail git show $tree && + echo "$UNCPATH/.git/objects" >.git/objects/info/alternates && + git show $tree + ) +' + test_done diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh index aaaa722cca..18cf138343 100755 --- a/t/t5801-remote-helpers.sh +++ b/t/t5801-remote-helpers.sh @@ -228,7 +228,7 @@ test_expect_success 'push update refs failure' ' echo "update fail" >>file && git commit -a -m "update fail" && git rev-parse --verify testgit/origin/heads/update >expect && - test_expect_code 1 env GIT_REMOTE_TESTGIT_FAILURE="non-fast forward" \ + test_must_fail env GIT_REMOTE_TESTGIT_FAILURE="non-fast forward" \ git push origin update && git rev-parse --verify testgit/origin/heads/update >actual && test_cmp expect actual diff --git a/t/t7108-reset-stdin.sh b/t/t7108-reset-stdin.sh new file mode 100755 index 0000000000..b7cbcbf869 --- /dev/null +++ b/t/t7108-reset-stdin.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +test_description='reset --stdin' + +. ./test-lib.sh + +test_expect_success 'reset --stdin' ' + test_commit hello && + git rm hello.t && + test -z "$(git ls-files hello.t)" && + echo hello.t | git reset --stdin && + test hello.t = "$(git ls-files hello.t)" +' + +test_expect_success 'reset --stdin -z' ' + test_commit world && + git rm hello.t world.t && + test -z "$(git ls-files hello.t world.t)" && + printf world.tQworld.tQhello.tQ | q_to_nul | git reset --stdin -z && + printf "hello.t\nworld.t\n" >expect && + git ls-files >actual && + test_cmp expect actual +' + +test_expect_success '--stdin requires --mixed' ' + echo hello.t >list && + test_must_fail git reset --soft --stdin expected << EOF +reset refs/heads/master +from $(git rev-parse master) + +EOF + +test_expect_failure 'refs are updated even if no commits need to be exported' ' + git fast-export master..master > actual && + test_cmp expected actual +' + test_done diff --git a/transport-helper.c b/transport-helper.c index 1f52c95fd8..848ae4d760 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -16,6 +16,8 @@ #include "protocol.h" static int debug; +/* TODO: put somewhere sensible, e.g. git_transport_options? */ +static int auto_gc = 1; struct helper_data { const char *name; @@ -460,10 +462,25 @@ static int get_exporter(struct transport *transport, for (i = 0; i < revlist_args->nr; i++) argv_array_push(&fastexport->args, revlist_args->items[i].string); + argv_array_push(&fastexport->args, "--"); + fastexport->git_cmd = 1; return start_command(fastexport); } +static void check_helper_status(struct helper_data *data) +{ + int pid, status; + + pid = waitpid(data->helper->pid, &status, WNOHANG); + if (pid < 0) + die("Could not retrieve status of remote helper '%s'", + data->name); + if (pid > 0 && WIFEXITED(status)) + die("Remote helper '%s' died with %d", + data->name, WEXITSTATUS(status)); +} + static int fetch_with_import(struct transport *transport, int nr_heads, struct ref **to_fetch) { @@ -500,6 +517,7 @@ static int fetch_with_import(struct transport *transport, if (finish_command(&fastimport)) die(_("error while running fast-import")); + check_helper_status(data); /* * The fast-import stream of a remote helper that advertises @@ -533,6 +551,12 @@ static int fetch_with_import(struct transport *transport, } } strbuf_release(&buf); + if (auto_gc) { + const char *argv_gc_auto[] = { + "gc", "--auto", "--quiet", NULL, + }; + run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); + } return 0; } @@ -992,6 +1016,7 @@ static int push_refs_with_export(struct transport *transport, if (finish_command(&exporter)) die(_("error while running fast-export")); + check_helper_status(data); if (push_update_refs_status(data, remote_refs, flags)) return 1;