mirror of
https://github.com/git/git.git
synced 2026-02-07 08:15:23 +00:00
Merge pull request #231 from kblees/kb/msysgit-2.1.0
Rebasing merge to Git v2.1.0
This commit is contained in:
@@ -212,6 +212,12 @@ The default is true, except linkgit:git-clone[1] or linkgit:git-init[1]
|
||||
will probe and set core.fileMode false if appropriate when the
|
||||
repository is created.
|
||||
|
||||
core.hideDotFiles::
|
||||
(Windows-only) If true (which is the default), mark newly-created
|
||||
directories and files whose name starts with a dot as hidden.
|
||||
If 'dotGitOnly', only the .git/ directory is hidden, but no other
|
||||
files starting with a dot.
|
||||
|
||||
core.ignorecase::
|
||||
If true, this option enables various workarounds to enable
|
||||
Git to work better on filesystems that are not case sensitive,
|
||||
@@ -625,6 +631,19 @@ relatively high IO latencies. When enabled, Git will do the
|
||||
index comparison to the filesystem data in parallel, allowing
|
||||
overlapping IO's. Defaults to true.
|
||||
|
||||
core.fscache::
|
||||
Enable additional caching of file system data for some operations.
|
||||
+
|
||||
Git for Windows uses this to bulk-read and cache lstat data of entire
|
||||
directories (instead of doing lstat file by file).
|
||||
|
||||
core.longpaths::
|
||||
Enable long path (> 260) support for builtin commands in Git for
|
||||
Windows. This is disabled by default, as long paths are not supported
|
||||
by Windows Explorer, cmd.exe and the Git for Windows tool chain
|
||||
(msys, bash, tcl, perl...). Only enable this if you know what you're
|
||||
doing and are prepared to live with a few quirks.
|
||||
|
||||
core.createObject::
|
||||
You can set this to 'link', in which case a hardlink followed by
|
||||
a delete of the source are used to make sure that object creation
|
||||
@@ -776,6 +795,7 @@ branch.<name>.rebase::
|
||||
instead of merging the default branch from the default remote when
|
||||
"git pull" is run. See "pull.rebase" for doing this in a non
|
||||
branch-specific manner.
|
||||
When the value is `interactive`, the rebase is run in interactive mode.
|
||||
+
|
||||
When preserve, also pass `--preserve-merges` along to 'git rebase'
|
||||
so that locally committed merge commits will not be flattened
|
||||
@@ -2076,6 +2096,11 @@ receive.denyCurrentBranch::
|
||||
print a warning of such a push to stderr, but allow the push to
|
||||
proceed. If set to false or "ignore", allow such pushes with no
|
||||
message. Defaults to "refuse".
|
||||
+
|
||||
There are two more options that are meant for Git experts: "updateInstead"
|
||||
which will run `read-tree -u -m HEAD` and "detachInstead" which will detach
|
||||
the HEAD so it does not need to change. Both options come with their own
|
||||
set of possible *complications*, but can be appropriate in rare workflows.
|
||||
|
||||
receive.denyNonFastForwards::
|
||||
If set to true, git-receive-pack will deny a ref update which is
|
||||
@@ -2459,3 +2484,9 @@ web.browser::
|
||||
Specify a web browser that may be used by some commands.
|
||||
Currently only linkgit:git-instaweb[1] and linkgit:git-help[1]
|
||||
may use it.
|
||||
|
||||
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.
|
||||
|
||||
@@ -104,7 +104,7 @@ Options related to merging
|
||||
include::merge-options.txt[]
|
||||
|
||||
-r::
|
||||
--rebase[=false|true|preserve]::
|
||||
--rebase[=false|true|preserve|interactive]::
|
||||
When true, rebase the current branch on top of the upstream
|
||||
branch after fetching. If there is a remote-tracking branch
|
||||
corresponding to the upstream branch and the upstream branch
|
||||
@@ -117,6 +117,8 @@ locally created merge commits will not be flattened.
|
||||
+
|
||||
When false, merge the current branch into the upstream branch.
|
||||
+
|
||||
When `interactive`, enable the interactive mode of rebase.
|
||||
+
|
||||
See `pull.rebase`, `branch.<name>.rebase` and `branch.autosetuprebase` in
|
||||
linkgit:git-config[1] if you want to make `git pull` always use
|
||||
`--rebase` instead of merging.
|
||||
|
||||
11
Makefile
11
Makefile
@@ -1148,13 +1148,11 @@ else
|
||||
REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
|
||||
PROGRAM_OBJS += http-fetch.o
|
||||
PROGRAMS += $(REMOTE_CURL_NAMES)
|
||||
curl_check := $(shell (echo 070908; curl-config --vernum) 2>/dev/null | sort -r | sed -ne 2p)
|
||||
ifeq "$(curl_check)" "070908"
|
||||
ifndef NO_EXPAT
|
||||
ifndef NO_EXPAT
|
||||
ifndef NO_CURL_MULTI
|
||||
PROGRAM_OBJS += http-push.o
|
||||
endif
|
||||
endif
|
||||
ifndef NO_EXPAT
|
||||
|
||||
ifdef EXPATDIR
|
||||
BASIC_CFLAGS += -I$(EXPATDIR)/include
|
||||
EXPAT_LIBEXPAT = -L$(EXPATDIR)/$(lib) $(CC_LD_DYNPATH)$(EXPATDIR)/$(lib) -lexpat
|
||||
@@ -1479,6 +1477,9 @@ ifdef NO_REGEX
|
||||
COMPAT_CFLAGS += -Icompat/regex
|
||||
COMPAT_OBJS += compat/regex/regex.o
|
||||
endif
|
||||
ifdef NATIVE_CRLF
|
||||
BASIC_CFLAGS += -DNATIVE_CRLF
|
||||
endif
|
||||
|
||||
ifdef USE_NED_ALLOCATOR
|
||||
COMPAT_CFLAGS += -Icompat/nedmalloc
|
||||
|
||||
6
README.md
Normal file
6
README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Git for Windows
|
||||
|
||||
This is the source code of [Git for Windows](http://msysgit.github.io/),
|
||||
forked from [Git](http://git-scm.com/).
|
||||
|
||||
If you encounter problems, you can report them as [GitHub issues](https://github.com/msysgit/git/issues?direction=desc&sort=updated&state=open), discuss them on Git for Windows' [Google Group](http://groups.google.com/group/msysgit), and encourage others to work on by tipping via [](http://tip4commit.com/projects/295).
|
||||
@@ -947,7 +947,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 =
|
||||
|
||||
@@ -1368,6 +1368,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
|
||||
PATHSPEC_PREFER_FULL,
|
||||
prefix, argv);
|
||||
|
||||
enable_fscache(1);
|
||||
read_cache_preload(&s.pathspec);
|
||||
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
|
||||
|
||||
|
||||
@@ -571,9 +571,20 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_reset(const char *name, struct object *object)
|
||||
{
|
||||
int mark = get_object_mark(object);
|
||||
|
||||
if (mark)
|
||||
printf("reset %s\nfrom :%d\n\n", name,
|
||||
get_object_mark(object));
|
||||
else
|
||||
printf("reset %s\nfrom %s\n\n", name,
|
||||
sha1_to_hex(object->sha1));
|
||||
}
|
||||
|
||||
static void handle_tags_and_duplicates(void)
|
||||
{
|
||||
struct commit *commit;
|
||||
int i;
|
||||
|
||||
for (i = extra_refs.nr - 1; i >= 0; i--) {
|
||||
@@ -585,9 +596,7 @@ static void handle_tags_and_duplicates(void)
|
||||
break;
|
||||
case OBJ_COMMIT:
|
||||
/* create refs pointing to already seen commits */
|
||||
commit = (struct commit *)object;
|
||||
printf("reset %s\nfrom :%d\n\n", name,
|
||||
get_object_mark(&commit->object));
|
||||
handle_reset(name, object);
|
||||
show_progress();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -385,6 +385,7 @@ int init_db(const char *template_dir, unsigned int flags)
|
||||
check_repository_format();
|
||||
|
||||
reinit = create_default_files(template_dir);
|
||||
mark_as_git_dir(get_git_dir());
|
||||
|
||||
create_object_directory();
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ enum deny_action {
|
||||
DENY_UNCONFIGURED,
|
||||
DENY_IGNORE,
|
||||
DENY_WARN,
|
||||
DENY_REFUSE
|
||||
DENY_REFUSE,
|
||||
DENY_UPDATE_INSTEAD,
|
||||
DENY_DETACH_INSTEAD,
|
||||
};
|
||||
|
||||
static int deny_deletes;
|
||||
@@ -100,7 +102,12 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
|
||||
}
|
||||
|
||||
if (!strcmp(var, "receive.denycurrentbranch")) {
|
||||
deny_current_branch = parse_deny_action(var, value);
|
||||
if (value && !strcasecmp(value, "updateinstead"))
|
||||
deny_current_branch = DENY_UPDATE_INSTEAD;
|
||||
else if (value && !strcasecmp(value, "detachinstead"))
|
||||
deny_current_branch = DENY_DETACH_INSTEAD;
|
||||
else
|
||||
deny_current_branch = parse_deny_action(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -468,6 +475,44 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void merge_worktree(unsigned char *sha1)
|
||||
{
|
||||
const char *update_refresh[] = {
|
||||
"update-index", "--ignore-submodules", "--refresh", NULL
|
||||
};
|
||||
const char *read_tree[] = {
|
||||
"read-tree", "-u", "-m", sha1_to_hex(sha1), NULL
|
||||
};
|
||||
struct child_process child;
|
||||
struct strbuf git_env = STRBUF_INIT;
|
||||
const char *env[2];
|
||||
|
||||
if (is_bare_repository())
|
||||
die ("denyCurrentBranch = updateInstead needs a worktree");
|
||||
|
||||
strbuf_addf(&git_env, "GIT_DIR=%s", absolute_path(get_git_dir()));
|
||||
env[0] = git_env.buf;
|
||||
env[1] = NULL;
|
||||
|
||||
memset(&child, 0, sizeof(child));
|
||||
child.argv = update_refresh;
|
||||
child.env = env;
|
||||
child.dir = git_work_tree_cfg ? git_work_tree_cfg : "..";
|
||||
child.stdout_to_stderr = 1;
|
||||
child.git_cmd = 1;
|
||||
if (run_command(&child))
|
||||
die ("Could not refresh the index");
|
||||
|
||||
child.argv = read_tree;
|
||||
child.no_stdin = 1;
|
||||
child.no_stdout = 1;
|
||||
child.stdout_to_stderr = 0;
|
||||
if (run_command(&child))
|
||||
die ("Could not merge working tree with new HEAD. Good luck.");
|
||||
|
||||
strbuf_release(&git_env);
|
||||
}
|
||||
|
||||
static const char *update(struct command *cmd, struct shallow_info *si)
|
||||
{
|
||||
const char *name = cmd->ref_name;
|
||||
@@ -499,6 +544,13 @@ static const char *update(struct command *cmd, struct shallow_info *si)
|
||||
if (deny_current_branch == DENY_UNCONFIGURED)
|
||||
refuse_unconfigured_deny();
|
||||
return "branch is currently checked out";
|
||||
case DENY_UPDATE_INSTEAD:
|
||||
merge_worktree(new_sha1);
|
||||
break;
|
||||
case DENY_DETACH_INSTEAD:
|
||||
update_ref("push into current branch (detach)", "HEAD",
|
||||
old_sha1, NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,6 +579,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
|
||||
refuse_unconfigured_deny_delete_current();
|
||||
rp_error("refusing to delete the current branch: %s", name);
|
||||
return "deletion of the current branch prohibited";
|
||||
default:
|
||||
die ("Invalid denyDeleteCurrent setting");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ static int add(int argc, const char **argv)
|
||||
struct branch_info {
|
||||
char *remote_name;
|
||||
struct string_list merge;
|
||||
int rebase;
|
||||
enum { NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE } rebase;
|
||||
};
|
||||
|
||||
static struct string_list branch_list;
|
||||
@@ -304,6 +304,8 @@ static int config_read_branches(const char *key, const char *value, void *cb)
|
||||
info->rebase = v;
|
||||
else if (!strcmp(value, "preserve"))
|
||||
info->rebase = 1;
|
||||
else if (!strcmp(value, "interactive"))
|
||||
info->rebase = INTERACTIVE_REBASE;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -999,7 +1001,9 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data)
|
||||
|
||||
printf(" %-*s ", show_info->width, item->string);
|
||||
if (branch_info->rebase) {
|
||||
printf_ln(_("rebases onto remote %s"), merge->items[0].string);
|
||||
printf_ln(_(branch_info->rebase == INTERACTIVE_REBASE ?
|
||||
"rebases interactively onto remote %s" :
|
||||
"rebases onto remote %s"), merge->items[0].string);
|
||||
return 0;
|
||||
} else if (show_info->any_rebase) {
|
||||
printf_ln(_(" merges with remote %s"), merge->items[0].string);
|
||||
|
||||
11
cache.h
11
cache.h
@@ -641,6 +641,17 @@ extern int precomposed_unicode;
|
||||
extern char comment_line_char;
|
||||
extern int auto_comment_line_char;
|
||||
|
||||
enum hide_dotfiles_type {
|
||||
HIDE_DOTFILES_FALSE = 0,
|
||||
HIDE_DOTFILES_TRUE,
|
||||
HIDE_DOTFILES_DOTGITONLY,
|
||||
};
|
||||
extern enum hide_dotfiles_type hide_dotfiles;
|
||||
|
||||
extern int core_fscache;
|
||||
|
||||
extern int core_long_paths;
|
||||
|
||||
enum branch_track {
|
||||
BRANCH_TRACK_UNSPECIFIED = -1,
|
||||
BRANCH_TRACK_NEVER = 0,
|
||||
|
||||
226
compat/mingw.c
226
compat/mingw.c
@@ -7,6 +7,7 @@
|
||||
#include "../cache.h"
|
||||
|
||||
static const int delay[] = { 0, 1, 10, 20, 40 };
|
||||
unsigned int _CRT_fmode = _O_BINARY;
|
||||
|
||||
int err_win_to_posix(DWORD winerr)
|
||||
{
|
||||
@@ -203,8 +204,8 @@ static int ask_yes_no_if_possible(const char *format, ...)
|
||||
int mingw_unlink(const char *pathname)
|
||||
{
|
||||
int ret, tries = 0;
|
||||
wchar_t wpathname[MAX_PATH];
|
||||
if (xutftowcs_path(wpathname, pathname) < 0)
|
||||
wchar_t wpathname[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wpathname, pathname) < 0)
|
||||
return -1;
|
||||
|
||||
/* read-only files cannot be removed */
|
||||
@@ -233,7 +234,7 @@ static int is_dir_empty(const wchar_t *wpath)
|
||||
{
|
||||
WIN32_FIND_DATAW findbuf;
|
||||
HANDLE handle;
|
||||
wchar_t wbuf[MAX_PATH + 2];
|
||||
wchar_t wbuf[MAX_LONG_PATH + 2];
|
||||
wcscpy(wbuf, wpath);
|
||||
wcscat(wbuf, L"\\*");
|
||||
handle = FindFirstFileW(wbuf, &findbuf);
|
||||
@@ -254,8 +255,8 @@ static int is_dir_empty(const wchar_t *wpath)
|
||||
int mingw_rmdir(const char *pathname)
|
||||
{
|
||||
int ret, tries = 0;
|
||||
wchar_t wpathname[MAX_PATH];
|
||||
if (xutftowcs_path(wpathname, pathname) < 0)
|
||||
wchar_t wpathname[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wpathname, pathname) < 0)
|
||||
return -1;
|
||||
|
||||
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
|
||||
@@ -284,13 +285,47 @@ int mingw_rmdir(const char *pathname)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int make_hidden(const wchar_t *path)
|
||||
{
|
||||
DWORD attribs = GetFileAttributesW(path);
|
||||
if (SetFileAttributesW(path, FILE_ATTRIBUTE_HIDDEN | attribs))
|
||||
return 0;
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
void mingw_mark_as_git_dir(const char *dir)
|
||||
{
|
||||
wchar_t wdir[MAX_LONG_PATH];
|
||||
if (hide_dotfiles != HIDE_DOTFILES_FALSE && !is_bare_repository())
|
||||
if (xutftowcs_long_path(wdir, dir) < 0 || make_hidden(wdir))
|
||||
warning("Failed to make '%s' hidden", dir);
|
||||
git_config_set("core.hideDotFiles",
|
||||
hide_dotfiles == HIDE_DOTFILES_FALSE ? "false" :
|
||||
(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY ?
|
||||
"dotGitOnly" : "true"));
|
||||
}
|
||||
|
||||
int mingw_mkdir(const char *path, int mode)
|
||||
{
|
||||
int ret;
|
||||
wchar_t wpath[MAX_PATH];
|
||||
if (xutftowcs_path(wpath, path) < 0)
|
||||
wchar_t wpath[MAX_LONG_PATH];
|
||||
/* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
|
||||
if (xutftowcs_path_ex(wpath, path, MAX_LONG_PATH, -1, 248,
|
||||
core_long_paths) < 0)
|
||||
return -1;
|
||||
|
||||
ret = _wmkdir(wpath);
|
||||
if (!ret && hide_dotfiles == HIDE_DOTFILES_TRUE) {
|
||||
/*
|
||||
* In Windows a file or dir starting with a dot is not
|
||||
* automatically hidden. So lets mark it as hidden when
|
||||
* such a directory is created.
|
||||
*/
|
||||
const char *start = basename((char*)path);
|
||||
if (*start == '.')
|
||||
return make_hidden(wpath);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -299,7 +334,7 @@ int mingw_open (const char *filename, int oflags, ...)
|
||||
va_list args;
|
||||
unsigned mode;
|
||||
int fd;
|
||||
wchar_t wfilename[MAX_PATH];
|
||||
wchar_t wfilename[MAX_LONG_PATH];
|
||||
|
||||
va_start(args, oflags);
|
||||
mode = va_arg(args, int);
|
||||
@@ -308,7 +343,7 @@ int mingw_open (const char *filename, int oflags, ...)
|
||||
if (filename && !strcmp(filename, "/dev/null"))
|
||||
filename = "nul";
|
||||
|
||||
if (xutftowcs_path(wfilename, filename) < 0)
|
||||
if (xutftowcs_long_path(wfilename, filename) < 0)
|
||||
return -1;
|
||||
fd = _wopen(wfilename, oflags, mode);
|
||||
|
||||
@@ -317,6 +352,17 @@ int mingw_open (const char *filename, int oflags, ...)
|
||||
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
|
||||
errno = EISDIR;
|
||||
}
|
||||
if ((oflags & O_CREAT) && fd >= 0 &&
|
||||
hide_dotfiles == HIDE_DOTFILES_TRUE) {
|
||||
/*
|
||||
* In Windows a file or dir starting with a dot is not
|
||||
* automatically hidden. So lets mark it as hidden when
|
||||
* such a file is created.
|
||||
*/
|
||||
const char *start = basename((char*)filename);
|
||||
if (*start == '.' && make_hidden(wfilename))
|
||||
warning("Could not mark '%s' as hidden.", filename);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
@@ -348,27 +394,39 @@ int mingw_fgetc(FILE *stream)
|
||||
#undef fopen
|
||||
FILE *mingw_fopen (const char *filename, const char *otype)
|
||||
{
|
||||
int hide = 0;
|
||||
FILE *file;
|
||||
wchar_t wfilename[MAX_PATH], wotype[4];
|
||||
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
|
||||
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
|
||||
basename((char*)filename)[0] == '.')
|
||||
hide = access(filename, F_OK);
|
||||
if (filename && !strcmp(filename, "/dev/null"))
|
||||
filename = "nul";
|
||||
if (xutftowcs_path(wfilename, filename) < 0 ||
|
||||
if (xutftowcs_long_path(wfilename, filename) < 0 ||
|
||||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
|
||||
return NULL;
|
||||
file = _wfopen(wfilename, wotype);
|
||||
if (file && hide && make_hidden(wfilename))
|
||||
warning("Could not mark '%s' as hidden.", filename);
|
||||
return file;
|
||||
}
|
||||
|
||||
FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
|
||||
{
|
||||
int hide = 0;
|
||||
FILE *file;
|
||||
wchar_t wfilename[MAX_PATH], wotype[4];
|
||||
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
|
||||
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
|
||||
basename((char*)filename)[0] == '.')
|
||||
hide = access(filename, F_OK);
|
||||
if (filename && !strcmp(filename, "/dev/null"))
|
||||
filename = "nul";
|
||||
if (xutftowcs_path(wfilename, filename) < 0 ||
|
||||
if (xutftowcs_long_path(wfilename, filename) < 0 ||
|
||||
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
|
||||
return NULL;
|
||||
file = _wfreopen(wfilename, wotype, stream);
|
||||
if (file && hide && make_hidden(wfilename))
|
||||
warning("Could not mark '%s' as hidden.", filename);
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -396,45 +454,36 @@ int mingw_fflush(FILE *stream)
|
||||
|
||||
int mingw_access(const char *filename, int mode)
|
||||
{
|
||||
wchar_t wfilename[MAX_PATH];
|
||||
if (xutftowcs_path(wfilename, filename) < 0)
|
||||
wchar_t wfilename[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wfilename, filename) < 0)
|
||||
return -1;
|
||||
/* X_OK is not supported by the MSVCRT version */
|
||||
return _waccess(wfilename, mode & ~X_OK);
|
||||
}
|
||||
|
||||
/* cached length of current directory for handle_long_path */
|
||||
static int current_directory_len = 0;
|
||||
|
||||
int mingw_chdir(const char *dirname)
|
||||
{
|
||||
int result;
|
||||
wchar_t wdirname[MAX_PATH];
|
||||
/* SetCurrentDirectoryW doesn't support long paths */
|
||||
if (xutftowcs_path(wdirname, dirname) < 0)
|
||||
return -1;
|
||||
return _wchdir(wdirname);
|
||||
result = _wchdir(wdirname);
|
||||
current_directory_len = GetCurrentDirectoryW(0, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
int mingw_chmod(const char *filename, int mode)
|
||||
{
|
||||
wchar_t wfilename[MAX_PATH];
|
||||
if (xutftowcs_path(wfilename, filename) < 0)
|
||||
wchar_t wfilename[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wfilename, filename) < 0)
|
||||
return -1;
|
||||
return _wchmod(wfilename, mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
|
||||
* Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
|
||||
*/
|
||||
static inline long long filetime_to_hnsec(const FILETIME *ft)
|
||||
{
|
||||
long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
|
||||
/* Windows to Unix Epoch conversion */
|
||||
return winTime - 116444736000000000LL;
|
||||
}
|
||||
|
||||
static inline time_t filetime_to_time_t(const FILETIME *ft)
|
||||
{
|
||||
return (time_t)(filetime_to_hnsec(ft) / 10000000);
|
||||
}
|
||||
|
||||
/* We keep the do_lstat code in a separate function to avoid recursion.
|
||||
* When a path ends with a slash, the stat will fail with ENOENT. In
|
||||
* this case, we strip the trailing slashes and stat again.
|
||||
@@ -445,8 +494,8 @@ static inline time_t filetime_to_time_t(const FILETIME *ft)
|
||||
static int do_lstat(int follow, const char *file_name, struct stat *buf)
|
||||
{
|
||||
WIN32_FILE_ATTRIBUTE_DATA fdata;
|
||||
wchar_t wfilename[MAX_PATH];
|
||||
if (xutftowcs_path(wfilename, file_name) < 0)
|
||||
wchar_t wfilename[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wfilename, file_name) < 0)
|
||||
return -1;
|
||||
|
||||
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
|
||||
@@ -511,7 +560,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
|
||||
static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
|
||||
{
|
||||
int namelen;
|
||||
char alt_name[PATH_MAX];
|
||||
char alt_name[MAX_LONG_PATH];
|
||||
|
||||
if (!do_lstat(follow, file_name, buf))
|
||||
return 0;
|
||||
@@ -527,7 +576,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
|
||||
return -1;
|
||||
while (namelen && file_name[namelen-1] == '/')
|
||||
--namelen;
|
||||
if (!namelen || namelen >= PATH_MAX)
|
||||
if (!namelen || namelen >= MAX_LONG_PATH)
|
||||
return -1;
|
||||
|
||||
memcpy(alt_name, file_name, namelen);
|
||||
@@ -535,6 +584,8 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
|
||||
return do_lstat(follow, alt_name, buf);
|
||||
}
|
||||
|
||||
int (*lstat)(const char *file_name, struct stat *buf) = mingw_lstat;
|
||||
|
||||
int mingw_lstat(const char *file_name, struct stat *buf)
|
||||
{
|
||||
return do_stat_internal(0, file_name, buf);
|
||||
@@ -587,8 +638,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
|
||||
FILETIME mft, aft;
|
||||
int fh, rc;
|
||||
DWORD attrs;
|
||||
wchar_t wfilename[MAX_PATH];
|
||||
if (xutftowcs_path(wfilename, file_name) < 0)
|
||||
wchar_t wfilename[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wfilename, file_name) < 0)
|
||||
return -1;
|
||||
|
||||
/* must have write permission */
|
||||
@@ -636,6 +687,7 @@ unsigned int sleep (unsigned int seconds)
|
||||
char *mingw_mktemp(char *template)
|
||||
{
|
||||
wchar_t wtemplate[MAX_PATH];
|
||||
/* we need to return the path, thus no long paths here! */
|
||||
if (xutftowcs_path(wtemplate, template) < 0)
|
||||
return NULL;
|
||||
if (!_wmktemp(wtemplate))
|
||||
@@ -869,14 +921,21 @@ static void free_path_split(char **path)
|
||||
static char *lookup_prog(const char *dir, const char *cmd, int isexe, int exe_only)
|
||||
{
|
||||
char path[MAX_PATH];
|
||||
snprintf(path, sizeof(path), "%s/%s.exe", dir, cmd);
|
||||
wchar_t wpath[MAX_PATH];
|
||||
snprintf(path, sizeof(path), "%s\\%s.exe", 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;
|
||||
}
|
||||
|
||||
@@ -989,6 +1048,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
|
||||
si.hStdOutput = winansi_get_osfhandle(fhout);
|
||||
si.hStdError = winansi_get_osfhandle(fherr);
|
||||
|
||||
/* executables and the current directory don't support long paths */
|
||||
if (xutftowcs_path(wcmd, cmd) < 0)
|
||||
return -1;
|
||||
if (dir && xutftowcs_path(wdir, dir) < 0)
|
||||
@@ -1564,8 +1624,9 @@ int mingw_rename(const char *pold, const char *pnew)
|
||||
{
|
||||
DWORD attrs, gle;
|
||||
int tries = 0;
|
||||
wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
|
||||
if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
|
||||
wchar_t wpold[MAX_LONG_PATH], wpnew[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wpold, pold) < 0 ||
|
||||
xutftowcs_long_path(wpnew, pnew) < 0)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
@@ -1841,9 +1902,9 @@ int link(const char *oldpath, const char *newpath)
|
||||
{
|
||||
typedef BOOL (WINAPI *T)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
|
||||
static T create_hard_link = NULL;
|
||||
wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
|
||||
if (xutftowcs_path(woldpath, oldpath) < 0 ||
|
||||
xutftowcs_path(wnewpath, newpath) < 0)
|
||||
wchar_t woldpath[MAX_LONG_PATH], wnewpath[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(woldpath, oldpath) < 0 ||
|
||||
xutftowcs_long_path(wnewpath, newpath) < 0)
|
||||
return -1;
|
||||
|
||||
if (!create_hard_link) {
|
||||
@@ -2024,6 +2085,68 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int handle_long_path(wchar_t *path, int len, int max_path, int expand)
|
||||
{
|
||||
int result;
|
||||
wchar_t buf[MAX_LONG_PATH];
|
||||
|
||||
/*
|
||||
* we don't need special handling if path is relative to the current
|
||||
* directory, and current directory + path don't exceed the desired
|
||||
* max_path limit. This should cover > 99 % of cases with minimal
|
||||
* performance impact (git almost always uses relative paths).
|
||||
*/
|
||||
if ((len < 2 || (!is_dir_sep(path[0]) && path[1] != ':')) &&
|
||||
(current_directory_len + len < max_path))
|
||||
return len;
|
||||
|
||||
/*
|
||||
* handle everything else:
|
||||
* - absolute paths: "C:\dir\file"
|
||||
* - absolute UNC paths: "\\server\share\dir\file"
|
||||
* - absolute paths on current drive: "\dir\file"
|
||||
* - relative paths on other drive: "X:file"
|
||||
* - prefixed paths: "\\?\...", "\\.\..."
|
||||
*/
|
||||
|
||||
/* convert to absolute path using GetFullPathNameW */
|
||||
result = GetFullPathNameW(path, MAX_LONG_PATH, buf, NULL);
|
||||
if (!result) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* return absolute path if it fits within max_path (even if
|
||||
* "cwd + path" doesn't due to '..' components)
|
||||
*/
|
||||
if (result < max_path) {
|
||||
wcscpy(path, buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* error out if we shouldn't expand the path or buf is too small */
|
||||
if (!expand || result >= MAX_LONG_PATH - 6) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* prefix full path with "\\?\" or "\\?\UNC\" */
|
||||
if (buf[0] == '\\') {
|
||||
/* ...unless already prefixed */
|
||||
if (buf[1] == '\\' && (buf[2] == '?' || buf[2] == '.'))
|
||||
return len;
|
||||
|
||||
wcscpy(path, L"\\\\?\\UNC\\");
|
||||
wcscpy(path + 8, buf + 2);
|
||||
return result + 6;
|
||||
} else {
|
||||
wcscpy(path, L"\\\\?\\");
|
||||
wcscpy(path + 4, buf);
|
||||
return result + 4;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
|
||||
* mingw startup code, see init.c in mingw runtime).
|
||||
@@ -2127,4 +2250,7 @@ void mingw_startup()
|
||||
|
||||
/* initialize Unicode console */
|
||||
winansi_init();
|
||||
|
||||
/* init length of current directory for handle_long_path */
|
||||
current_directory_len = GetCurrentDirectoryW(0, NULL);
|
||||
}
|
||||
|
||||
@@ -277,6 +277,22 @@ static inline int getrlimit(int resource, struct rlimit *rlp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
|
||||
* Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
|
||||
*/
|
||||
static inline long long filetime_to_hnsec(const FILETIME *ft)
|
||||
{
|
||||
long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
|
||||
/* Windows to Unix Epoch conversion */
|
||||
return winTime - 116444736000000000LL;
|
||||
}
|
||||
|
||||
static inline time_t filetime_to_time_t(const FILETIME *ft)
|
||||
{
|
||||
return (time_t)(filetime_to_hnsec(ft) / 10000000);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use mingw specific stat()/lstat()/fstat() implementations on Windows.
|
||||
*/
|
||||
@@ -298,7 +314,7 @@ int mingw_fstat(int fd, struct stat *buf);
|
||||
#ifdef lstat
|
||||
#undef lstat
|
||||
#endif
|
||||
#define lstat mingw_lstat
|
||||
extern int (*lstat)(const char *file_name, struct stat *buf);
|
||||
|
||||
#ifndef _stati64
|
||||
# define _stati64(x,y) mingw_stat(x,y)
|
||||
@@ -363,6 +379,42 @@ void mingw_open_html(const char *path);
|
||||
void mingw_mark_as_git_dir(const char *dir);
|
||||
#define mark_as_git_dir mingw_mark_as_git_dir
|
||||
|
||||
/**
|
||||
* Max length of long paths (exceeding MAX_PATH). The actual maximum supported
|
||||
* by NTFS is 32,767 (* sizeof(wchar_t)), but we choose an arbitrary smaller
|
||||
* value to limit required stack memory.
|
||||
*/
|
||||
#define MAX_LONG_PATH 4096
|
||||
|
||||
/**
|
||||
* Handles paths that would exceed the MAX_PATH limit of Windows Unicode APIs.
|
||||
*
|
||||
* With expand == false, the function checks for over-long paths and fails
|
||||
* with ENAMETOOLONG. The path parameter is not modified, except if cwd + path
|
||||
* exceeds max_path, but the resulting absolute path doesn't (e.g. due to
|
||||
* eliminating '..' components). The path parameter must point to a buffer
|
||||
* of max_path wide characters.
|
||||
*
|
||||
* With expand == true, an over-long path is automatically converted in place
|
||||
* to an absolute path prefixed with '\\?\', and the new length is returned.
|
||||
* The path parameter must point to a buffer of MAX_LONG_PATH wide characters.
|
||||
*
|
||||
* Parameters:
|
||||
* path: path to check and / or convert
|
||||
* len: size of path on input (number of wide chars without \0)
|
||||
* max_path: max short path length to check (usually MAX_PATH = 260, but just
|
||||
* 248 for CreateDirectoryW)
|
||||
* expand: false to only check the length, true to expand the path to a
|
||||
* '\\?\'-prefixed absolute path
|
||||
*
|
||||
* Return:
|
||||
* length of the resulting path, or -1 on failure
|
||||
*
|
||||
* Errors:
|
||||
* ENAMETOOLONG if path is too long
|
||||
*/
|
||||
int handle_long_path(wchar_t *path, int len, int max_path, int expand);
|
||||
|
||||
/**
|
||||
* Converts UTF-8 encoded string to UTF-16LE.
|
||||
*
|
||||
@@ -420,17 +472,48 @@ static inline int xutftowcs(wchar_t *wcs, const char *utf, size_t wcslen)
|
||||
return xutftowcsn(wcs, utf, wcslen, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified file system specific wrapper of xutftowcsn and handle_long_path.
|
||||
* Converts ERANGE to ENAMETOOLONG. If expand is true, wcs must be at least
|
||||
* MAX_LONG_PATH wide chars (see handle_long_path).
|
||||
*/
|
||||
static inline int xutftowcs_path_ex(wchar_t *wcs, const char *utf,
|
||||
size_t wcslen, int utflen, int max_path, int expand)
|
||||
{
|
||||
int result = xutftowcsn(wcs, utf, wcslen, utflen);
|
||||
if (result < 0 && errno == ERANGE)
|
||||
errno = ENAMETOOLONG;
|
||||
if (result >= 0)
|
||||
result = handle_long_path(wcs, result, max_path, expand);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified file system specific variant of xutftowcsn, assumes output
|
||||
* buffer size is MAX_PATH wide chars and input string is \0-terminated,
|
||||
* fails with ENAMETOOLONG if input string is too long.
|
||||
* fails with ENAMETOOLONG if input string is too long. Typically used for
|
||||
* Windows APIs that don't support long paths, e.g. SetCurrentDirectory,
|
||||
* LoadLibrary, CreateProcess...
|
||||
*/
|
||||
static inline int xutftowcs_path(wchar_t *wcs, const char *utf)
|
||||
{
|
||||
int result = xutftowcsn(wcs, utf, MAX_PATH, -1);
|
||||
if (result < 0 && errno == ERANGE)
|
||||
errno = ENAMETOOLONG;
|
||||
return result;
|
||||
return xutftowcs_path_ex(wcs, utf, MAX_PATH, -1, MAX_PATH, 0);
|
||||
}
|
||||
|
||||
/* need to re-declare that here as mingw.h is included before cache.h */
|
||||
extern int core_long_paths;
|
||||
|
||||
/**
|
||||
* Simplified file system specific variant of xutftowcsn for Windows APIs
|
||||
* that support long paths via '\\?\'-prefix, assumes output buffer size is
|
||||
* MAX_LONG_PATH wide chars, fails with ENAMETOOLONG if input string is too
|
||||
* long. The 'core.longpaths' git-config option controls whether the path
|
||||
* is only checked or expanded to a long path.
|
||||
*/
|
||||
static inline int xutftowcs_long_path(wchar_t *wcs, const char *utf)
|
||||
{
|
||||
return xutftowcs_path_ex(wcs, utf, MAX_LONG_PATH, -1, MAX_PATH,
|
||||
core_long_paths);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
#include "../../git-compat-util.h"
|
||||
|
||||
struct DIR {
|
||||
typedef struct dirent_DIR {
|
||||
struct DIR base_dir; /* extend base struct DIR */
|
||||
struct dirent dd_dir; /* includes d_type */
|
||||
HANDLE dd_handle; /* FindFirstFile handle */
|
||||
int dd_stat; /* 0-based index */
|
||||
};
|
||||
char dd_name[MAX_PATH * 3]; /* file name (* 3 for UTF-8 conversion) */
|
||||
} dirent_DIR;
|
||||
|
||||
DIR *(*opendir)(const char *dirname) = dirent_opendir;
|
||||
|
||||
static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
|
||||
{
|
||||
/* convert UTF-16 name to UTF-8 */
|
||||
xwcstoutf(ent->d_name, fdata->cFileName, sizeof(ent->d_name));
|
||||
/* convert UTF-16 name to UTF-8 (d_name points to dirent_DIR.dd_name) */
|
||||
xwcstoutf(ent->d_name, fdata->cFileName, MAX_PATH * 3);
|
||||
|
||||
/* Set file type, based on WIN32_FIND_DATA */
|
||||
if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
@@ -18,41 +22,7 @@ static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata)
|
||||
ent->d_type = DT_REG;
|
||||
}
|
||||
|
||||
DIR *opendir(const char *name)
|
||||
{
|
||||
wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */
|
||||
WIN32_FIND_DATAW fdata;
|
||||
HANDLE h;
|
||||
int len;
|
||||
DIR *dir;
|
||||
|
||||
/* convert name to UTF-16 and check length < MAX_PATH */
|
||||
if ((len = xutftowcs_path(pattern, name)) < 0)
|
||||
return NULL;
|
||||
|
||||
/* append optional '/' and wildcard '*' */
|
||||
if (len && !is_dir_sep(pattern[len - 1]))
|
||||
pattern[len++] = '/';
|
||||
pattern[len++] = '*';
|
||||
pattern[len] = 0;
|
||||
|
||||
/* open find handle */
|
||||
h = FindFirstFileW(pattern, &fdata);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
DWORD err = GetLastError();
|
||||
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* initialize DIR structure and copy first dir entry */
|
||||
dir = xmalloc(sizeof(DIR));
|
||||
dir->dd_handle = h;
|
||||
dir->dd_stat = 0;
|
||||
finddata2dirent(&dir->dd_dir, &fdata);
|
||||
return dir;
|
||||
}
|
||||
|
||||
struct dirent *readdir(DIR *dir)
|
||||
static struct dirent *dirent_readdir(dirent_DIR *dir)
|
||||
{
|
||||
if (!dir) {
|
||||
errno = EBADF; /* No set_errno for mingw */
|
||||
@@ -79,7 +49,7 @@ struct dirent *readdir(DIR *dir)
|
||||
return &dir->dd_dir;
|
||||
}
|
||||
|
||||
int closedir(DIR *dir)
|
||||
static int dirent_closedir(dirent_DIR *dir)
|
||||
{
|
||||
if (!dir) {
|
||||
errno = EBADF;
|
||||
@@ -90,3 +60,44 @@ int closedir(DIR *dir)
|
||||
free(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DIR *dirent_opendir(const char *name)
|
||||
{
|
||||
wchar_t pattern[MAX_LONG_PATH + 2]; /* + 2 for "\*" */
|
||||
WIN32_FIND_DATAW fdata;
|
||||
HANDLE h;
|
||||
int len;
|
||||
dirent_DIR *dir;
|
||||
|
||||
/* convert name to UTF-16 and check length */
|
||||
if ((len = xutftowcs_path_ex(pattern, name, MAX_LONG_PATH, -1,
|
||||
MAX_PATH - 2, core_long_paths)) < 0)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* append optional '\' and wildcard '*'. Note: we need to use '\' as
|
||||
* Windows doesn't translate '/' to '\' for "\\?\"-prefixed paths.
|
||||
*/
|
||||
if (len && !is_dir_sep(pattern[len - 1]))
|
||||
pattern[len++] = '\\';
|
||||
pattern[len++] = '*';
|
||||
pattern[len] = 0;
|
||||
|
||||
/* open find handle */
|
||||
h = FindFirstFileW(pattern, &fdata);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
DWORD err = GetLastError();
|
||||
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* initialize DIR structure and copy first dir entry */
|
||||
dir = xmalloc(sizeof(dirent_DIR));
|
||||
dir->base_dir.preaddir = (struct dirent *(*)(DIR *dir)) dirent_readdir;
|
||||
dir->base_dir.pclosedir = (int (*)(DIR *dir)) dirent_closedir;
|
||||
dir->dd_dir.d_name = dir->dd_name;
|
||||
dir->dd_handle = h;
|
||||
dir->dd_stat = 0;
|
||||
finddata2dirent(&dir->dd_dir, &fdata);
|
||||
return (DIR*) dir;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,32 @@
|
||||
#ifndef DIRENT_H
|
||||
#define DIRENT_H
|
||||
|
||||
typedef struct DIR DIR;
|
||||
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_DIR 1
|
||||
#define DT_REG 2
|
||||
#define DT_LNK 3
|
||||
|
||||
struct dirent {
|
||||
unsigned char d_type; /* file type to prevent lstat after readdir */
|
||||
char d_name[MAX_PATH * 3]; /* file name (* 3 for UTF-8 conversion) */
|
||||
unsigned char d_type; /* file type to prevent lstat after readdir */
|
||||
char *d_name; /* file name */
|
||||
};
|
||||
|
||||
DIR *opendir(const char *dirname);
|
||||
struct dirent *readdir(DIR *dir);
|
||||
int closedir(DIR *dir);
|
||||
/*
|
||||
* Base DIR structure, contains pointers to readdir/closedir implementations so
|
||||
* that opendir may choose a concrete implementation on a call-by-call basis.
|
||||
*/
|
||||
typedef struct DIR {
|
||||
struct dirent *(*preaddir)(struct DIR *dir);
|
||||
int (*pclosedir)(struct DIR *dir);
|
||||
} DIR;
|
||||
|
||||
/* default dirent implementation */
|
||||
extern DIR *dirent_opendir(const char *dirname);
|
||||
|
||||
/* current dirent implementation */
|
||||
extern DIR *(*opendir)(const char *dirname);
|
||||
|
||||
#define readdir(dir) (dir->preaddir(dir))
|
||||
#define closedir(dir) (dir->pclosedir(dir))
|
||||
|
||||
#endif /* DIRENT_H */
|
||||
|
||||
506
compat/win32/fscache.c
Normal file
506
compat/win32/fscache.c
Normal file
@@ -0,0 +1,506 @@
|
||||
#include "../../cache.h"
|
||||
#include "../../hashmap.h"
|
||||
#include "../win32.h"
|
||||
#include "fscache.h"
|
||||
|
||||
static int initialized;
|
||||
static volatile long enabled;
|
||||
static struct hashmap map;
|
||||
static CRITICAL_SECTION mutex;
|
||||
|
||||
/*
|
||||
* An entry in the file system cache. Used for both entire directory listings
|
||||
* and file entries.
|
||||
*/
|
||||
struct fsentry {
|
||||
struct hashmap_entry ent;
|
||||
mode_t st_mode;
|
||||
/* Length of name. */
|
||||
unsigned short len;
|
||||
/*
|
||||
* Name of the entry. For directory listings: relative path of the
|
||||
* directory, without trailing '/' (empty for cwd()). For file entries:
|
||||
* name of the file. Typically points to the end of the structure if
|
||||
* the fsentry is allocated on the heap (see fsentry_alloc), or to a
|
||||
* local variable if on the stack (see fsentry_init).
|
||||
*/
|
||||
const char *name;
|
||||
/* Pointer to the directory listing, or NULL for the listing itself. */
|
||||
struct fsentry *list;
|
||||
/* Pointer to the next file entry of the list. */
|
||||
struct fsentry *next;
|
||||
|
||||
union {
|
||||
/* Reference count of the directory listing. */
|
||||
volatile long refcnt;
|
||||
/* Handle to wait on the loading thread. */
|
||||
HANDLE hwait;
|
||||
struct {
|
||||
/* More stat members (only used for file entries). */
|
||||
off64_t st_size;
|
||||
time_t st_atime;
|
||||
time_t st_mtime;
|
||||
time_t st_ctime;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Compares the paths of two fsentry structures for equality.
|
||||
*/
|
||||
static int fsentry_cmp(const struct fsentry *fse1, const struct fsentry *fse2)
|
||||
{
|
||||
int res;
|
||||
if (fse1 == fse2)
|
||||
return 0;
|
||||
|
||||
/* compare the list parts first */
|
||||
if (fse1->list != fse2->list && (res = fsentry_cmp(
|
||||
fse1->list ? fse1->list : fse1,
|
||||
fse2->list ? fse2->list : fse2)))
|
||||
return res;
|
||||
|
||||
/* if list parts are equal, compare len and name */
|
||||
if (fse1->len != fse2->len)
|
||||
return fse1->len - fse2->len;
|
||||
return strnicmp(fse1->name, fse2->name, fse1->len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the hash code of an fsentry structure's path.
|
||||
*/
|
||||
static unsigned int fsentry_hash(const struct fsentry *fse)
|
||||
{
|
||||
unsigned int hash = fse->list ? fse->list->ent.hash : 0;
|
||||
return hash ^ memihash(fse->name, fse->len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize an fsentry structure for use by fsentry_hash and fsentry_cmp.
|
||||
*/
|
||||
static void fsentry_init(struct fsentry *fse, struct fsentry *list,
|
||||
const char *name, size_t len)
|
||||
{
|
||||
fse->list = list;
|
||||
fse->name = name;
|
||||
fse->len = len;
|
||||
hashmap_entry_init(fse, fsentry_hash(fse));
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate an fsentry structure on the heap.
|
||||
*/
|
||||
static struct fsentry *fsentry_alloc(struct fsentry *list, const char *name,
|
||||
size_t len)
|
||||
{
|
||||
/* overallocate fsentry and copy the name to the end */
|
||||
struct fsentry *fse = xmalloc(sizeof(struct fsentry) + len + 1);
|
||||
char *nm = ((char*) fse) + sizeof(struct fsentry);
|
||||
memcpy(nm, name, len);
|
||||
nm[len] = 0;
|
||||
/* init the rest of the structure */
|
||||
fsentry_init(fse, list, nm, len);
|
||||
fse->next = NULL;
|
||||
fse->refcnt = 1;
|
||||
return fse;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a reference to an fsentry.
|
||||
*/
|
||||
inline static void fsentry_addref(struct fsentry *fse)
|
||||
{
|
||||
if (fse->list)
|
||||
fse = fse->list;
|
||||
|
||||
InterlockedIncrement(&(fse->refcnt));
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the reference to an fsentry, frees the memory if its the last ref.
|
||||
*/
|
||||
static void fsentry_release(struct fsentry *fse)
|
||||
{
|
||||
if (fse->list)
|
||||
fse = fse->list;
|
||||
|
||||
if (InterlockedDecrement(&(fse->refcnt)))
|
||||
return;
|
||||
|
||||
while (fse) {
|
||||
struct fsentry *next = fse->next;
|
||||
free(fse);
|
||||
fse = next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and initialize an fsentry from a WIN32_FIND_DATA structure.
|
||||
*/
|
||||
static struct fsentry *fseentry_create_entry(struct fsentry *list,
|
||||
const WIN32_FIND_DATAW *fdata)
|
||||
{
|
||||
char buf[MAX_PATH * 3];
|
||||
int len;
|
||||
struct fsentry *fse;
|
||||
len = xwcstoutf(buf, fdata->cFileName, ARRAY_SIZE(buf));
|
||||
|
||||
fse = fsentry_alloc(list, buf, len);
|
||||
|
||||
fse->st_mode = file_attr_to_st_mode(fdata->dwFileAttributes);
|
||||
fse->st_size = (((off64_t) (fdata->nFileSizeHigh)) << 32)
|
||||
| fdata->nFileSizeLow;
|
||||
fse->st_atime = filetime_to_time_t(&(fdata->ftLastAccessTime));
|
||||
fse->st_mtime = filetime_to_time_t(&(fdata->ftLastWriteTime));
|
||||
fse->st_ctime = filetime_to_time_t(&(fdata->ftCreationTime));
|
||||
|
||||
return fse;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an fsentry-based directory listing (similar to opendir / readdir).
|
||||
* Dir should not contain trailing '/'. Use an empty string for the current
|
||||
* directory (not "."!).
|
||||
*/
|
||||
static struct fsentry *fsentry_create_list(const struct fsentry *dir)
|
||||
{
|
||||
wchar_t pattern[MAX_LONG_PATH + 2]; /* + 2 for "\*" */
|
||||
WIN32_FIND_DATAW fdata;
|
||||
HANDLE h;
|
||||
int wlen;
|
||||
struct fsentry *list, **phead;
|
||||
DWORD err;
|
||||
|
||||
/* convert name to UTF-16 and check length */
|
||||
if ((wlen = xutftowcs_path_ex(pattern, dir->name, MAX_LONG_PATH,
|
||||
dir->len, MAX_PATH - 2, core_long_paths)) < 0)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* append optional '\' and wildcard '*'. Note: we need to use '\' as
|
||||
* Windows doesn't translate '/' to '\' for "\\?\"-prefixed paths.
|
||||
*/
|
||||
if (wlen)
|
||||
pattern[wlen++] = '\\';
|
||||
pattern[wlen++] = '*';
|
||||
pattern[wlen] = 0;
|
||||
|
||||
/* open find handle */
|
||||
h = FindFirstFileW(pattern, &fdata);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
err = GetLastError();
|
||||
errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* allocate object to hold directory listing */
|
||||
list = fsentry_alloc(NULL, dir->name, dir->len);
|
||||
|
||||
/* walk directory and build linked list of fsentry structures */
|
||||
phead = &list->next;
|
||||
do {
|
||||
*phead = fseentry_create_entry(list, &fdata);
|
||||
phead = &(*phead)->next;
|
||||
} while (FindNextFileW(h, &fdata));
|
||||
|
||||
/* remember result of last FindNextFile, then close find handle */
|
||||
err = GetLastError();
|
||||
FindClose(h);
|
||||
|
||||
/* return the list if we've got all the files */
|
||||
if (err == ERROR_NO_MORE_FILES)
|
||||
return list;
|
||||
|
||||
/* otherwise free the list and return error */
|
||||
fsentry_release(list);
|
||||
errno = err_win_to_posix(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds a directory listing to the cache.
|
||||
*/
|
||||
static void fscache_add(struct fsentry *fse)
|
||||
{
|
||||
if (fse->list)
|
||||
fse = fse->list;
|
||||
|
||||
for (; fse; fse = fse->next)
|
||||
hashmap_add(&map, fse);
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes a directory listing from the cache.
|
||||
*/
|
||||
static void fscache_remove(struct fsentry *fse)
|
||||
{
|
||||
if (fse->list)
|
||||
fse = fse->list;
|
||||
|
||||
for (; fse; fse = fse->next)
|
||||
hashmap_remove(&map, fse, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clears the cache.
|
||||
*/
|
||||
static void fscache_clear()
|
||||
{
|
||||
struct hashmap_iter iter;
|
||||
struct fsentry *fse;
|
||||
while ((fse = hashmap_iter_first(&map, &iter))) {
|
||||
fscache_remove(fse);
|
||||
fsentry_release(fse);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the cache is enabled for the given path.
|
||||
*/
|
||||
static inline int fscache_enabled(const char *path)
|
||||
{
|
||||
return enabled > 0 && !is_absolute_path(path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Looks up a cache entry, waits if its being loaded by another thread.
|
||||
* The mutex must be owned by the calling thread.
|
||||
*/
|
||||
static struct fsentry *fscache_get_wait(struct fsentry *key)
|
||||
{
|
||||
struct fsentry *fse = hashmap_get(&map, key, NULL);
|
||||
|
||||
/* return if its a 'real' entry (future entries have refcnt == 0) */
|
||||
if (!fse || fse->list || fse->refcnt)
|
||||
return fse;
|
||||
|
||||
/* create an event and link our key to the future entry */
|
||||
key->hwait = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
key->next = fse->next;
|
||||
fse->next = key;
|
||||
|
||||
/* wait for the loading thread to signal us */
|
||||
LeaveCriticalSection(&mutex);
|
||||
WaitForSingleObject(key->hwait, INFINITE);
|
||||
CloseHandle(key->hwait);
|
||||
EnterCriticalSection(&mutex);
|
||||
|
||||
/* repeat cache lookup */
|
||||
return hashmap_get(&map, key, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Looks up or creates a cache entry for the specified key.
|
||||
*/
|
||||
static struct fsentry *fscache_get(struct fsentry *key)
|
||||
{
|
||||
struct fsentry *fse, *future, *waiter;
|
||||
|
||||
EnterCriticalSection(&mutex);
|
||||
/* check if entry is in cache */
|
||||
fse = fscache_get_wait(key);
|
||||
if (fse) {
|
||||
fsentry_addref(fse);
|
||||
LeaveCriticalSection(&mutex);
|
||||
return fse;
|
||||
}
|
||||
/* if looking for a file, check if directory listing is in cache */
|
||||
if (!fse && key->list) {
|
||||
fse = fscache_get_wait(key->list);
|
||||
if (fse) {
|
||||
LeaveCriticalSection(&mutex);
|
||||
/* dir entry without file entry -> file doesn't exist */
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* add future entry to indicate that we're loading it */
|
||||
future = key->list ? key->list : key;
|
||||
future->next = NULL;
|
||||
future->refcnt = 0;
|
||||
hashmap_add(&map, future);
|
||||
|
||||
/* create the directory listing (outside mutex!) */
|
||||
LeaveCriticalSection(&mutex);
|
||||
fse = fsentry_create_list(future);
|
||||
EnterCriticalSection(&mutex);
|
||||
|
||||
/* remove future entry and signal waiting threads */
|
||||
hashmap_remove(&map, future, NULL);
|
||||
waiter = future->next;
|
||||
while (waiter) {
|
||||
HANDLE h = waiter->hwait;
|
||||
waiter = waiter->next;
|
||||
SetEvent(h);
|
||||
}
|
||||
|
||||
/* leave on error (errno set by fsentry_create_list) */
|
||||
if (!fse) {
|
||||
LeaveCriticalSection(&mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* add directory listing to the cache */
|
||||
fscache_add(fse);
|
||||
|
||||
/* lookup file entry if requested (fse already points to directory) */
|
||||
if (key->list)
|
||||
fse = hashmap_get(&map, key, NULL);
|
||||
|
||||
/* return entry or ENOENT */
|
||||
if (fse)
|
||||
fsentry_addref(fse);
|
||||
else
|
||||
errno = ENOENT;
|
||||
|
||||
LeaveCriticalSection(&mutex);
|
||||
return fse;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enables or disables the cache. Note that the cache is read-only, changes to
|
||||
* the working directory are NOT reflected in the cache while enabled.
|
||||
*/
|
||||
int fscache_enable(int enable)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (!initialized) {
|
||||
/* allow the cache to be disabled entirely */
|
||||
if (!core_fscache)
|
||||
return 0;
|
||||
|
||||
InitializeCriticalSection(&mutex);
|
||||
hashmap_init(&map, (hashmap_cmp_fn) fsentry_cmp, 0);
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
result = enable ? InterlockedIncrement(&enabled)
|
||||
: InterlockedDecrement(&enabled);
|
||||
|
||||
if (enable && result == 1) {
|
||||
/* redirect opendir and lstat to the fscache implementations */
|
||||
opendir = fscache_opendir;
|
||||
lstat = fscache_lstat;
|
||||
} else if (!enable && !result) {
|
||||
/* reset opendir and lstat to the original implementations */
|
||||
opendir = dirent_opendir;
|
||||
lstat = mingw_lstat;
|
||||
EnterCriticalSection(&mutex);
|
||||
fscache_clear();
|
||||
LeaveCriticalSection(&mutex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lstat replacement, uses the cache if enabled, otherwise redirects to
|
||||
* mingw_lstat.
|
||||
*/
|
||||
int fscache_lstat(const char *filename, struct stat *st)
|
||||
{
|
||||
int dirlen, base, len;
|
||||
struct fsentry key[2], *fse;
|
||||
|
||||
if (!fscache_enabled(filename))
|
||||
return mingw_lstat(filename, st);
|
||||
|
||||
/* split filename into path + name */
|
||||
len = strlen(filename);
|
||||
if (len && is_dir_sep(filename[len - 1]))
|
||||
len--;
|
||||
base = len;
|
||||
while (base && !is_dir_sep(filename[base - 1]))
|
||||
base--;
|
||||
dirlen = base ? base - 1 : 0;
|
||||
|
||||
/* lookup entry for path + name in cache */
|
||||
fsentry_init(key, NULL, filename, dirlen);
|
||||
fsentry_init(key + 1, key, filename + base, len - base);
|
||||
fse = fscache_get(key + 1);
|
||||
if (!fse)
|
||||
return -1;
|
||||
|
||||
/* copy stat data */
|
||||
st->st_ino = 0;
|
||||
st->st_gid = 0;
|
||||
st->st_uid = 0;
|
||||
st->st_dev = 0;
|
||||
st->st_rdev = 0;
|
||||
st->st_nlink = 1;
|
||||
st->st_mode = fse->st_mode;
|
||||
st->st_size = fse->st_size;
|
||||
st->st_atime = fse->st_atime;
|
||||
st->st_mtime = fse->st_mtime;
|
||||
st->st_ctime = fse->st_ctime;
|
||||
|
||||
/* don't forget to release fsentry */
|
||||
fsentry_release(fse);
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct fscache_DIR {
|
||||
struct DIR base_dir; /* extend base struct DIR */
|
||||
struct fsentry *pfsentry;
|
||||
struct dirent dirent;
|
||||
} fscache_DIR;
|
||||
|
||||
/*
|
||||
* Readdir replacement.
|
||||
*/
|
||||
static struct dirent *fscache_readdir(DIR *base_dir)
|
||||
{
|
||||
fscache_DIR *dir = (fscache_DIR*) base_dir;
|
||||
struct fsentry *next = dir->pfsentry->next;
|
||||
if (!next)
|
||||
return NULL;
|
||||
dir->pfsentry = next;
|
||||
dir->dirent.d_type = S_ISDIR(next->st_mode) ? DT_DIR : DT_REG;
|
||||
dir->dirent.d_name = (char*) next->name;
|
||||
return &(dir->dirent);
|
||||
}
|
||||
|
||||
/*
|
||||
* Closedir replacement.
|
||||
*/
|
||||
static int fscache_closedir(DIR *base_dir)
|
||||
{
|
||||
fscache_DIR *dir = (fscache_DIR*) base_dir;
|
||||
fsentry_release(dir->pfsentry);
|
||||
free(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Opendir replacement, uses a directory listing from the cache if enabled,
|
||||
* otherwise calls original dirent implementation.
|
||||
*/
|
||||
DIR *fscache_opendir(const char *dirname)
|
||||
{
|
||||
struct fsentry key, *list;
|
||||
fscache_DIR *dir;
|
||||
int len;
|
||||
|
||||
if (!fscache_enabled(dirname))
|
||||
return dirent_opendir(dirname);
|
||||
|
||||
/* prepare name (strip trailing '/', replace '.') */
|
||||
len = strlen(dirname);
|
||||
if ((len == 1 && dirname[0] == '.') ||
|
||||
(len && is_dir_sep(dirname[len - 1])))
|
||||
len--;
|
||||
|
||||
/* get directory listing from cache */
|
||||
fsentry_init(&key, NULL, dirname, len);
|
||||
list = fscache_get(&key);
|
||||
if (!list)
|
||||
return NULL;
|
||||
|
||||
/* alloc and return DIR structure */
|
||||
dir = (fscache_DIR*) xmalloc(sizeof(fscache_DIR));
|
||||
dir->base_dir.preaddir = fscache_readdir;
|
||||
dir->base_dir.pclosedir = fscache_closedir;
|
||||
dir->pfsentry = list;
|
||||
return (DIR*) dir;
|
||||
}
|
||||
10
compat/win32/fscache.h
Normal file
10
compat/win32/fscache.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef FSCACHE_H
|
||||
#define FSCACHE_H
|
||||
|
||||
int fscache_enable(int enable);
|
||||
#define enable_fscache(x) fscache_enable(x)
|
||||
|
||||
DIR *fscache_opendir(const char *dir);
|
||||
int fscache_lstat(const char *file_name, struct stat *buf);
|
||||
|
||||
#endif
|
||||
19
config.c
19
config.c
@@ -880,6 +880,25 @@ static int git_default_core_config(const char *var, const char *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.hidedotfiles")) {
|
||||
if (value && !strcasecmp(value, "dotgitonly")) {
|
||||
hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
|
||||
return 0;
|
||||
}
|
||||
hide_dotfiles = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.fscache")) {
|
||||
core_fscache = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.longpaths")) {
|
||||
core_long_paths = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add other config variables here and to Documentation/config.txt. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -362,7 +362,7 @@ ifeq ($(uname_S),Windows)
|
||||
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
|
||||
COMPAT_OBJS = compat/msvc.o compat/winansi.o \
|
||||
compat/win32/pthread.o compat/win32/syslog.o \
|
||||
compat/win32/dirent.o
|
||||
compat/win32/dirent.o compat/win32/fscache.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
|
||||
@@ -508,7 +508,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
|
||||
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
|
||||
COMPAT_OBJS += compat/mingw.o compat/winansi.o \
|
||||
compat/win32/pthread.o compat/win32/syslog.o \
|
||||
compat/win32/dirent.o
|
||||
compat/win32/dirent.o compat/win32/fscache.o
|
||||
BASIC_LDFLAGS += -Wl,--large-address-aware
|
||||
EXTLIBS += -lws2_32
|
||||
GITLIBS += git.res
|
||||
|
||||
11
configure.ac
11
configure.ac
@@ -521,6 +521,17 @@ AC_CHECK_LIB([curl], [curl_global_init],
|
||||
[NO_CURL=],
|
||||
[NO_CURL=YesPlease])
|
||||
|
||||
if test -z "$NO_CURL"; then
|
||||
|
||||
AC_CHECK_DECLS([curl_multi_init],
|
||||
[NO_CURL_MULTI=],
|
||||
[NO_CURL_MULTI=UnfortunatelyYes],
|
||||
[[#include <curl/curl.h>]])
|
||||
|
||||
GIT_CONF_SUBST([NO_CURL_MULTI])
|
||||
|
||||
fi
|
||||
|
||||
GIT_UNSTASH_FLAGS($CURLDIR)
|
||||
|
||||
GIT_CONF_SUBST([NO_CURL])
|
||||
|
||||
@@ -94,6 +94,12 @@ static WCHAR *wusername, *password, *protocol, *host, *path, target[1024];
|
||||
static void write_item(const char *what, LPCWSTR wbuf, int wlen)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
if (!wbuf || !wlen) {
|
||||
printf("%s=\n", what);
|
||||
return;
|
||||
}
|
||||
|
||||
int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, wlen, NULL, 0, NULL,
|
||||
FALSE);
|
||||
buf = xmalloc(len);
|
||||
@@ -141,7 +147,7 @@ static int match_part(LPCWSTR *ptarget, LPCWSTR want, LPCWSTR delim)
|
||||
static int match_cred(const CREDENTIALW *cred)
|
||||
{
|
||||
LPCWSTR target = cred->TargetName;
|
||||
if (wusername && wcscmp(wusername, cred->UserName))
|
||||
if (wusername && wcscmp(wusername, cred->UserName ? cred->UserName : L""))
|
||||
return 0;
|
||||
|
||||
return match_part(&target, L"git", L":") &&
|
||||
@@ -164,7 +170,7 @@ static void get_credential(void)
|
||||
for (i = 0; i < num_creds; ++i)
|
||||
if (match_cred(creds[i])) {
|
||||
write_item("username", creds[i]->UserName,
|
||||
wcslen(creds[i]->UserName));
|
||||
creds[i]->UserName ? wcslen(creds[i]->UserName) : 0);
|
||||
write_item("password",
|
||||
(LPCWSTR)creds[i]->CredentialBlob,
|
||||
creds[i]->CredentialBlobSize / sizeof(WCHAR));
|
||||
|
||||
@@ -63,6 +63,9 @@ int merge_log_config = -1;
|
||||
int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
|
||||
struct startup_info *startup_info;
|
||||
unsigned long pack_size_limit_cfg;
|
||||
enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
|
||||
int core_fscache;
|
||||
int core_long_paths;
|
||||
|
||||
/*
|
||||
* The character that begins a commented line in user-editable file
|
||||
|
||||
15
git-am.sh
15
git-am.sh
@@ -184,7 +184,9 @@ It does not apply to blobs recorded in its index.")"
|
||||
}
|
||||
|
||||
clean_abort () {
|
||||
test $# = 0 || echo >&2 "$@"
|
||||
test $# = 0 || cat >&2 <<EOF
|
||||
$@
|
||||
EOF
|
||||
rm -fr "$dotest"
|
||||
exit 1
|
||||
}
|
||||
@@ -593,7 +595,8 @@ case "$resolved" in
|
||||
'')
|
||||
files=$(git ls-files) ;;
|
||||
?*)
|
||||
files=$(git diff-index --cached --name-only HEAD --) ;;
|
||||
files=$(git diff-index --ignore-submodules --cached \
|
||||
--name-only HEAD --) ;;
|
||||
esac || exit
|
||||
if test "$files"
|
||||
then
|
||||
@@ -767,7 +770,8 @@ To restore the original branch and stop patching run \"\$cmdline --abort\"."
|
||||
case "$resolved$interactive" in
|
||||
tt)
|
||||
# This is used only for interactive view option.
|
||||
git diff-index -p --cached HEAD -- >"$dotest/patch"
|
||||
git diff-index --ignore-submodules -p --cached \
|
||||
HEAD -- >"$dotest/patch"
|
||||
;;
|
||||
esac
|
||||
esac
|
||||
@@ -843,7 +847,7 @@ To restore the original branch and stop patching run \"\$cmdline --abort\"."
|
||||
# trust what the user has in the index file and the
|
||||
# working tree.
|
||||
resolved=
|
||||
git diff-index --quiet --cached HEAD -- && {
|
||||
git diff-index --ignore-submodules --quiet --cached HEAD -- && {
|
||||
gettextln "No changes - did you forget to use 'git add'?
|
||||
If there is nothing left to stage, chances are that something else
|
||||
already introduced the same changes; you might want to skip this patch."
|
||||
@@ -867,7 +871,8 @@ did you forget to use 'git add'?"
|
||||
then
|
||||
# Applying the patch to an earlier tree and merging the
|
||||
# result may have produced the same tree as ours.
|
||||
git diff-index --quiet --cached HEAD -- && {
|
||||
git diff-index --ignore-submodules --quiet --cached \
|
||||
HEAD -- && {
|
||||
say "$(gettext "No changes -- Patch already applied.")"
|
||||
go_next
|
||||
continue
|
||||
|
||||
@@ -129,8 +129,10 @@
|
||||
#if defined(__MINGW32__)
|
||||
/* pull in Windows compatibility stuff */
|
||||
#include "compat/mingw.h"
|
||||
#include "compat/win32/fscache.h"
|
||||
#elif defined(_MSC_VER)
|
||||
#include "compat/msvc.h"
|
||||
#include "compat/win32/fscache.h"
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#include <sys/resource.h>
|
||||
@@ -790,4 +792,23 @@ struct tm *git_gmtime_r(const time_t *, struct tm *);
|
||||
#define gmtime_r git_gmtime_r
|
||||
#endif
|
||||
|
||||
#ifndef mark_as_git_dir
|
||||
#define mark_as_git_dir(x) /* noop */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enable/disable a read-only cache for file system data on platforms that
|
||||
* support it.
|
||||
*
|
||||
* Implementing a live-cache is complicated and requires special platform
|
||||
* support (inotify, ReadDirectoryChangesW...). enable_fscache shall be used
|
||||
* to mark sections of git code that extensively read from the file system
|
||||
* without modifying anything. Implementations can use this to cache e.g. stat
|
||||
* data or even file content without the need to synchronize with the file
|
||||
* system.
|
||||
*/
|
||||
#ifndef enable_fscache
|
||||
#define enable_fscache(x) /* noop */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -290,6 +290,7 @@ install: all
|
||||
$(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL_D1)
|
||||
$(QUIET)$(INSTALL_X0)git-gui $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
|
||||
$(QUIET)$(INSTALL_X0)git-gui--askpass $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
|
||||
$(QUIET)$(INSTALL_X0)git-gui--askyesno $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
|
||||
$(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(INSTALL_L0)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L1)'$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' $(INSTALL_L2)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L3) &&) true
|
||||
ifdef GITGUI_WINDOWS_WRAPPER
|
||||
$(QUIET)$(INSTALL_R0)git-gui.tcl $(INSTALL_R1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
|
||||
@@ -308,6 +309,7 @@ uninstall:
|
||||
$(QUIET)$(CLEAN_DST) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
|
||||
$(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui $(REMOVE_F1)
|
||||
$(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui--askpass $(REMOVE_F1)
|
||||
$(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui--askyesno $(REMOVE_F1)
|
||||
$(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/$p $(REMOVE_F1) &&) true
|
||||
ifdef GITGUI_WINDOWS_WRAPPER
|
||||
$(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui.tcl $(REMOVE_F1)
|
||||
|
||||
51
git-gui/git-gui--askyesno
Executable file
51
git-gui/git-gui--askyesno
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/bin/sh
|
||||
# Tcl ignores the next line -*- tcl -*- \
|
||||
exec wish "$0" -- "$@"
|
||||
|
||||
# This is an implementation of a simple yes no dialog
|
||||
# which is injected into the git commandline by git gui
|
||||
# in case a yesno question needs to be answered.
|
||||
|
||||
set NS {}
|
||||
set use_ttk [package vsatisfies [package provide Tk] 8.5]
|
||||
if {$use_ttk} {
|
||||
set NS ttk
|
||||
}
|
||||
|
||||
if {$argc < 1} {
|
||||
puts stderr "Usage: $argv0 <question>"
|
||||
exit 1
|
||||
} else {
|
||||
set prompt [join $argv " "]
|
||||
}
|
||||
|
||||
${NS}::frame .t
|
||||
${NS}::label .t.m -text $prompt -justify center -width 40
|
||||
.t.m configure -wraplength 400
|
||||
pack .t.m -side top -fill x -padx 20 -pady 20 -expand 1
|
||||
pack .t -side top -fill x -ipadx 20 -ipady 20 -expand 1
|
||||
|
||||
${NS}::frame .b
|
||||
${NS}::frame .b.left -width 200
|
||||
${NS}::button .b.yes -text Yes -command yes
|
||||
${NS}::button .b.no -text No -command no
|
||||
|
||||
|
||||
pack .b.left -side left -expand 1 -fill x
|
||||
pack .b.yes -side left -expand 1
|
||||
pack .b.no -side right -expand 1 -ipadx 5
|
||||
pack .b -side bottom -fill x -ipadx 20 -ipady 15
|
||||
|
||||
bind . <Key-Return> {exit 0}
|
||||
bind . <Key-Escape> {exit 1}
|
||||
|
||||
proc no {} {
|
||||
exit 1
|
||||
}
|
||||
|
||||
proc yes {} {
|
||||
exit 0
|
||||
}
|
||||
|
||||
wm title . "Question?"
|
||||
tk::PlaceWindow .
|
||||
@@ -548,6 +548,9 @@ proc git {args} {
|
||||
|
||||
_trace_exec [concat $opt $cmdp $args]
|
||||
set result [eval exec $opt $cmdp $args]
|
||||
if {[encoding system] != "utf-8"} {
|
||||
set result [encoding convertfrom utf-8 [encoding convertto $result]]
|
||||
}
|
||||
if {$::_trace} {
|
||||
puts stderr "< $result"
|
||||
}
|
||||
@@ -1104,7 +1107,7 @@ git-version proc _parse_config {arr_name args} {
|
||||
[list git_read config] \
|
||||
$args \
|
||||
[list --null --list]]
|
||||
fconfigure $fd_rc -translation binary
|
||||
fconfigure $fd_rc -translation binary -encoding utf-8
|
||||
set buf [read $fd_rc]
|
||||
close $fd_rc
|
||||
}
|
||||
@@ -1241,6 +1244,12 @@ set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
|
||||
if {![info exists env(SSH_ASKPASS)]} {
|
||||
set env(SSH_ASKPASS) [gitexec git-gui--askpass]
|
||||
}
|
||||
if {![info exists env(GIT_ASKPASS)]} {
|
||||
set env(GIT_ASKPASS) [gitexec git-gui--askpass]
|
||||
}
|
||||
if {![info exists env(GIT_ASK_YESNO)]} {
|
||||
set env(GIT_ASK_YESNO) [gitexec git-gui--askyesno]
|
||||
}
|
||||
|
||||
######################################################################
|
||||
##
|
||||
@@ -1337,9 +1346,6 @@ if {[lindex $_reponame end] eq {.git}} {
|
||||
set _reponame [lindex $_reponame end]
|
||||
}
|
||||
|
||||
set env(GIT_DIR) $_gitdir
|
||||
set env(GIT_WORK_TREE) $_gitworktree
|
||||
|
||||
######################################################################
|
||||
##
|
||||
## global init
|
||||
@@ -1682,7 +1688,7 @@ proc read_diff_index {fd after} {
|
||||
set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
|
||||
set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
|
||||
merge_state \
|
||||
[encoding convertfrom $p] \
|
||||
[encoding convertfrom utf-8 $p] \
|
||||
[lindex $i 4]? \
|
||||
[list [lindex $i 0] [lindex $i 2]] \
|
||||
[list]
|
||||
@@ -1715,7 +1721,7 @@ proc read_diff_files {fd after} {
|
||||
set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
|
||||
set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
|
||||
merge_state \
|
||||
[encoding convertfrom $p] \
|
||||
[encoding convertfrom utf-8 $p] \
|
||||
?[lindex $i 4] \
|
||||
[list] \
|
||||
[list [lindex $i 0] [lindex $i 2]]
|
||||
@@ -1738,7 +1744,7 @@ proc read_ls_others {fd after} {
|
||||
set pck [split $buf_rlo "\0"]
|
||||
set buf_rlo [lindex $pck end]
|
||||
foreach p [lrange $pck 0 end-1] {
|
||||
set p [encoding convertfrom $p]
|
||||
set p [encoding convertfrom utf-8 $p]
|
||||
if {[string index $p end] eq {/}} {
|
||||
set p [string range $p 0 end-1]
|
||||
}
|
||||
@@ -2159,7 +2165,7 @@ set starting_gitk_msg [mc "Starting gitk... please wait..."]
|
||||
|
||||
proc do_gitk {revs {is_submodule false}} {
|
||||
global current_diff_path file_states current_diff_side ui_index
|
||||
global _gitdir _gitworktree
|
||||
global _gitworktree
|
||||
|
||||
# -- Always start gitk through whatever we were loaded with. This
|
||||
# lets us bypass using shell process on Windows systems.
|
||||
@@ -2171,12 +2177,19 @@ proc do_gitk {revs {is_submodule false}} {
|
||||
} else {
|
||||
global env
|
||||
|
||||
if {[info exists env(GIT_DIR)]} {
|
||||
set old_GIT_DIR $env(GIT_DIR)
|
||||
} else {
|
||||
set old_GIT_DIR {}
|
||||
}
|
||||
|
||||
set pwd [pwd]
|
||||
|
||||
if {!$is_submodule} {
|
||||
if {![is_bare]} {
|
||||
cd $_gitworktree
|
||||
}
|
||||
set env(GIT_DIR) [file normalize [gitdir]]
|
||||
} else {
|
||||
cd $current_diff_path
|
||||
if {$revs eq {--}} {
|
||||
@@ -2197,18 +2210,15 @@ proc do_gitk {revs {is_submodule false}} {
|
||||
}
|
||||
set revs $old_sha1...$new_sha1
|
||||
}
|
||||
# GIT_DIR and GIT_WORK_TREE for the submodule are not the ones
|
||||
# we've been using for the main repository, so unset them.
|
||||
# TODO we could make life easier (start up faster?) for gitk
|
||||
# by setting these to the appropriate values to allow gitk
|
||||
# to skip the heuristics to find their proper value
|
||||
unset env(GIT_DIR)
|
||||
unset env(GIT_WORK_TREE)
|
||||
if {[info exists env(GIT_DIR)]} {
|
||||
unset env(GIT_DIR)
|
||||
}
|
||||
}
|
||||
eval exec $cmd $revs "--" "--" &
|
||||
|
||||
set env(GIT_DIR) $_gitdir
|
||||
set env(GIT_WORK_TREE) $_gitworktree
|
||||
if {$old_GIT_DIR ne {}} {
|
||||
set env(GIT_DIR) $old_GIT_DIR
|
||||
}
|
||||
cd $pwd
|
||||
|
||||
ui_status $::starting_gitk_msg
|
||||
@@ -2229,20 +2239,22 @@ proc do_git_gui {} {
|
||||
error_popup [mc "Couldn't find git gui in PATH"]
|
||||
} else {
|
||||
global env
|
||||
global _gitdir _gitworktree
|
||||
|
||||
# see note in do_gitk about unsetting these vars when
|
||||
# running tools in a submodule
|
||||
unset env(GIT_DIR)
|
||||
unset env(GIT_WORK_TREE)
|
||||
if {[info exists env(GIT_DIR)]} {
|
||||
set old_GIT_DIR $env(GIT_DIR)
|
||||
unset env(GIT_DIR)
|
||||
} else {
|
||||
set old_GIT_DIR {}
|
||||
}
|
||||
|
||||
set pwd [pwd]
|
||||
cd $current_diff_path
|
||||
|
||||
eval exec $exe gui &
|
||||
|
||||
set env(GIT_DIR) $_gitdir
|
||||
set env(GIT_WORK_TREE) $_gitworktree
|
||||
if {$old_GIT_DIR ne {}} {
|
||||
set env(GIT_DIR) $old_GIT_DIR
|
||||
}
|
||||
cd $pwd
|
||||
|
||||
ui_status $::starting_gitk_msg
|
||||
|
||||
@@ -197,7 +197,7 @@ method _ls {tree_id {name {}}} {
|
||||
$w conf -state disabled
|
||||
|
||||
set fd [git_read ls-tree -z $tree_id]
|
||||
fconfigure $fd -blocking 0 -translation binary -encoding binary
|
||||
fconfigure $fd -blocking 0 -translation binary -encoding utf-8
|
||||
fileevent $fd readable [cb _read $fd]
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ proc write_update_indexinfo {fd pathList totalCnt batch after} {
|
||||
set info [lindex $s 2]
|
||||
if {$info eq {}} continue
|
||||
|
||||
puts -nonewline $fd "$info\t[encoding convertto $path]\0"
|
||||
puts -nonewline $fd "$info\t[encoding convertto utf-8 $path]\0"
|
||||
display_file $path $new
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ proc write_update_index {fd pathList totalCnt batch after} {
|
||||
?M {set new M_}
|
||||
?? {continue}
|
||||
}
|
||||
puts -nonewline $fd "[encoding convertto $path]\0"
|
||||
puts -nonewline $fd "[encoding convertto utf-8 $path]\0"
|
||||
display_file $path $new
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ proc write_checkout_index {fd pathList totalCnt batch after} {
|
||||
?M -
|
||||
?T -
|
||||
?D {
|
||||
puts -nonewline $fd "[encoding convertto $path]\0"
|
||||
puts -nonewline $fd "[encoding convertto utf-8 $path]\0"
|
||||
display_file $path ?_
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,9 @@ do
|
||||
|
||||
case "$LF$common$LF" in
|
||||
*"$LF$SHA1$LF"*)
|
||||
echo "Already up-to-date with $pretty_name"
|
||||
cat << EOF
|
||||
Already up-to-date with $pretty_name
|
||||
EOF
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
@@ -83,7 +85,9 @@ do
|
||||
# tree as the intermediate result of the merge.
|
||||
# We still need to count this as part of the parent set.
|
||||
|
||||
echo "Fast-forwarding to: $pretty_name"
|
||||
cat << EOF
|
||||
Fast-forwarding to: $pretty_name
|
||||
EOF
|
||||
git read-tree -u -m $head $SHA1 || exit
|
||||
MRC=$SHA1 MRT=$(git write-tree)
|
||||
continue
|
||||
@@ -91,7 +95,9 @@ do
|
||||
|
||||
NON_FF_MERGE=1
|
||||
|
||||
echo "Trying simple merge with $pretty_name"
|
||||
cat << EOF
|
||||
Trying simple merge with $pretty_name
|
||||
EOF
|
||||
git read-tree -u -m --aggressive $common $MRT $SHA1 || exit 2
|
||||
next=$(git write-tree 2>/dev/null)
|
||||
if test $? -ne 0
|
||||
|
||||
@@ -135,6 +135,7 @@ do
|
||||
;;
|
||||
--no-r|--no-re|--no-reb|--no-reba|--no-rebas|--no-rebase)
|
||||
rebase=false
|
||||
|
||||
;;
|
||||
--recurse-submodules)
|
||||
recurse_submodules=--recurse-submodules
|
||||
@@ -175,6 +176,10 @@ do
|
||||
done
|
||||
|
||||
case "$rebase" in
|
||||
i|interactive)
|
||||
rebase=true
|
||||
rebase_args=-i
|
||||
;;
|
||||
preserve)
|
||||
rebase=true
|
||||
rebase_args=--preserve-merges
|
||||
@@ -182,7 +187,7 @@ preserve)
|
||||
true|false|'')
|
||||
;;
|
||||
*)
|
||||
echo "Invalid value for --rebase, should be true, false, or preserve"
|
||||
echo "Invalid value for --rebase, should be true, false, interactive or preserve"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
|
||||
@@ -59,7 +59,9 @@ GIT_QUIET=
|
||||
say () {
|
||||
if test -z "$GIT_QUIET"
|
||||
then
|
||||
printf '%s\n' "$*"
|
||||
cat <<EOF
|
||||
$*
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -434,9 +434,11 @@ cmd_add()
|
||||
|
||||
if test -z "$force" && ! git add --dry-run --ignore-missing "$sm_path" > /dev/null 2>&1
|
||||
then
|
||||
eval_gettextln "The following path is ignored by one of your .gitignore files:
|
||||
\$sm_path
|
||||
Use -f if you really want to add it." >&2
|
||||
cat >&2 <<EOF
|
||||
The following path is ignored by one of your .gitignore files:
|
||||
$(eval_gettextln $sm_path)
|
||||
Use -f if you really want to add it.
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -404,7 +404,7 @@ proc start_rev_list {view} {
|
||||
if {$revs eq {}} {
|
||||
return 0
|
||||
}
|
||||
set args [concat $vflags($view) $revs]
|
||||
set args [limit_arg_length [concat $vflags($view) $revs]]
|
||||
} else {
|
||||
set args $vorigargs($view)
|
||||
}
|
||||
@@ -7557,7 +7557,7 @@ proc gettreeline {gtf id} {
|
||||
if {[string index $fname 0] eq "\""} {
|
||||
set fname [lindex $fname 0]
|
||||
}
|
||||
set fname [encoding convertfrom $fname]
|
||||
set fname [encoding convertfrom utf-8 $fname]
|
||||
lappend treefilelist($id) $fname
|
||||
}
|
||||
if {![eof $gtf]} {
|
||||
@@ -7819,7 +7819,7 @@ proc gettreediffline {gdtf ids} {
|
||||
if {[string index $file 0] eq "\""} {
|
||||
set file [lindex $file 0]
|
||||
}
|
||||
set file [encoding convertfrom $file]
|
||||
set file [encoding convertfrom utf-8 $file]
|
||||
if {$file ne [lindex $treediff end]} {
|
||||
lappend treediff $file
|
||||
lappend sublist $file
|
||||
@@ -7964,7 +7964,7 @@ proc makediffhdr {fname ids} {
|
||||
global ctext curdiffstart treediffs diffencoding
|
||||
global ctext_file_names jump_to_here targetline diffline
|
||||
|
||||
set fname [encoding convertfrom $fname]
|
||||
set fname [encoding convertfrom utf-8 $fname]
|
||||
set diffencoding [get_path_encoding $fname]
|
||||
set i [lsearch -exact $treediffs($ids) $fname]
|
||||
if {$i >= 0} {
|
||||
@@ -8021,7 +8021,7 @@ proc parseblobdiffline {ids line} {
|
||||
|
||||
if {![string compare -length 5 "diff " $line]} {
|
||||
if {![regexp {^diff (--cc|--git) } $line m type]} {
|
||||
set line [encoding convertfrom $line]
|
||||
set line [encoding convertfrom utf-8 $line]
|
||||
$ctext insert end "$line\n" hunksep
|
||||
continue
|
||||
}
|
||||
@@ -8068,7 +8068,7 @@ proc parseblobdiffline {ids line} {
|
||||
makediffhdr $fname $ids
|
||||
|
||||
} elseif {![string compare -length 16 "* Unmerged path " $line]} {
|
||||
set fname [encoding convertfrom [string range $line 16 end]]
|
||||
set fname [encoding convertfrom utf-8 [string range $line 16 end]]
|
||||
$ctext insert end "\n"
|
||||
set curdiffstart [$ctext index "end - 1c"]
|
||||
lappend ctext_file_names $fname
|
||||
@@ -8123,7 +8123,7 @@ proc parseblobdiffline {ids line} {
|
||||
if {[string index $fname 0] eq "\""} {
|
||||
set fname [lindex $fname 0]
|
||||
}
|
||||
set fname [encoding convertfrom $fname]
|
||||
set fname [encoding convertfrom utf-8 $fname]
|
||||
set i [lsearch -exact $treediffs($ids) $fname]
|
||||
if {$i >= 0} {
|
||||
setinlist difffilestart $i $curdiffstart
|
||||
@@ -8142,6 +8142,7 @@ proc parseblobdiffline {ids line} {
|
||||
set diffinhdr 0
|
||||
return
|
||||
}
|
||||
set line [encoding convertfrom utf-8 $line]
|
||||
$ctext insert end "$line\n" filesep
|
||||
|
||||
} else {
|
||||
@@ -9969,7 +9970,8 @@ proc getallcommits {} {
|
||||
}
|
||||
}
|
||||
if {$ids ne {}} {
|
||||
set fd [open [concat $cmd $ids] r]
|
||||
set cmd [limit_arg_length [concat $cmd $ids]]
|
||||
set fd [open $cmd r]
|
||||
fconfigure $fd -blocking 0
|
||||
incr allcommits
|
||||
nowbusy allcommits
|
||||
@@ -9979,6 +9981,21 @@ proc getallcommits {} {
|
||||
}
|
||||
}
|
||||
|
||||
# The maximum command line length for the CreateProcess function is 32767 characters, see
|
||||
# http://blogs.msdn.com/oldnewthing/archive/2003/12/10/56028.aspx
|
||||
# Be a little conservative in case Tcl adds some more stuff to the command line we do not
|
||||
# know about and truncate the command line at a SHA1-boundary below 32000 characters.
|
||||
proc limit_arg_length {cmd} {
|
||||
if {[tk windowingsystem] == "win32" &&
|
||||
[string length $cmd] > 32000} {
|
||||
set ndx [string last " " $cmd 32000]
|
||||
if {$ndx != -1} {
|
||||
return [string range $cmd 0 $ndx]
|
||||
}
|
||||
}
|
||||
return $cmd
|
||||
}
|
||||
|
||||
# Since most commits have 1 parent and 1 child, we group strings of
|
||||
# such commits into "arcs" joining branch/merge points (BMPs), which
|
||||
# are commits that either don't have 1 parent or don't have 1 child.
|
||||
@@ -11952,7 +11969,7 @@ proc cache_gitattr {attr pathlist} {
|
||||
foreach row [split $rlist "\n"] {
|
||||
if {[regexp "(.*): $attr: (.*)" $row m path value]} {
|
||||
if {[string index $path 0] eq "\""} {
|
||||
set path [encoding convertfrom [lindex $path 0]]
|
||||
set path [encoding convertfrom utf-8 [lindex $path 0]]
|
||||
}
|
||||
set path_attr_cache($attr,$path) $value
|
||||
}
|
||||
@@ -12135,7 +12152,6 @@ if { [info exists ::env(GITK_MSGSDIR)] } {
|
||||
set gitk_prefix [file dirname [file dirname [file normalize $argv0]]]
|
||||
set gitk_libdir [file join $gitk_prefix share gitk lib]
|
||||
set gitk_msgsdir [file join $gitk_libdir msgs]
|
||||
unset gitk_prefix
|
||||
}
|
||||
|
||||
## Internationalization (i18n) through msgcat and gettext. See
|
||||
@@ -12314,28 +12330,32 @@ if {[expr {[exec git rev-parse --is-inside-work-tree] == "true"}]} {
|
||||
set worktree [exec git rev-parse --show-toplevel]
|
||||
setcoords
|
||||
makewindow
|
||||
catch {
|
||||
image create photo gitlogo -width 16 -height 16
|
||||
if {$::tcl_platform(platform) eq {windows} && [file exists $gitk_prefix/etc/git.ico]} {
|
||||
wm iconbitmap . -default $gitk_prefix/etc/git.ico
|
||||
} else {
|
||||
catch {
|
||||
image create photo gitlogo -width 16 -height 16
|
||||
|
||||
image create photo gitlogominus -width 4 -height 2
|
||||
gitlogominus put #C00000 -to 0 0 4 2
|
||||
gitlogo copy gitlogominus -to 1 5
|
||||
gitlogo copy gitlogominus -to 6 5
|
||||
gitlogo copy gitlogominus -to 11 5
|
||||
image delete gitlogominus
|
||||
image create photo gitlogominus -width 4 -height 2
|
||||
gitlogominus put #C00000 -to 0 0 4 2
|
||||
gitlogo copy gitlogominus -to 1 5
|
||||
gitlogo copy gitlogominus -to 6 5
|
||||
gitlogo copy gitlogominus -to 11 5
|
||||
image delete gitlogominus
|
||||
|
||||
image create photo gitlogoplus -width 4 -height 4
|
||||
gitlogoplus put #008000 -to 1 0 3 4
|
||||
gitlogoplus put #008000 -to 0 1 4 3
|
||||
gitlogo copy gitlogoplus -to 1 9
|
||||
gitlogo copy gitlogoplus -to 6 9
|
||||
gitlogo copy gitlogoplus -to 11 9
|
||||
image delete gitlogoplus
|
||||
image create photo gitlogoplus -width 4 -height 4
|
||||
gitlogoplus put #008000 -to 1 0 3 4
|
||||
gitlogoplus put #008000 -to 0 1 4 3
|
||||
gitlogo copy gitlogoplus -to 1 9
|
||||
gitlogo copy gitlogoplus -to 6 9
|
||||
gitlogo copy gitlogoplus -to 11 9
|
||||
image delete gitlogoplus
|
||||
|
||||
image create photo gitlogo32 -width 32 -height 32
|
||||
gitlogo32 copy gitlogo -zoom 2 2
|
||||
image create photo gitlogo32 -width 32 -height 32
|
||||
gitlogo32 copy gitlogo -zoom 2 2
|
||||
|
||||
wm iconphoto . -default gitlogo gitlogo32
|
||||
wm iconphoto . -default gitlogo gitlogo32
|
||||
}
|
||||
}
|
||||
# wait for the window to become visible
|
||||
tkwait visibility .
|
||||
|
||||
@@ -4543,6 +4543,29 @@ sub git_print_page_path {
|
||||
print $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name,
|
||||
hash_base=>$hb),
|
||||
-title => $name}, esc_path($basename));
|
||||
if (gitweb_check_feature('highlight')) {
|
||||
print '
|
||||
<a id="lineNoToggle" href="#" onclick="toggleLineNumbers();"></a>
|
||||
<script>
|
||||
function toggleLineNumbers() {
|
||||
e = document.getElementById("lineNoStyle");
|
||||
e2 = document.getElementById("lineNoToggle");
|
||||
if (e2.innerHTML == "[Hide line numbers]") {
|
||||
e.innerHTML = ".linenr { display:none; }";
|
||||
e2.innerHTML = "[Show line numbers]";
|
||||
}
|
||||
else {
|
||||
e.innerHTML = "";
|
||||
e2.innerHTML = "[Hide line numbers]";
|
||||
}
|
||||
}
|
||||
var style = document.createElement("style");
|
||||
style.setAttribute("id", "lineNoStyle");
|
||||
document.getElementsByTagName("head")[0].appendChild(style);
|
||||
toggleLineNumbers();
|
||||
</script>
|
||||
';
|
||||
}
|
||||
} elsif (defined $type && $type eq 'tree') {
|
||||
print $cgi->a({-href => href(action=>"tree", file_name=>$file_name,
|
||||
hash_base=>$hb),
|
||||
@@ -7054,7 +7077,19 @@ sub git_blob {
|
||||
# we can have blame only for text/* mimetype
|
||||
$have_blame &&= ($mimetype =~ m!^text/!);
|
||||
|
||||
my $highlight_js = gitweb_check_feature('syntaxhighlighter_js');
|
||||
if ($highlight_js) {
|
||||
push @stylesheets, $highlight_js->{url} . '/styles/shCore'
|
||||
. $highlight_js->{style} . '.css';
|
||||
push @stylesheets, $highlight_js->{url} . '/styles/shTheme'
|
||||
. $highlight_js->{theme} . '.css';
|
||||
}
|
||||
|
||||
my $highlight = gitweb_check_feature('highlight');
|
||||
if ($highlight_js && $highlight) {
|
||||
die_error(500, 'The highlight and syntaxhighlighter_js are'
|
||||
. 'mutually exclusive');
|
||||
}
|
||||
my $syntax = guess_file_syntax($highlight, $mimetype, $file_name);
|
||||
$fd = run_highlighter($fd, $highlight, $syntax)
|
||||
if $syntax;
|
||||
@@ -7102,6 +7137,72 @@ sub git_blob {
|
||||
href(action=>"blob_plain", hash=>$hash,
|
||||
hash_base=>$hash_base, file_name=>$file_name) .
|
||||
qq!" />\n!;
|
||||
} elsif ($highlight_js) {
|
||||
my $ext = $file_name;
|
||||
$ext =~ s/.*\.//;
|
||||
print qq!<pre class="brush:!.$ext.qq!">!;
|
||||
while (my $line = <$fd>) {
|
||||
$line =~ s!&!\&!g;
|
||||
$line =~ s!<!\<!g;
|
||||
print $line;
|
||||
}
|
||||
print qq!</pre>!;
|
||||
foreach my $name ('Core', 'Autoloader') {
|
||||
print qq!<script src="!.$highlight_js->{url}
|
||||
.qq!/scripts/sh!.$name
|
||||
.qq!.js" type="text/javascript"></script>!;
|
||||
}
|
||||
print qq!<script type="text/javascript">!;
|
||||
print qq!SyntaxHighlighter.defaults["pad-line-numbers"] = 3;!;
|
||||
print qq!SyntaxHighlighter.defaults["toolbar"] = false;!;
|
||||
# for XHTML compliance
|
||||
print qq!SyntaxHighlighter.config["space"] = ' ';!;
|
||||
print qq!SyntaxHighlighter.autoloader(!;
|
||||
my $brush_prefix = $highlight_js->{url} . '/scripts/shBrush';
|
||||
foreach my $language ('applescript AppleScript',
|
||||
'actionscript3 as3 AS3',
|
||||
'bash shell Bash',
|
||||
'clj Clojure',
|
||||
'coldfusion cf ColdFusion',
|
||||
'cpp c Cpp',
|
||||
'c# c-sharp csharp CSharp',
|
||||
'css Css',
|
||||
'delphi pascal Delphi',
|
||||
'diff patch pas Diff',
|
||||
'erl erlang Erlang',
|
||||
'groovy Groovy',
|
||||
'java Java',
|
||||
'jfx javafx JavaFX',
|
||||
'js jscript javascript JScript',
|
||||
'perl pl Perl',
|
||||
'php Php',
|
||||
'text plain Plain',
|
||||
'py python Python',
|
||||
'ruby rails ror rb Ruby',
|
||||
'scala Scala',
|
||||
'scm Scheme',
|
||||
'sql Sql',
|
||||
'vb vbnet Vb',
|
||||
'xml xhtml xslt html Xml') {
|
||||
my $lang = $language;
|
||||
$lang =~ s! (\S+)$! $brush_prefix$1!;
|
||||
print "'".$lang.qq!.js',!;
|
||||
}
|
||||
print qq!''); SyntaxHighlighter.all();!
|
||||
.qq!function scrollTo(number) {!
|
||||
.qq! var elements = document.getElementsByClassName(number);!
|
||||
.qq! if (elements.length == 0) setTimeout('scrollTo("' + number + '");', 50);!
|
||||
.qq! else {!
|
||||
.qq! window.scroll(0, elements[0].offsetTop);!
|
||||
.qq! window.scrollTo(0, elements[0].offsetTop);!
|
||||
.qq! elements[0].style.color = '#ff0000';!
|
||||
.qq! }!
|
||||
.qq!}!
|
||||
.qq!var lineRegex = /#l(\\d+)\$/;!
|
||||
.qq!var lineNumber = lineRegex.exec(document.URL);!
|
||||
.qq!if (lineNumber)!
|
||||
.qq! scrollTo('number' + lineNumber[1]);!
|
||||
.qq!</script>!;
|
||||
} else {
|
||||
my $nr;
|
||||
while (my $line = <$fd>) {
|
||||
|
||||
15
help.c
15
help.c
@@ -107,7 +107,16 @@ static int is_executable(const char *name)
|
||||
return 0;
|
||||
|
||||
#if defined(GIT_WINDOWS_NATIVE)
|
||||
{ /* cannot trust the executable bit, peek into the file instead */
|
||||
/* On Windows we cannot use the executable bit. The executable
|
||||
* state is determined by extension only. We do this first
|
||||
* because with virus scanners opening an executeable for
|
||||
* reading is potentially expensive.
|
||||
*/
|
||||
if (ends_with(name, ".exe"))
|
||||
return S_IXUSR;
|
||||
|
||||
{ /* now that we know it does not have an executable extension,
|
||||
peek into the file instead */
|
||||
char buf[3] = { 0 };
|
||||
int n;
|
||||
int fd = open(name, O_RDONLY);
|
||||
@@ -115,8 +124,8 @@ static int is_executable(const char *name)
|
||||
if (fd >= 0) {
|
||||
n = read(fd, buf, 2);
|
||||
if (n == 2)
|
||||
/* DOS executables start with "MZ" */
|
||||
if (!strcmp(buf, "#!") || !strcmp(buf, "MZ"))
|
||||
/* look for a she-bang */
|
||||
if (!strcmp(buf, "#!"))
|
||||
st.st_mode |= S_IXUSR;
|
||||
close(fd);
|
||||
}
|
||||
|
||||
21
http.c
21
http.c
@@ -7,6 +7,7 @@
|
||||
#include "credential.h"
|
||||
#include "version.h"
|
||||
#include "pkt-line.h"
|
||||
#include "exec_cmd.h"
|
||||
|
||||
int active_requests;
|
||||
int http_is_verbose;
|
||||
@@ -143,6 +144,18 @@ static void process_curl_messages(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int git_config_path(const char **result,
|
||||
const char *var, const char *value)
|
||||
{
|
||||
if (git_config_string(result, var, value))
|
||||
return 1;
|
||||
#ifdef __MINGW32__
|
||||
if (**result == '/')
|
||||
*result = system_path((*result) + 1);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int http_options(const char *var, const char *value, void *cb)
|
||||
{
|
||||
if (!strcmp("http.sslverify", var)) {
|
||||
@@ -150,17 +163,17 @@ static int http_options(const char *var, const char *value, void *cb)
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp("http.sslcert", var))
|
||||
return git_config_string(&ssl_cert, var, value);
|
||||
return git_config_path(&ssl_cert, var, value);
|
||||
#if LIBCURL_VERSION_NUM >= 0x070903
|
||||
if (!strcmp("http.sslkey", var))
|
||||
return git_config_string(&ssl_key, var, value);
|
||||
return git_config_path(&ssl_key, var, value);
|
||||
#endif
|
||||
#if LIBCURL_VERSION_NUM >= 0x070908
|
||||
if (!strcmp("http.sslcapath", var))
|
||||
return git_config_string(&ssl_capath, var, value);
|
||||
return git_config_path(&ssl_capath, var, value);
|
||||
#endif
|
||||
if (!strcmp("http.sslcainfo", var))
|
||||
return git_config_string(&ssl_cainfo, var, value);
|
||||
return git_config_path(&ssl_cainfo, var, value);
|
||||
if (!strcmp("http.sslcertpasswordprotected", var)) {
|
||||
ssl_cert_password_required = git_config_bool(var, value);
|
||||
return 0;
|
||||
|
||||
@@ -84,6 +84,7 @@ static void preload_index(struct index_state *index,
|
||||
offset = 0;
|
||||
work = DIV_ROUND_UP(index->cache_nr, threads);
|
||||
memset(&data, 0, sizeof(data));
|
||||
enable_fscache(1);
|
||||
for (i = 0; i < threads; i++) {
|
||||
struct thread_data *p = data+i;
|
||||
p->index = index;
|
||||
@@ -100,6 +101,7 @@ static void preload_index(struct index_state *index,
|
||||
if (pthread_join(p->pthread, NULL))
|
||||
die("unable to join threaded lstat");
|
||||
}
|
||||
enable_fscache(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
14
send-pack.c
14
send-pack.c
@@ -12,6 +12,16 @@
|
||||
#include "version.h"
|
||||
#include "sha1-array.h"
|
||||
|
||||
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 int feed_object(const unsigned char *sha1, int fd, int negative)
|
||||
{
|
||||
char buf[42];
|
||||
@@ -209,6 +219,8 @@ int send_pack(struct send_pack_args *args,
|
||||
int ret;
|
||||
struct async demux;
|
||||
|
||||
git_config(send_pack_config, NULL);
|
||||
|
||||
/* Does the other end support the reporting? */
|
||||
if (server_supports("report-status"))
|
||||
status_report = 1;
|
||||
@@ -216,7 +228,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;
|
||||
|
||||
@@ -263,27 +263,27 @@ test_expect_success 'blame -L X,-N' '
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L /RE/ (RE to end)' '
|
||||
check_count -L/evil/ C 1 "A U Thor" 1
|
||||
check_count -L/\;*evil/ C 1 "A U Thor" 1
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L /RE/,/RE2/' '
|
||||
check_count -L/robot/,/green/ A 1 B 1 B2 1 D 1 E 1
|
||||
check_count -L/\;*robot/,/\;*green/ A 1 B 1 B2 1 D 1 E 1
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L X,/RE/' '
|
||||
check_count -L5,/evil/ B1 1 D 1 "A U Thor" 1
|
||||
check_count -L5,/\;*evil/ B1 1 D 1 "A U Thor" 1
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L /RE/,Y' '
|
||||
check_count -L/99/,7 B1 1 D 1 "A U Thor" 1
|
||||
check_count -L/\;*99/,7 B1 1 D 1 "A U Thor" 1
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L /RE/,+N' '
|
||||
check_count -L/99/,+3 B1 1 D 1 "A U Thor" 1
|
||||
check_count -L/\;*99/,+3 B1 1 D 1 "A U Thor" 1
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L /RE/,-N' '
|
||||
check_count -L/99/,-3 B 1 B2 1 D 1
|
||||
check_count -L/\;*99/,-3 B 1 B2 1 D 1
|
||||
'
|
||||
|
||||
# 'file' ends with an incomplete line, so 'wc' reports one fewer lines than
|
||||
@@ -349,19 +349,19 @@ test_expect_success 'blame -L multiple (superset/subset: unordered)' '
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L /RE/ (relative)' '
|
||||
check_count -L3,3 -L/fox/ B1 1 B2 1 C 1 D 1 "A U Thor" 1
|
||||
check_count -L3,3 -L/\;*fox/ B1 1 B2 1 C 1 D 1 "A U Thor" 1
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L /RE/ (relative: no preceding range)' '
|
||||
check_count -L/dog/ A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1
|
||||
check_count -L/\;*dog/ A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L /RE/ (relative: adjacent)' '
|
||||
check_count -L1,1 -L/dog/,+1 A 1 E 1
|
||||
check_count -L1,1 -L/\;*dog/,+1 A 1 E 1
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L /RE/ (relative: not found)' '
|
||||
test_must_fail $PROG -L4,4 -L/dog/ file
|
||||
test_must_fail $PROG -L4,4 -L/\;*dog/ file
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L /RE/ (relative: end-of-file)' '
|
||||
@@ -369,11 +369,11 @@ test_expect_success 'blame -L /RE/ (relative: end-of-file)' '
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L ^/RE/ (absolute)' '
|
||||
check_count -L3,3 -L^/dog/,+2 A 1 B2 1
|
||||
check_count -L3,3 -L^/\;*dog/,+2 A 1 B2 1
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L ^/RE/ (absolute: no preceding range)' '
|
||||
check_count -L^/dog/,+2 A 1 B2 1
|
||||
check_count -L^/\;*dog/,+2 A 1 B2 1
|
||||
'
|
||||
|
||||
test_expect_success 'blame -L ^/RE/ (absolute: not found)' '
|
||||
|
||||
@@ -44,6 +44,7 @@ helper_test_clean() {
|
||||
reject $1 https example.com user2
|
||||
reject $1 http path.tld user
|
||||
reject $1 https timeout.tld user
|
||||
reject $1 https sso.tld
|
||||
}
|
||||
|
||||
reject() {
|
||||
@@ -250,6 +251,24 @@ helper_test() {
|
||||
password=pass2
|
||||
EOF
|
||||
'
|
||||
|
||||
test_expect_success "helper ($HELPER) can store empty username" '
|
||||
check approve $HELPER <<-\EOF &&
|
||||
protocol=https
|
||||
host=sso.tld
|
||||
username=
|
||||
password=
|
||||
EOF
|
||||
check fill $HELPER <<-\EOF
|
||||
protocol=https
|
||||
host=sso.tld
|
||||
--
|
||||
protocol=https
|
||||
host=sso.tld
|
||||
username=
|
||||
password=
|
||||
EOF
|
||||
'
|
||||
}
|
||||
|
||||
helper_test_timeout() {
|
||||
|
||||
@@ -23,6 +23,11 @@ then
|
||||
test_done
|
||||
fi
|
||||
|
||||
if ! test_have_prereq PIPE
|
||||
then
|
||||
test_skip_or_die $GIT_TEST_GIT_DAEMON "file system does not support FIFOs"
|
||||
fi
|
||||
|
||||
LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-${this_test#t}}
|
||||
|
||||
GIT_DAEMON_PID=
|
||||
|
||||
@@ -332,4 +332,32 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '
|
||||
test_path_is_dir realgitdir/refs
|
||||
'
|
||||
|
||||
# Tests for the hidden file attribute on windows
|
||||
is_hidden () {
|
||||
test "1" -eq "$(echo puts [file attributes $1 -hidden]|tclsh)"
|
||||
}
|
||||
|
||||
test_expect_success MINGW 'plain hidden' '
|
||||
rm -rf newdir &&
|
||||
(
|
||||
unset GIT_DIR GIT_WORK_TREE
|
||||
mkdir newdir &&
|
||||
cd newdir &&
|
||||
git init &&
|
||||
is_hidden .git
|
||||
) &&
|
||||
check_config newdir/.git false unset
|
||||
'
|
||||
|
||||
test_expect_success MINGW 'plain bare not hidden' '
|
||||
rm -rf newdir
|
||||
(
|
||||
unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
|
||||
mkdir newdir &&
|
||||
cd newdir &&
|
||||
git --bare init
|
||||
) &&
|
||||
! is_hidden newdir
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -5,7 +5,13 @@ test_description=check-ignore
|
||||
. ./test-lib.sh
|
||||
|
||||
init_vars () {
|
||||
global_excludes="$(pwd)/global-excludes"
|
||||
# On Windows, avoid using "C:" in the global-excludes paths.
|
||||
if test_have_prereq MINGW
|
||||
then
|
||||
global_excludes="global-excludes"
|
||||
else
|
||||
global_excludes="$(pwd)/global-excludes"
|
||||
fi
|
||||
}
|
||||
|
||||
enable_global_excludes () {
|
||||
|
||||
@@ -80,4 +80,22 @@ test_expect_success 'autocrlf=true overrides unset eol' '
|
||||
test -z "$onediff" && test -z "$twodiff"
|
||||
'
|
||||
|
||||
test_expect_success NATIVE_CRLF 'eol native is crlf' '
|
||||
|
||||
rm -rf native_eol && mkdir native_eol &&
|
||||
( cd native_eol &&
|
||||
printf "*.txt text\n" > .gitattributes
|
||||
printf "one\r\ntwo\r\nthree\r\n" > filedos.txt
|
||||
printf "one\ntwo\nthree\n" > fileunix.txt
|
||||
git init &&
|
||||
git config core.autocrlf false &&
|
||||
git config core.eol native &&
|
||||
git add filedos.txt fileunix.txt &&
|
||||
git commit -m "first" &&
|
||||
rm file*.txt &&
|
||||
git reset --hard HEAD &&
|
||||
has_cr filedos.txt && has_cr fileunix.txt
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -9,10 +9,10 @@ test_expect_success setup '
|
||||
# clone does not allow us to pass core.bigfilethreshold to
|
||||
# new repos, so set core.bigfilethreshold globally
|
||||
git config --global core.bigfilethreshold 200k &&
|
||||
echo X | dd of=large1 bs=1k seek=2000 &&
|
||||
echo X | dd of=large2 bs=1k seek=2000 &&
|
||||
echo X | dd of=large3 bs=1k seek=2000 &&
|
||||
echo Y | dd of=huge bs=1k seek=2500 &&
|
||||
perl -e "print \"\\0\" x 2048000; print \"X\\n\";" > large1 &&
|
||||
perl -e "print \"\\0\" x 2048000; print \"X\\n\";" > large2 &&
|
||||
perl -e "print \"\\0\" x 2048000; print \"X\\n\";" > large3 &&
|
||||
perl -e "print \"\\0\" x 2560000; print \"Y\\n\";" > huge &&
|
||||
GIT_ALLOC_LIMIT=1500 &&
|
||||
export GIT_ALLOC_LIMIT
|
||||
'
|
||||
|
||||
@@ -339,6 +339,10 @@ test_expect_success 'make_relative_path handles double slashes in GIT_DIR' '
|
||||
git --git-dir="$(pwd)//repo.git" --work-tree="$(pwd)" add dummy_file
|
||||
'
|
||||
|
||||
test_have_prereq MINGW &&
|
||||
# make sure to test DOS path on Windows
|
||||
TRASH_DIRECTORY="$(cd "$TRASH_DIRECTORY" && pwd)"
|
||||
|
||||
test_expect_success 'relative $GIT_WORK_TREE and git subprocesses' '
|
||||
GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work \
|
||||
test-subprocess --setup-work-tree rev-parse --show-toplevel >actual &&
|
||||
|
||||
99
t/t2025-checkout-long-paths.sh
Executable file
99
t/t2025-checkout-long-paths.sh
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='checkout long paths on Windows
|
||||
|
||||
Ensures that Git for Windows can deal with long paths (>260) enabled via core.longpaths'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
if test_have_prereq NOT_MINGW
|
||||
then
|
||||
skip_all='skipping MINGW specific long paths test'
|
||||
test_done
|
||||
fi
|
||||
|
||||
test_expect_success setup '
|
||||
p=longpathxx && # -> 10
|
||||
p=$p$p$p$p$p && # -> 50
|
||||
p=$p$p$p$p$p && # -> 250
|
||||
|
||||
path=${p}/longtestfile && # -> 263 (MAX_PATH = 260)
|
||||
|
||||
blob=$(echo foobar | git hash-object -w --stdin) &&
|
||||
|
||||
printf "100644 %s 0\t%s\n" "$blob" "$path" |
|
||||
git update-index --add --index-info &&
|
||||
git commit -m initial -q
|
||||
'
|
||||
|
||||
test_expect_success 'checkout of long paths without core.longpaths fails' '
|
||||
git config core.longpaths false &&
|
||||
test_must_fail git checkout -f 2>error &&
|
||||
grep -q "Filename too long" error &&
|
||||
test_path_is_missing longpa~1/longtestfile
|
||||
'
|
||||
|
||||
test_expect_success 'checkout of long paths with core.longpaths works' '
|
||||
git config core.longpaths true &&
|
||||
git checkout -f &&
|
||||
test_path_is_file longpa~1/longtestfile
|
||||
'
|
||||
|
||||
test_expect_success 'update of long paths' '
|
||||
echo frotz >> longpa~1/longtestfile &&
|
||||
echo $path > expect &&
|
||||
git ls-files -m > actual &&
|
||||
test_cmp expect actual &&
|
||||
git add $path &&
|
||||
git commit -m second &&
|
||||
git grep "frotz" HEAD -- $path
|
||||
'
|
||||
|
||||
test_expect_success cleanup '
|
||||
# bash cannot delete the trash dir if it contains a long path
|
||||
# lets help cleaning up (unless in debug mode)
|
||||
test ! -z "$debug" || rm -rf longpa~1
|
||||
'
|
||||
|
||||
# check that the template used in the test won't be too long:
|
||||
abspath="$(pwd -W)"/testdir
|
||||
test ${#abspath} -gt 230 ||
|
||||
test_set_prereq SHORTABSPATH
|
||||
|
||||
test_expect_success SHORTABSPATH 'clean up path close to MAX_PATH' '
|
||||
p=/123456789abcdef/123456789abcdef/123456789abcdef/123456789abc/ef &&
|
||||
p=y$p$p$p$p &&
|
||||
subdir="x$(echo "$p" | tail -c $((253 - ${#abspath})))" &&
|
||||
# Now, $abspath/$subdir has exactly 254 characters, and is inside CWD
|
||||
p2="$abspath/$subdir" &&
|
||||
test 254 = ${#p2} &&
|
||||
|
||||
# Be careful to overcome path limitations of the MSys tools and split
|
||||
# the $subdir into two parts. ($subdir2 has to contain 16 chars and a
|
||||
# slash somewhere following; that is why we asked for abspath <= 230 and
|
||||
# why we placed a slash near the end of the $subdir template.)
|
||||
subdir2=${subdir#????????????????*/} &&
|
||||
subdir1=testdir/${subdir%/$subdir2} &&
|
||||
mkdir -p "$subdir1" &&
|
||||
i=0 &&
|
||||
# The most important case is when absolute path is 258 characters long,
|
||||
# and that will be when i == 4.
|
||||
while test $i -le 7
|
||||
do
|
||||
mkdir -p $subdir2 &&
|
||||
touch $subdir2/one-file &&
|
||||
mv ${subdir2%%/*} "$subdir1/" &&
|
||||
subdir2=z${subdir2} &&
|
||||
i=$(($i+1)) ||
|
||||
exit 1
|
||||
done &&
|
||||
|
||||
# now check that git is able to clear the tree:
|
||||
(cd testdir &&
|
||||
git init &&
|
||||
git config core.longpaths yes &&
|
||||
git clean -fdx) &&
|
||||
test ! -d "$subdir1"
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -4,14 +4,14 @@ test_description='ls-tree with(out) globs'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
test_expect_success NOT_MINGW 'setup' '
|
||||
mkdir a aa "a[a]" &&
|
||||
touch a/one aa/two "a[a]/three" &&
|
||||
git add a/one aa/two "a[a]/three" &&
|
||||
git commit -m test
|
||||
'
|
||||
|
||||
test_expect_success 'ls-tree a[a] matches literally' '
|
||||
test_expect_success NOT_MINGW 'ls-tree a* matches literally' '
|
||||
cat >expected <<EOF &&
|
||||
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 a[a]/three
|
||||
EOF
|
||||
|
||||
@@ -14,8 +14,8 @@ create_file() {
|
||||
|
||||
test_expect_success 'setup' '
|
||||
create_file file1 "File1 contents" &&
|
||||
create_file file2 "File2 contents" &&
|
||||
create_file file3 "File3 contents" &&
|
||||
create_file file2 "File2 more contents" &&
|
||||
create_file file3 "File3 even more contents" &&
|
||||
git add file1 file2 file3 &&
|
||||
git commit -m 1
|
||||
'
|
||||
|
||||
@@ -101,7 +101,7 @@ test_expect_success 'push with receive.fsckobjects' '
|
||||
git config transfer.fsckobjects false
|
||||
) &&
|
||||
test_must_fail git push --porcelain dst master:refs/heads/test >act &&
|
||||
test_cmp exp act
|
||||
test_cmp exp act || test ! -s act
|
||||
'
|
||||
|
||||
test_expect_success 'push with transfer.fsckobjects' '
|
||||
@@ -112,7 +112,7 @@ test_expect_success 'push with transfer.fsckobjects' '
|
||||
git config transfer.fsckobjects true
|
||||
) &&
|
||||
test_must_fail git push --porcelain dst master:refs/heads/test >act &&
|
||||
test_cmp exp act
|
||||
test_cmp exp act || test ! -s act
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -1277,4 +1277,40 @@ EOF
|
||||
git push --no-thin --receive-pack="$rcvpck" no-thin/.git refs/heads/master:refs/heads/foo
|
||||
'
|
||||
|
||||
test_expect_success 'receive.denyCurrentBranch = updateInstead' '
|
||||
git push testrepo master &&
|
||||
(cd testrepo &&
|
||||
git reset --hard &&
|
||||
git config receive.denyCurrentBranch updateInstead
|
||||
) &&
|
||||
test_commit third path2 &&
|
||||
git push testrepo master &&
|
||||
test $(git rev-parse HEAD) = $(cd testrepo && git rev-parse HEAD) &&
|
||||
test third = "$(cat testrepo/path2)" &&
|
||||
(cd testrepo &&
|
||||
git update-index --refresh &&
|
||||
git diff-files --quiet &&
|
||||
git diff-index --cached HEAD --
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'receive.denyCurrentBranch = detachInstead' '
|
||||
(cd testrepo &&
|
||||
git reset --hard &&
|
||||
git config receive.denyCurrentBranch detachInstead
|
||||
) &&
|
||||
OLDHEAD=$(cd testrepo && git rev-parse HEAD) &&
|
||||
test_commit fourth path2 &&
|
||||
test fourth = "$(cat path2)" &&
|
||||
git push testrepo master &&
|
||||
test $OLDHEAD = $(cd testrepo && git rev-parse HEAD) &&
|
||||
test fourth != "$(cat testrepo/path2)" &&
|
||||
(cd testrepo &&
|
||||
test_must_fail git symbolic-ref HEAD &&
|
||||
git update-index --refresh &&
|
||||
git diff-files --quiet &&
|
||||
git diff-index --cached HEAD --
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -72,6 +72,10 @@ test_expect_success 'Merge after setting text=auto' '
|
||||
same line
|
||||
EOF
|
||||
|
||||
if test_have_prereq NATIVE_CRLF; then
|
||||
append_cr <expected >expected.temp &&
|
||||
mv expected.temp expected
|
||||
fi &&
|
||||
git config merge.renormalize true &&
|
||||
git rm -fr . &&
|
||||
rm -f .gitattributes &&
|
||||
@@ -86,6 +90,10 @@ test_expect_success 'Merge addition of text=auto' '
|
||||
same line
|
||||
EOF
|
||||
|
||||
if test_have_prereq NATIVE_CRLF; then
|
||||
append_cr <expected >expected.temp &&
|
||||
mv expected.temp expected
|
||||
fi &&
|
||||
git config merge.renormalize true &&
|
||||
git rm -fr . &&
|
||||
rm -f .gitattributes &&
|
||||
@@ -95,16 +103,19 @@ test_expect_success 'Merge addition of text=auto' '
|
||||
'
|
||||
|
||||
test_expect_success 'Detect CRLF/LF conflict after setting text=auto' '
|
||||
q_to_cr <<-\EOF >expected &&
|
||||
<<<<<<<
|
||||
first line
|
||||
same line
|
||||
=======
|
||||
first lineQ
|
||||
same lineQ
|
||||
>>>>>>>
|
||||
EOF
|
||||
|
||||
echo "<<<<<<<" >expected &&
|
||||
if test_have_prereq NATIVE_CRLF; then
|
||||
echo first line | append_cr >>expected &&
|
||||
echo same line | append_cr >>expected &&
|
||||
echo ======= | append_cr >>expected
|
||||
else
|
||||
echo first line >>expected &&
|
||||
echo same line >>expected &&
|
||||
echo ======= >>expected
|
||||
fi &&
|
||||
echo first line | append_cr >>expected &&
|
||||
echo same line | append_cr >>expected &&
|
||||
echo ">>>>>>>" >>expected &&
|
||||
git config merge.renormalize false &&
|
||||
rm -f .gitattributes &&
|
||||
git reset --hard a &&
|
||||
@@ -114,16 +125,19 @@ test_expect_success 'Detect CRLF/LF conflict after setting text=auto' '
|
||||
'
|
||||
|
||||
test_expect_success 'Detect LF/CRLF conflict from addition of text=auto' '
|
||||
q_to_cr <<-\EOF >expected &&
|
||||
<<<<<<<
|
||||
first lineQ
|
||||
same lineQ
|
||||
=======
|
||||
first line
|
||||
same line
|
||||
>>>>>>>
|
||||
EOF
|
||||
|
||||
echo "<<<<<<<" >expected &&
|
||||
echo first line | append_cr >>expected &&
|
||||
echo same line | append_cr >>expected &&
|
||||
if test_have_prereq NATIVE_CRLF; then
|
||||
echo ======= | append_cr >>expected &&
|
||||
echo first line | append_cr >>expected &&
|
||||
echo same line | append_cr >>expected
|
||||
else
|
||||
echo ======= >>expected &&
|
||||
echo first line >>expected &&
|
||||
echo same line >>expected
|
||||
fi &&
|
||||
echo ">>>>>>>" >>expected &&
|
||||
git config merge.renormalize false &&
|
||||
rm -f .gitattributes &&
|
||||
git reset --hard b &&
|
||||
|
||||
@@ -308,7 +308,7 @@ test_expect_success 'git mv moves a submodule with a .git directory and no .gitm
|
||||
(
|
||||
cd sub &&
|
||||
rm -f .git &&
|
||||
cp -R -P -p ../.git/modules/sub .git &&
|
||||
cp -a ../.git/modules/sub .git &&
|
||||
GIT_WORK_TREE=. git config --unset core.worktree
|
||||
) &&
|
||||
mkdir mod &&
|
||||
@@ -331,7 +331,7 @@ test_expect_success 'git mv moves a submodule with a .git directory and .gitmodu
|
||||
(
|
||||
cd sub &&
|
||||
rm -f .git &&
|
||||
cp -R -P -p ../.git/modules/sub .git &&
|
||||
cp -a ../.git/modules/sub .git &&
|
||||
GIT_WORK_TREE=. git config --unset core.worktree
|
||||
) &&
|
||||
mkdir mod &&
|
||||
|
||||
105
t/t7410-submodule-long-path.sh
Executable file
105
t/t7410-submodule-long-path.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2013 Doug Kelly
|
||||
#
|
||||
|
||||
test_description='Test submodules with a path near PATH_MAX
|
||||
|
||||
This test verifies that "git submodule" initialization, update and clones work, including with recursive submodules and paths approaching PATH_MAX (260 characters on Windows)
|
||||
'
|
||||
|
||||
TEST_NO_CREATE_REPO=1
|
||||
. ./test-lib.sh
|
||||
|
||||
# cloning a submodule calls is_git_directory("$path/../.git/modules/$path"),
|
||||
# which effectively limits the maximum length to PATH_MAX / 2 minus some
|
||||
# overhead; start with 3 * 36 = 108 chars (test 2 fails if >= 110)
|
||||
longpath36=0123456789abcdefghijklmnopqrstuvwxyz
|
||||
longpath180=$longpath36$longpath36$longpath36$longpath36$longpath36
|
||||
|
||||
# the git database must fit within PATH_MAX, which limits the submodule name
|
||||
# to PATH_MAX - len(pwd) - ~90 (= len("/objects//") + 40-byte sha1 + some
|
||||
# overhead from the test case)
|
||||
pwd=$(pwd)
|
||||
pwdlen=$(echo "$pwd" | wc -c)
|
||||
longpath=$(echo $longpath180 | cut -c 1-$((170-$pwdlen)))
|
||||
|
||||
test_expect_success 'submodule with a long path' '
|
||||
git init --bare remote &&
|
||||
test_create_repo bundle1 &&
|
||||
(
|
||||
cd bundle1 &&
|
||||
test_commit "shoot" &&
|
||||
git rev-parse --verify HEAD >../expect
|
||||
) &&
|
||||
mkdir home &&
|
||||
(
|
||||
cd home &&
|
||||
git clone ../remote test &&
|
||||
cd test &&
|
||||
git submodule add ../bundle1 $longpath &&
|
||||
test_commit "sogood" &&
|
||||
(
|
||||
cd $longpath &&
|
||||
git rev-parse --verify HEAD >actual &&
|
||||
test_cmp ../../../expect actual
|
||||
) &&
|
||||
git push origin master
|
||||
) &&
|
||||
mkdir home2 &&
|
||||
(
|
||||
cd home2 &&
|
||||
git clone ../remote test &&
|
||||
cd test &&
|
||||
git checkout master &&
|
||||
git submodule update --init &&
|
||||
(
|
||||
cd $longpath &&
|
||||
git rev-parse --verify HEAD >actual &&
|
||||
test_cmp ../../../expect actual
|
||||
)
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'recursive submodule with a long path' '
|
||||
git init --bare super &&
|
||||
test_create_repo child &&
|
||||
(
|
||||
cd child &&
|
||||
test_commit "shoot" &&
|
||||
git rev-parse --verify HEAD >../expect
|
||||
) &&
|
||||
test_create_repo parent &&
|
||||
(
|
||||
cd parent &&
|
||||
git submodule add ../child $longpath &&
|
||||
test_commit "aim"
|
||||
) &&
|
||||
mkdir home3 &&
|
||||
(
|
||||
cd home3 &&
|
||||
git clone ../super test &&
|
||||
cd test &&
|
||||
git submodule add ../parent foo &&
|
||||
git submodule update --init --recursive
|
||||
test_commit "sogood" &&
|
||||
(
|
||||
cd foo/$longpath &&
|
||||
git rev-parse --verify HEAD >actual &&
|
||||
test_cmp ../../../../expect actual
|
||||
) &&
|
||||
git push origin master
|
||||
) &&
|
||||
mkdir home4 &&
|
||||
(
|
||||
cd home4 &&
|
||||
git clone ../super test --recursive &&
|
||||
(
|
||||
cd test/foo/$longpath &&
|
||||
git rev-parse --verify HEAD >actual &&
|
||||
test_cmp ../../../../expect actual
|
||||
)
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -522,4 +522,15 @@ test_expect_success 'delete refspec' '
|
||||
test_cmp expected actual
|
||||
'
|
||||
|
||||
cat > 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
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see http://www.gnu.org/licenses/ .
|
||||
|
||||
# for git on windows so stdin will not be misdetected as attached to a
|
||||
# terminal
|
||||
exec < /dev/null
|
||||
|
||||
# Keep the original TERM for say_color
|
||||
ORIGINAL_TERM=$TERM
|
||||
|
||||
@@ -871,6 +875,7 @@ case $(uname -s) in
|
||||
# exec does not inherit the PID
|
||||
test_set_prereq MINGW
|
||||
test_set_prereq NOT_CYGWIN
|
||||
test_set_prereq NATIVE_CRLF
|
||||
test_set_prereq SED_STRIPS_CR
|
||||
test_set_prereq GREP_STRIPS_CR
|
||||
GIT_TEST_CMP=mingw_test_cmp
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "refs.h"
|
||||
|
||||
static int debug;
|
||||
/* TODO: put somewhere sensible, e.g. git_transport_options? */
|
||||
static int auto_gc = 1;
|
||||
|
||||
struct helper_data {
|
||||
const char *name;
|
||||
@@ -435,10 +437,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)
|
||||
{
|
||||
@@ -474,6 +491,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
|
||||
@@ -505,6 +523,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;
|
||||
}
|
||||
|
||||
@@ -913,6 +937,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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user