mirror of
https://github.com/git-for-windows/git.git
synced 2026-03-18 16:15:22 -05:00
Merge branch 'ready-for-upstream'
This is the branch thicket of patches in Git for Windows that are considered ready for upstream. To keep them in a ready-to-submit shape, they are kept as close to the beginning of the branch thicket as possible.
This commit is contained in:
@@ -429,6 +429,8 @@ include::config/reset.txt[]
|
||||
|
||||
include::config/sendemail.txt[]
|
||||
|
||||
include::config/sendpack.txt[]
|
||||
|
||||
include::config/sequencer.txt[]
|
||||
|
||||
include::config/showbranch.txt[]
|
||||
|
||||
@@ -5,3 +5,8 @@ add.ignore-errors (deprecated)::
|
||||
option of linkgit:git-add[1]. `add.ignore-errors` is deprecated,
|
||||
as it does not follow the usual naming convention for configuration
|
||||
variables.
|
||||
|
||||
add.interactive.useBuiltin::
|
||||
[EXPERIMENTAL] Set to `true` to use the experimental built-in
|
||||
implementation of the interactive version of linkgit:git-add[1]
|
||||
instead of the Perl script version. Is `false` by default.
|
||||
|
||||
@@ -559,6 +559,12 @@ core.unsetenvvars::
|
||||
Defaults to `PERL5LIB` to account for the fact that Git for
|
||||
Windows insists on using its own Perl interpreter.
|
||||
|
||||
core.restrictinheritedhandles::
|
||||
Windows-only: override whether spawned processes inherit only standard
|
||||
file handles (`stdin`, `stdout` and `stderr`) or all handles. Can be
|
||||
`auto`, `true` or `false`. Defaults to `auto`, which means `true` on
|
||||
Windows 7 and later, and `false` on older Windows versions.
|
||||
|
||||
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
|
||||
|
||||
5
Documentation/config/sendpack.txt
Normal file
5
Documentation/config/sendpack.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
sendpack.sideband::
|
||||
Allows to disable the side-band-64k capability for send-pack even
|
||||
when it is advertised by the server. Makes it possible to work
|
||||
around a limitation in the git for windows implementation together
|
||||
with the dump git protocol. Defaults to true.
|
||||
@@ -10,6 +10,7 @@ SYNOPSIS
|
||||
[verse]
|
||||
'git reset' [-q] [<tree-ish>] [--] <paths>...
|
||||
'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
|
||||
EXPERIMENTAL: 'git reset' [-q] [--stdin [-z]] [<tree-ish>]
|
||||
'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
|
||||
|
||||
DESCRIPTION
|
||||
@@ -101,6 +102,15 @@ OPTIONS
|
||||
`reset.quiet` config option. `--quiet` and `--no-quiet` will
|
||||
override the default behavior.
|
||||
|
||||
--stdin::
|
||||
EXPERIMENTAL: Instead of taking list of paths from the
|
||||
command line, read list of paths from the standard input.
|
||||
Paths are separated by LF (i.e. one path per line) by
|
||||
default.
|
||||
|
||||
-z::
|
||||
EXPERIMENTAL: Only meaningful with `--stdin`; paths are
|
||||
separated with NUL character instead of LF.
|
||||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
@@ -16,6 +16,7 @@ SYNOPSIS
|
||||
[--chmod=(+|-)x]
|
||||
[--[no-]assume-unchanged]
|
||||
[--[no-]skip-worktree]
|
||||
[--[no-]ignore-skip-worktree-entries]
|
||||
[--[no-]fsmonitor-valid]
|
||||
[--ignore-submodules]
|
||||
[--[no-]split-index]
|
||||
@@ -113,6 +114,11 @@ you will need to handle the situation manually.
|
||||
set and unset the "skip-worktree" bit for the paths. See
|
||||
section "Skip-worktree bit" below for more information.
|
||||
|
||||
|
||||
--[no-]ignore-skip-worktree-entries::
|
||||
Do not remove skip-worktree (AKA "index-only") entries even when
|
||||
the `--remove` option was specified.
|
||||
|
||||
--[no-]fsmonitor-valid::
|
||||
When one of these flags is specified, the object name recorded
|
||||
for the paths are not updated. Instead, these options
|
||||
|
||||
2
Makefile
2
Makefile
@@ -823,6 +823,8 @@ LIB_H := $(sort $(patsubst ./%,%,$(shell git ls-files '*.h' ':!t/' ':!Documentat
|
||||
-name '*.h' -print)))
|
||||
|
||||
LIB_OBJS += abspath.o
|
||||
LIB_OBJS += add-interactive.o
|
||||
LIB_OBJS += add-patch.o
|
||||
LIB_OBJS += advice.o
|
||||
LIB_OBJS += alias.o
|
||||
LIB_OBJS += alloc.o
|
||||
|
||||
1160
add-interactive.c
Normal file
1160
add-interactive.c
Normal file
File diff suppressed because it is too large
Load Diff
53
add-interactive.h
Normal file
53
add-interactive.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef ADD_INTERACTIVE_H
|
||||
#define ADD_INTERACTIVE_H
|
||||
|
||||
#include "color.h"
|
||||
|
||||
struct add_i_state {
|
||||
struct repository *r;
|
||||
int use_color;
|
||||
char header_color[COLOR_MAXLEN];
|
||||
char help_color[COLOR_MAXLEN];
|
||||
char prompt_color[COLOR_MAXLEN];
|
||||
char error_color[COLOR_MAXLEN];
|
||||
char reset_color[COLOR_MAXLEN];
|
||||
char fraginfo_color[COLOR_MAXLEN];
|
||||
char context_color[COLOR_MAXLEN];
|
||||
char file_old_color[COLOR_MAXLEN];
|
||||
char file_new_color[COLOR_MAXLEN];
|
||||
|
||||
int use_single_key;
|
||||
char *interactive_diff_filter, *interactive_diff_algorithm;
|
||||
};
|
||||
|
||||
void init_add_i_state(struct add_i_state *s, struct repository *r);
|
||||
void clear_add_i_state(struct add_i_state *s);
|
||||
|
||||
enum color_add_i {
|
||||
COLOR_HEADER = 0,
|
||||
COLOR_HELP,
|
||||
COLOR_PROMPT,
|
||||
COLOR_ERROR,
|
||||
COLOR_RESET,
|
||||
};
|
||||
const char *get_add_i_color(enum color_add_i ix);
|
||||
const char *get_interactive_diff_filter(void);
|
||||
const char *get_interactive_diff_algorithm(void);
|
||||
int get_interactive_use_single_key(void);
|
||||
|
||||
struct repository;
|
||||
struct pathspec;
|
||||
int run_add_i(struct repository *r, const struct pathspec *ps);
|
||||
|
||||
enum add_p_mode {
|
||||
ADD_P_STAGE,
|
||||
ADD_P_STASH,
|
||||
ADD_P_RESET,
|
||||
ADD_P_CHECKOUT,
|
||||
ADD_P_WORKTREE,
|
||||
};
|
||||
|
||||
int run_add_p(struct repository *r, enum add_p_mode mode,
|
||||
const char *revision, const struct pathspec *ps);
|
||||
|
||||
#endif
|
||||
1656
add-patch.c
Normal file
1656
add-patch.c
Normal file
File diff suppressed because it is too large
Load Diff
10
apply.c
10
apply.c
@@ -2661,6 +2661,16 @@ static int find_pos(struct apply_state *state,
|
||||
unsigned long backwards, forwards, current;
|
||||
int backwards_lno, forwards_lno, current_lno;
|
||||
|
||||
/*
|
||||
* When running with --allow-overlap, it is possible that a hunk is
|
||||
* seen that pretends to start at the beginning (but no longer does),
|
||||
* and that *still* needs to match the end. So trust `match_end` more
|
||||
* than `match_beginning`.
|
||||
*/
|
||||
if (state->allow_overlap && match_beginning && match_end &&
|
||||
img->nr - preimage->nr != 0)
|
||||
match_beginning = 0;
|
||||
|
||||
/*
|
||||
* If match_beginning or match_end is specified, there is no
|
||||
* point starting from a wrong line that will never match and
|
||||
|
||||
@@ -17,6 +17,8 @@ static unsigned long offset;
|
||||
|
||||
static int tar_umask = 002;
|
||||
|
||||
static gzFile gzip;
|
||||
|
||||
static int write_tar_filter_archive(const struct archiver *ar,
|
||||
struct archiver_args *args);
|
||||
|
||||
@@ -38,11 +40,21 @@ static int write_tar_filter_archive(const struct archiver *ar,
|
||||
#define USTAR_MAX_MTIME 077777777777ULL
|
||||
#endif
|
||||
|
||||
/* writes out the whole block, or dies if fails */
|
||||
static void write_block_or_die(const char *block) {
|
||||
if (gzip) {
|
||||
if (gzwrite(gzip, block, (unsigned) BLOCKSIZE) != BLOCKSIZE)
|
||||
die(_("gzwrite failed"));
|
||||
} else {
|
||||
write_or_die(1, block, BLOCKSIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/* writes out the whole block, but only if it is full */
|
||||
static void write_if_needed(void)
|
||||
{
|
||||
if (offset == BLOCKSIZE) {
|
||||
write_or_die(1, block, BLOCKSIZE);
|
||||
write_block_or_die(block);
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
@@ -66,7 +78,7 @@ static void do_write_blocked(const void *data, unsigned long size)
|
||||
write_if_needed();
|
||||
}
|
||||
while (size >= BLOCKSIZE) {
|
||||
write_or_die(1, buf, BLOCKSIZE);
|
||||
write_block_or_die(buf);
|
||||
size -= BLOCKSIZE;
|
||||
buf += BLOCKSIZE;
|
||||
}
|
||||
@@ -101,10 +113,10 @@ static void write_trailer(void)
|
||||
{
|
||||
int tail = BLOCKSIZE - offset;
|
||||
memset(block + offset, 0, tail);
|
||||
write_or_die(1, block, BLOCKSIZE);
|
||||
write_block_or_die(block);
|
||||
if (tail < 2 * RECORDSIZE) {
|
||||
memset(block, 0, offset);
|
||||
write_or_die(1, block, BLOCKSIZE);
|
||||
write_block_or_die(block);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,18 +473,34 @@ static int write_tar_filter_archive(const struct archiver *ar,
|
||||
filter.use_shell = 1;
|
||||
filter.in = -1;
|
||||
|
||||
if (start_command(&filter) < 0)
|
||||
die_errno(_("unable to start '%s' filter"), argv[0]);
|
||||
close(1);
|
||||
if (dup2(filter.in, 1) < 0)
|
||||
die_errno(_("unable to redirect descriptor"));
|
||||
close(filter.in);
|
||||
if (!strcmp("gzip -cn", ar->data)) {
|
||||
char outmode[4] = "wb\0";
|
||||
|
||||
if (args->compression_level >= 0 && args->compression_level <= 9)
|
||||
outmode[2] = '0' + args->compression_level;
|
||||
|
||||
gzip = gzdopen(fileno(stdout), outmode);
|
||||
if (!gzip)
|
||||
die(_("Could not gzdopen stdout"));
|
||||
} else {
|
||||
if (start_command(&filter) < 0)
|
||||
die_errno(_("unable to start '%s' filter"), argv[0]);
|
||||
close(1);
|
||||
if (dup2(filter.in, 1) < 0)
|
||||
die_errno(_("unable to redirect descriptor"));
|
||||
close(filter.in);
|
||||
}
|
||||
|
||||
r = write_tar_archive(ar, args);
|
||||
|
||||
close(1);
|
||||
if (finish_command(&filter) != 0)
|
||||
die(_("'%s' filter reported error"), argv[0]);
|
||||
if (gzip) {
|
||||
if (gzclose(gzip) != Z_OK)
|
||||
die(_("gzclose failed"));
|
||||
} else {
|
||||
close(1);
|
||||
if (finish_command(&filter) != 0)
|
||||
die(_("'%s' filter reported error"), argv[0]);
|
||||
}
|
||||
|
||||
strbuf_release(&cmd);
|
||||
return r;
|
||||
|
||||
@@ -20,12 +20,14 @@
|
||||
#include "bulk-checkin.h"
|
||||
#include "argv-array.h"
|
||||
#include "submodule.h"
|
||||
#include "add-interactive.h"
|
||||
|
||||
static const char * const builtin_add_usage[] = {
|
||||
N_("git add [<options>] [--] <pathspec>..."),
|
||||
NULL
|
||||
};
|
||||
static int patch_interactive, add_interactive, edit_interactive;
|
||||
static const char *patch_interactive;
|
||||
static int add_interactive, edit_interactive;
|
||||
static int take_worktree_changes;
|
||||
static int add_renormalize;
|
||||
|
||||
@@ -180,11 +182,40 @@ static void refresh(int verbose, const struct pathspec *pathspec)
|
||||
free(seen);
|
||||
}
|
||||
|
||||
static int add_config(const char *var, const char *value, void *cb);
|
||||
|
||||
int run_add_interactive(const char *revision, const char *patch_mode,
|
||||
const struct pathspec *pathspec)
|
||||
{
|
||||
int status, i;
|
||||
struct argv_array argv = ARGV_ARRAY_INIT;
|
||||
int use_builtin_add_i =
|
||||
git_env_bool("GIT_TEST_ADD_I_USE_BUILTIN", -1);
|
||||
if (use_builtin_add_i < 0)
|
||||
git_config_get_bool("add.interactive.usebuiltin",
|
||||
&use_builtin_add_i);
|
||||
|
||||
if (use_builtin_add_i == 1) {
|
||||
enum add_p_mode mode;
|
||||
|
||||
if (!patch_mode)
|
||||
return !!run_add_i(the_repository, pathspec);
|
||||
|
||||
if (!strcmp(patch_mode, "--patch"))
|
||||
mode = ADD_P_STAGE;
|
||||
else if (!strcmp(patch_mode, "--patch=stash"))
|
||||
mode = ADD_P_STASH;
|
||||
else if (!strcmp(patch_mode, "--patch=reset"))
|
||||
mode = ADD_P_RESET;
|
||||
else if (!strcmp(patch_mode, "--patch=checkout"))
|
||||
mode = ADD_P_CHECKOUT;
|
||||
else if (!strcmp(patch_mode, "--patch=worktree"))
|
||||
mode = ADD_P_WORKTREE;
|
||||
else
|
||||
die("'%s' not supported", patch_mode);
|
||||
|
||||
return !!run_add_p(the_repository, mode, revision, pathspec);
|
||||
}
|
||||
|
||||
argv_array_push(&argv, "add--interactive");
|
||||
if (patch_mode)
|
||||
@@ -201,9 +232,11 @@ int run_add_interactive(const char *revision, const char *patch_mode,
|
||||
return status;
|
||||
}
|
||||
|
||||
int interactive_add(int argc, const char **argv, const char *prefix, int patch)
|
||||
int interactive_add(int argc, const char **argv, const char *prefix,
|
||||
const char *patch_mode)
|
||||
{
|
||||
struct pathspec pathspec;
|
||||
char buffer[64];
|
||||
|
||||
parse_pathspec(&pathspec, 0,
|
||||
PATHSPEC_PREFER_FULL |
|
||||
@@ -211,9 +244,13 @@ int interactive_add(int argc, const char **argv, const char *prefix, int patch)
|
||||
PATHSPEC_PREFIX_ORIGIN,
|
||||
prefix, argv);
|
||||
|
||||
return run_add_interactive(NULL,
|
||||
patch ? "--patch" : NULL,
|
||||
&pathspec);
|
||||
if (patch_mode) {
|
||||
xsnprintf(buffer, sizeof(buffer), "--patch%s%s",
|
||||
*patch_mode ? "=" : "", patch_mode);
|
||||
patch_mode = buffer;
|
||||
}
|
||||
|
||||
return run_add_interactive(NULL, patch_mode, &pathspec);
|
||||
}
|
||||
|
||||
static int edit_patch(int argc, const char **argv, const char *prefix)
|
||||
@@ -291,7 +328,9 @@ static struct option builtin_add_options[] = {
|
||||
OPT__VERBOSE(&verbose, N_("be verbose")),
|
||||
OPT_GROUP(""),
|
||||
OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")),
|
||||
OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")),
|
||||
{ OPTION_STRING, 'p', "patch", &patch_interactive, N_("patch-mode"),
|
||||
N_("select hunks interactively"), PARSE_OPT_OPTARG, NULL,
|
||||
(intptr_t) "" },
|
||||
OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
|
||||
OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0),
|
||||
OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
|
||||
@@ -319,6 +358,7 @@ static int add_config(const char *var, const char *value, void *cb)
|
||||
ignore_add_errors = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return git_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ static const char *msg_remove = N_("Removing %s\n");
|
||||
static const char *msg_would_remove = N_("Would remove %s\n");
|
||||
static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
|
||||
static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
|
||||
#ifndef CAN_UNLINK_MOUNT_POINTS
|
||||
static const char *msg_skip_mount_point = N_("Skipping mount point %s\n");
|
||||
static const char *msg_would_skip_mount_point = N_("Would skip mount point %s\n");
|
||||
#endif
|
||||
static const char *msg_warn_remove_failed = N_("failed to remove %s");
|
||||
static const char *msg_warn_lstat_failed = N_("could not lstat %s\n");
|
||||
|
||||
@@ -170,6 +174,29 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_mount_point(path)) {
|
||||
#ifndef CAN_UNLINK_MOUNT_POINTS
|
||||
if (!quiet) {
|
||||
quote_path_relative(path->buf, prefix, "ed);
|
||||
printf(dry_run ?
|
||||
_(msg_would_skip_mount_point) :
|
||||
_(msg_skip_mount_point), quoted.buf);
|
||||
}
|
||||
*dir_gone = 0;
|
||||
#else
|
||||
if (!dry_run && unlink(path->buf)) {
|
||||
int saved_errno = errno;
|
||||
quote_path_relative(path->buf, prefix, "ed);
|
||||
errno = saved_errno;
|
||||
warning_errno(_(msg_warn_remove_failed), quoted.buf);
|
||||
*dir_gone = 0;
|
||||
ret = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
dir = opendir(path->buf);
|
||||
if (!dir) {
|
||||
/* an empty dir could be removed even if it is unreadble */
|
||||
@@ -580,6 +607,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
|
||||
clean_get_color(CLEAN_COLOR_RESET));
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
if (strbuf_getline_lf(&choice, stdin) != EOF) {
|
||||
strbuf_trim(&choice);
|
||||
} else {
|
||||
@@ -662,6 +690,7 @@ static int filter_by_patterns_cmd(void)
|
||||
clean_print_color(CLEAN_COLOR_PROMPT);
|
||||
printf(_("Input ignore patterns>> "));
|
||||
clean_print_color(CLEAN_COLOR_RESET);
|
||||
fflush(stdout);
|
||||
if (strbuf_getline_lf(&confirm, stdin) != EOF)
|
||||
strbuf_trim(&confirm);
|
||||
else
|
||||
@@ -760,6 +789,7 @@ static int ask_each_cmd(void)
|
||||
qname = quote_path_relative(item->string, NULL, &buf);
|
||||
/* TRANSLATORS: Make sure to keep [y/N] as is */
|
||||
printf(_("Remove %s [y/N]? "), qname);
|
||||
fflush(stdout);
|
||||
if (strbuf_getline_lf(&confirm, stdin) != EOF) {
|
||||
strbuf_trim(&confirm);
|
||||
} else {
|
||||
|
||||
@@ -1198,7 +1198,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 =
|
||||
|
||||
@@ -347,7 +347,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
|
||||
die(_("index file corrupt"));
|
||||
|
||||
if (interactive) {
|
||||
char *old_index_env = NULL;
|
||||
char *old_index_env = NULL, *old_repo_index_file;
|
||||
hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
|
||||
|
||||
refresh_cache_or_die(refresh_flags);
|
||||
@@ -355,12 +355,17 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
|
||||
if (write_locked_index(&the_index, &index_lock, 0))
|
||||
die(_("unable to create temporary index"));
|
||||
|
||||
old_repo_index_file = the_repository->index_file;
|
||||
the_repository->index_file =
|
||||
(char *)get_lock_file_path(&index_lock);
|
||||
old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
|
||||
setenv(INDEX_ENVIRONMENT, get_lock_file_path(&index_lock), 1);
|
||||
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
|
||||
|
||||
if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
|
||||
if (interactive_add(argc, argv, prefix,
|
||||
patch_interactive ? "" : NULL) != 0)
|
||||
die(_("interactive add failed"));
|
||||
|
||||
the_repository->index_file = old_repo_index_file;
|
||||
if (old_index_env && *old_index_env)
|
||||
setenv(INDEX_ENVIRONMENT, old_index_env, 1);
|
||||
else
|
||||
|
||||
@@ -25,12 +25,15 @@
|
||||
#include "cache-tree.h"
|
||||
#include "submodule.h"
|
||||
#include "submodule-config.h"
|
||||
#include "strbuf.h"
|
||||
#include "quote.h"
|
||||
|
||||
#define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
|
||||
|
||||
static const char * const git_reset_usage[] = {
|
||||
N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
|
||||
N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
|
||||
N_("EXPERIMENTAL: git reset [-q] [--stdin [-z]] [<tree-ish>]"),
|
||||
N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
|
||||
NULL
|
||||
};
|
||||
@@ -284,7 +287,9 @@ static int git_reset_config(const char *var, const char *value, void *cb)
|
||||
int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int reset_type = NONE, update_ref_status = 0, quiet = 0;
|
||||
int patch_mode = 0, unborn;
|
||||
int patch_mode = 0, nul_term_line = 0, read_from_stdin = 0, unborn;
|
||||
char **stdin_paths = NULL;
|
||||
int stdin_nr = 0, stdin_alloc = 0;
|
||||
const char *rev;
|
||||
struct object_id oid;
|
||||
struct pathspec pathspec;
|
||||
@@ -306,6 +311,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
|
||||
OPT_BOOL('N', "intent-to-add", &intent_to_add,
|
||||
N_("record only the fact that removed paths will be added later")),
|
||||
OPT_BOOL('z', NULL, &nul_term_line,
|
||||
N_("EXPERIMENTAL: paths are separated with NUL character")),
|
||||
OPT_BOOL(0, "stdin", &read_from_stdin,
|
||||
N_("EXPERIMENTAL: read paths from <stdin>")),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@@ -316,6 +325,42 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
PARSE_OPT_KEEP_DASHDASH);
|
||||
parse_args(&pathspec, argv, prefix, patch_mode, &rev);
|
||||
|
||||
if (read_from_stdin) {
|
||||
strbuf_getline_fn getline_fn = nul_term_line ?
|
||||
strbuf_getline_nul : strbuf_getline_lf;
|
||||
int flags = PATHSPEC_PREFER_FULL;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
struct strbuf unquoted = STRBUF_INIT;
|
||||
|
||||
if (patch_mode)
|
||||
die(_("--stdin is incompatible with --patch"));
|
||||
|
||||
if (pathspec.nr)
|
||||
die(_("--stdin is incompatible with path arguments"));
|
||||
|
||||
while (getline_fn(&buf, stdin) != EOF) {
|
||||
if (!nul_term_line && buf.buf[0] == '"') {
|
||||
strbuf_reset(&unquoted);
|
||||
if (unquote_c_style(&unquoted, buf.buf, NULL))
|
||||
die(_("line is badly quoted"));
|
||||
strbuf_swap(&buf, &unquoted);
|
||||
}
|
||||
ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc);
|
||||
stdin_paths[stdin_nr++] = xstrdup(buf.buf);
|
||||
strbuf_reset(&buf);
|
||||
}
|
||||
strbuf_release(&unquoted);
|
||||
strbuf_release(&buf);
|
||||
|
||||
ALLOC_GROW(stdin_paths, stdin_nr + 1, stdin_alloc);
|
||||
stdin_paths[stdin_nr++] = NULL;
|
||||
flags |= PATHSPEC_LITERAL_PATH;
|
||||
parse_pathspec(&pathspec, 0, flags, prefix,
|
||||
(const char **)stdin_paths);
|
||||
|
||||
} else if (nul_term_line)
|
||||
die(_("-z requires --stdin"));
|
||||
|
||||
unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
|
||||
if (unborn) {
|
||||
/* reset on unborn branch: treat as reset to empty tree */
|
||||
@@ -423,5 +468,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
if (!pathspec.nr)
|
||||
remove_branch_state(the_repository, 0);
|
||||
|
||||
if (stdin_paths) {
|
||||
while (stdin_nr)
|
||||
free(stdin_paths[--stdin_nr]);
|
||||
free(stdin_paths);
|
||||
}
|
||||
|
||||
return update_ref_status;
|
||||
}
|
||||
|
||||
@@ -999,9 +999,9 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
|
||||
{
|
||||
int ret = 0;
|
||||
struct child_process cp_read_tree = CHILD_PROCESS_INIT;
|
||||
struct child_process cp_add_i = CHILD_PROCESS_INIT;
|
||||
struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
|
||||
struct index_state istate = { NULL };
|
||||
char *old_index_env = NULL, *old_repo_index_file;
|
||||
|
||||
remove_path(stash_index_path.buf);
|
||||
|
||||
@@ -1015,16 +1015,19 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
|
||||
}
|
||||
|
||||
/* Find out what the user wants. */
|
||||
cp_add_i.git_cmd = 1;
|
||||
argv_array_pushl(&cp_add_i.args, "add--interactive", "--patch=stash",
|
||||
"--", NULL);
|
||||
add_pathspecs(&cp_add_i.args, ps);
|
||||
argv_array_pushf(&cp_add_i.env_array, "GIT_INDEX_FILE=%s",
|
||||
stash_index_path.buf);
|
||||
if (run_command(&cp_add_i)) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
old_repo_index_file = the_repository->index_file;
|
||||
the_repository->index_file = stash_index_path.buf;
|
||||
old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
|
||||
setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
|
||||
|
||||
ret = run_add_interactive(NULL, "--patch=stash", ps);
|
||||
|
||||
the_repository->index_file = old_repo_index_file;
|
||||
if (old_index_env && *old_index_env)
|
||||
setenv(INDEX_ENVIRONMENT, old_index_env, 1);
|
||||
else
|
||||
unsetenv(INDEX_ENVIRONMENT);
|
||||
FREE_AND_NULL(old_index_env);
|
||||
|
||||
/* State of the working tree. */
|
||||
if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
|
||||
@@ -1034,7 +1037,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
|
||||
}
|
||||
|
||||
cp_diff_tree.git_cmd = 1;
|
||||
argv_array_pushl(&cp_diff_tree.args, "diff-tree", "-p", "HEAD",
|
||||
argv_array_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD",
|
||||
oid_to_hex(&info->w_tree), "--", NULL);
|
||||
if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
|
||||
ret = -1;
|
||||
@@ -1088,8 +1091,9 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
|
||||
}
|
||||
|
||||
cp_upd_index.git_cmd = 1;
|
||||
argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
|
||||
"--remove", "--stdin", NULL);
|
||||
argv_array_pushl(&cp_upd_index.args, "update-index",
|
||||
"--ignore-skip-worktree-entries",
|
||||
"-z", "--add", "--remove", "--stdin", NULL);
|
||||
argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
|
||||
stash_index_path.buf);
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ static int verbose;
|
||||
static int mark_valid_only;
|
||||
static int mark_skip_worktree_only;
|
||||
static int mark_fsmonitor_only;
|
||||
static int ignore_skip_worktree_entries;
|
||||
#define MARK_FLAG 1
|
||||
#define UNMARK_FLAG 2
|
||||
static struct strbuf mtime_dir = STRBUF_INIT;
|
||||
@@ -381,7 +382,8 @@ static int process_path(const char *path, struct stat *st, int stat_errno)
|
||||
* so updating it does not make sense.
|
||||
* On the other hand, removing it from index should work
|
||||
*/
|
||||
if (allow_remove && remove_file_from_cache(path))
|
||||
if (!ignore_skip_worktree_entries && allow_remove &&
|
||||
remove_file_from_cache(path))
|
||||
return error("%s: cannot remove from the index", path);
|
||||
return 0;
|
||||
}
|
||||
@@ -1014,6 +1016,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
|
||||
{OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL,
|
||||
N_("clear skip-worktree bit"),
|
||||
PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
|
||||
OPT_BOOL(0, "ignore-skip-worktree-entries", &ignore_skip_worktree_entries,
|
||||
N_("do not touch index-only entries")),
|
||||
OPT_SET_INT(0, "info-only", &info_only,
|
||||
N_("add to index only; do not add content to object database"), 1),
|
||||
OPT_SET_INT(0, "force-remove", &force_remove,
|
||||
|
||||
1
cache.h
1
cache.h
@@ -1288,6 +1288,7 @@ int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
|
||||
int normalize_path_copy(char *dst, const char *src);
|
||||
int longest_ancestor_length(const char *path, struct string_list *prefixes);
|
||||
char *strip_path_suffix(const char *path, const char *suffix);
|
||||
int is_mount_point_via_stat(struct strbuf *path);
|
||||
int daemon_avoid_alias(const char *path);
|
||||
|
||||
/*
|
||||
|
||||
@@ -20,6 +20,7 @@ linux-gcc)
|
||||
export GIT_TEST_OE_DELTA_SIZE=5
|
||||
export GIT_TEST_COMMIT_GRAPH=1
|
||||
export GIT_TEST_MULTI_PACK_INDEX=1
|
||||
export GIT_TEST_ADD_I_USE_BUILTIN=1
|
||||
make test
|
||||
;;
|
||||
linux-gcc-4.8)
|
||||
|
||||
3
commit.h
3
commit.h
@@ -295,7 +295,8 @@ int delayed_reachability_test(struct shallow_info *si, int c);
|
||||
void prune_shallow(unsigned options);
|
||||
extern struct trace_key trace_shallow;
|
||||
|
||||
int interactive_add(int argc, const char **argv, const char *prefix, int patch);
|
||||
int interactive_add(int argc, const char **argv, const char *prefix,
|
||||
const char *patch_mode);
|
||||
int run_add_interactive(const char *revision, const char *patch_mode,
|
||||
const struct pathspec *pathspec);
|
||||
|
||||
|
||||
227
compat/mingw.c
227
compat/mingw.c
@@ -13,6 +13,19 @@
|
||||
|
||||
static const int delay[] = { 0, 1, 10, 20, 40 };
|
||||
|
||||
void open_in_gdb(void)
|
||||
{
|
||||
static struct child_process cp = CHILD_PROCESS_INIT;
|
||||
extern char *_pgmptr;
|
||||
|
||||
argv_array_pushl(&cp.args, "mintty", "gdb", NULL);
|
||||
argv_array_pushf(&cp.args, "--pid=%d", getpid());
|
||||
cp.clean_on_exit = 1;
|
||||
if (start_command(&cp) < 0)
|
||||
die_errno("Could not start gdb");
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
int err_win_to_posix(DWORD winerr)
|
||||
{
|
||||
int error = ENOSYS;
|
||||
@@ -212,6 +225,7 @@ enum hide_dotfiles_type {
|
||||
HIDE_DOTFILES_DOTGITONLY
|
||||
};
|
||||
|
||||
static int core_restrict_inherited_handles = -1;
|
||||
static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
|
||||
static char *unset_environment_variables;
|
||||
|
||||
@@ -231,6 +245,15 @@ int mingw_core_config(const char *var, const char *value, void *cb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.restrictinheritedhandles")) {
|
||||
if (value && !strcasecmp(value, "auto"))
|
||||
core_restrict_inherited_handles = -1;
|
||||
else
|
||||
core_restrict_inherited_handles =
|
||||
git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -430,8 +453,19 @@ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
|
||||
handle = CreateFileW(wfilename, FILE_APPEND_DATA,
|
||||
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||||
NULL, create, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return errno = err_win_to_posix(GetLastError()), -1;
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
DWORD err = GetLastError();
|
||||
/*
|
||||
* Some network storage solutions (e.g. Isilon) might return
|
||||
* ERROR_INVALID_PARAMETER instead of expected error
|
||||
* ERROR_PATH_NOT_FOUND, which results in a unknow error. If
|
||||
* so, the error is now forced to be an ERROR_PATH_NOT_FOUND
|
||||
* error instead.
|
||||
*/
|
||||
if (err == ERROR_INVALID_PARAMETER)
|
||||
err = ERROR_PATH_NOT_FOUND;
|
||||
return errno = err_win_to_posix(err), -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* No O_APPEND here, because the CRT uses it only to reset the
|
||||
@@ -931,11 +965,19 @@ unsigned int sleep (unsigned int seconds)
|
||||
char *mingw_mktemp(char *template)
|
||||
{
|
||||
wchar_t wtemplate[MAX_PATH];
|
||||
int offset = 0;
|
||||
|
||||
if (xutftowcs_path(wtemplate, template) < 0)
|
||||
return NULL;
|
||||
|
||||
if (is_dir_sep(template[0]) && !is_dir_sep(template[1]) &&
|
||||
iswalpha(wtemplate[0]) && wtemplate[1] == L':') {
|
||||
/* We have an absolute path missing the drive prefix */
|
||||
offset = 2;
|
||||
}
|
||||
if (!_wmktemp(wtemplate))
|
||||
return NULL;
|
||||
if (xwcstoutf(template, wtemplate, strlen(template) + 1) < 0)
|
||||
if (xwcstoutf(template, wtemplate + offset, strlen(template) + 1) < 0)
|
||||
return NULL;
|
||||
return template;
|
||||
}
|
||||
@@ -1398,8 +1440,13 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
|
||||
const char *dir,
|
||||
int prepend_cmd, int fhin, int fhout, int fherr)
|
||||
{
|
||||
STARTUPINFOW si;
|
||||
static int restrict_handle_inheritance = -1;
|
||||
STARTUPINFOEXW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
LPPROC_THREAD_ATTRIBUTE_LIST attr_list = NULL;
|
||||
HANDLE stdhandles[3];
|
||||
DWORD stdhandles_count = 0;
|
||||
SIZE_T size;
|
||||
struct strbuf args;
|
||||
wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL;
|
||||
unsigned flags = CREATE_UNICODE_ENVIRONMENT;
|
||||
@@ -1407,6 +1454,17 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
|
||||
HANDLE cons;
|
||||
const char *(*quote_arg)(const char *arg) =
|
||||
is_msys2_sh(*argv) ? quote_arg_msys2 : quote_arg_msvc;
|
||||
const char *strace_env;
|
||||
|
||||
if (restrict_handle_inheritance < 0)
|
||||
restrict_handle_inheritance = core_restrict_inherited_handles;
|
||||
/*
|
||||
* The following code to restrict which handles are inherited seems
|
||||
* to work properly only on Windows 7 and later, so let's disable it
|
||||
* on Windows Vista and 2008.
|
||||
*/
|
||||
if (restrict_handle_inheritance < 0)
|
||||
restrict_handle_inheritance = GetVersion() >> 16 >= 7601;
|
||||
|
||||
do_unset_environment_variables();
|
||||
|
||||
@@ -1435,11 +1493,23 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
|
||||
CloseHandle(cons);
|
||||
}
|
||||
memset(&si, 0, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
si.hStdInput = winansi_get_osfhandle(fhin);
|
||||
si.hStdOutput = winansi_get_osfhandle(fhout);
|
||||
si.hStdError = winansi_get_osfhandle(fherr);
|
||||
si.StartupInfo.cb = sizeof(si);
|
||||
si.StartupInfo.hStdInput = winansi_get_osfhandle(fhin);
|
||||
si.StartupInfo.hStdOutput = winansi_get_osfhandle(fhout);
|
||||
si.StartupInfo.hStdError = winansi_get_osfhandle(fherr);
|
||||
|
||||
/* The list of handles cannot contain duplicates */
|
||||
if (si.StartupInfo.hStdInput != INVALID_HANDLE_VALUE)
|
||||
stdhandles[stdhandles_count++] = si.StartupInfo.hStdInput;
|
||||
if (si.StartupInfo.hStdOutput != INVALID_HANDLE_VALUE &&
|
||||
si.StartupInfo.hStdOutput != si.StartupInfo.hStdInput)
|
||||
stdhandles[stdhandles_count++] = si.StartupInfo.hStdOutput;
|
||||
if (si.StartupInfo.hStdError != INVALID_HANDLE_VALUE &&
|
||||
si.StartupInfo.hStdError != si.StartupInfo.hStdInput &&
|
||||
si.StartupInfo.hStdError != si.StartupInfo.hStdOutput)
|
||||
stdhandles[stdhandles_count++] = si.StartupInfo.hStdError;
|
||||
if (stdhandles_count)
|
||||
si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
|
||||
|
||||
if (*argv && !strcmp(cmd, *argv))
|
||||
wcmd[0] = L'\0';
|
||||
@@ -1465,6 +1535,31 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
|
||||
free(quoted);
|
||||
}
|
||||
|
||||
strace_env = getenv("GIT_STRACE_COMMANDS");
|
||||
if (strace_env) {
|
||||
char *p = path_lookup("strace.exe", 1);
|
||||
if (!p)
|
||||
return error("strace not found!");
|
||||
if (xutftowcs_path(wcmd, p) < 0) {
|
||||
free(p);
|
||||
return -1;
|
||||
}
|
||||
free(p);
|
||||
if (!strcmp("1", strace_env) ||
|
||||
!strcasecmp("yes", strace_env) ||
|
||||
!strcasecmp("true", strace_env))
|
||||
strbuf_insert(&args, 0, "strace ", 7);
|
||||
else {
|
||||
const char *quoted = quote_arg(strace_env);
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
strbuf_addf(&buf, "strace -o %s ", quoted);
|
||||
if (quoted != strace_env)
|
||||
free((char *)quoted);
|
||||
strbuf_insert(&args, 0, buf.buf, buf.len);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
ALLOC_ARRAY(wargs, st_add(st_mult(2, args.len), 1));
|
||||
xutftowcs(wargs, args.buf, 2 * args.len + 1);
|
||||
strbuf_release(&args);
|
||||
@@ -1472,16 +1567,97 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
|
||||
wenvblk = make_environment_block(deltaenv);
|
||||
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL, TRUE,
|
||||
flags, wenvblk, dir ? wdir : NULL, &si, &pi);
|
||||
if (restrict_handle_inheritance && stdhandles_count &&
|
||||
(InitializeProcThreadAttributeList(NULL, 1, 0, &size) ||
|
||||
GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
|
||||
(attr_list = (LPPROC_THREAD_ATTRIBUTE_LIST)
|
||||
(HeapAlloc(GetProcessHeap(), 0, size))) &&
|
||||
InitializeProcThreadAttributeList(attr_list, 1, 0, &size) &&
|
||||
UpdateProcThreadAttribute(attr_list, 0,
|
||||
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
|
||||
stdhandles,
|
||||
stdhandles_count * sizeof(HANDLE),
|
||||
NULL, NULL)) {
|
||||
si.lpAttributeList = attr_list;
|
||||
flags |= EXTENDED_STARTUPINFO_PRESENT;
|
||||
}
|
||||
|
||||
ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL,
|
||||
stdhandles_count ? TRUE : FALSE,
|
||||
flags, wenvblk, dir ? wdir : NULL,
|
||||
&si.StartupInfo, &pi);
|
||||
|
||||
/*
|
||||
* On Windows 2008 R2, it seems that specifying certain types of handles
|
||||
* (such as FILE_TYPE_CHAR or FILE_TYPE_PIPE) will always produce an
|
||||
* error. Rather than playing finicky and fragile games, let's just try
|
||||
* to detect this situation and simply try again without restricting any
|
||||
* handle inheritance. This is still better than failing to create
|
||||
* processes.
|
||||
*/
|
||||
if (!ret && restrict_handle_inheritance && stdhandles_count) {
|
||||
DWORD err = GetLastError();
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
if (err != ERROR_NO_SYSTEM_RESOURCES &&
|
||||
/*
|
||||
* On Windows 7 and earlier, handles on pipes and character
|
||||
* devices are inherited automatically, and cannot be
|
||||
* specified in the thread handle list. Rather than trying
|
||||
* to catch each and every corner case (and running the
|
||||
* chance of *still* forgetting a few), let's just fall
|
||||
* back to creating the process without trying to limit the
|
||||
* handle inheritance.
|
||||
*/
|
||||
!(err == ERROR_INVALID_PARAMETER &&
|
||||
GetVersion() >> 16 < 9200) &&
|
||||
!getenv("SUPPRESS_HANDLE_INHERITANCE_WARNING")) {
|
||||
DWORD fl = 0;
|
||||
int i;
|
||||
|
||||
setenv("SUPPRESS_HANDLE_INHERITANCE_WARNING", "1", 1);
|
||||
|
||||
for (i = 0; i < stdhandles_count; i++) {
|
||||
HANDLE h = stdhandles[i];
|
||||
strbuf_addf(&buf, "handle #%d: %p (type %lx, "
|
||||
"handle info (%d) %lx\n", i, h,
|
||||
GetFileType(h),
|
||||
GetHandleInformation(h, &fl),
|
||||
fl);
|
||||
}
|
||||
strbuf_addstr(&buf, "\nThis is a bug; please report it "
|
||||
"at\nhttps://github.com/git-for-windows/"
|
||||
"git/issues/new\n\n"
|
||||
"To suppress this warning, please set "
|
||||
"the environment variable\n\n"
|
||||
"\tSUPPRESS_HANDLE_INHERITANCE_WARNING=1"
|
||||
"\n");
|
||||
}
|
||||
restrict_handle_inheritance = 0;
|
||||
flags &= ~EXTENDED_STARTUPINFO_PRESENT;
|
||||
ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL,
|
||||
TRUE, flags, wenvblk, dir ? wdir : NULL,
|
||||
&si.StartupInfo, &pi);
|
||||
if (ret && buf.len) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
warning("failed to restrict file handles (%ld)\n\n%s",
|
||||
err, buf.buf);
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
} else if (!ret)
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
|
||||
if (si.lpAttributeList)
|
||||
DeleteProcThreadAttributeList(si.lpAttributeList);
|
||||
if (attr_list)
|
||||
HeapFree(GetProcessHeap(), 0, attr_list);
|
||||
|
||||
free(wenvblk);
|
||||
free(wargs);
|
||||
|
||||
if (!ret) {
|
||||
errno = ENOENT;
|
||||
if (!ret)
|
||||
return -1;
|
||||
}
|
||||
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
/*
|
||||
@@ -2229,6 +2405,28 @@ pid_t waitpid(pid_t pid, int *status, int options)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mingw_is_mount_point(struct strbuf *path)
|
||||
{
|
||||
WIN32_FIND_DATAW findbuf = { 0 };
|
||||
HANDLE handle;
|
||||
wchar_t wfilename[MAX_PATH];
|
||||
int wlen = xutftowcs_path(wfilename, path->buf);
|
||||
if (wlen < 0)
|
||||
die(_("could not get long path for '%s'"), path->buf);
|
||||
|
||||
/* remove trailing slash, if any */
|
||||
if (wlen > 0 && wfilename[wlen - 1] == L'/')
|
||||
wfilename[--wlen] = L'\0';
|
||||
|
||||
handle = FindFirstFileW(wfilename, &findbuf);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
FindClose(handle);
|
||||
|
||||
return (findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
|
||||
(findbuf.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT);
|
||||
}
|
||||
|
||||
int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
|
||||
{
|
||||
int upos = 0, wpos = 0;
|
||||
@@ -2485,6 +2683,7 @@ int wmain(int argc, const wchar_t **wargv)
|
||||
#endif
|
||||
|
||||
maybe_redirect_std_handles();
|
||||
fsync_object_files = 1;
|
||||
|
||||
/* determine size of argv and environ conversion buffer */
|
||||
maxlen = wcslen(wargv[0]);
|
||||
|
||||
@@ -442,6 +442,10 @@ static inline void convert_slashes(char *path)
|
||||
if (*path == '\\')
|
||||
*path = '/';
|
||||
}
|
||||
struct strbuf;
|
||||
int mingw_is_mount_point(struct strbuf *path);
|
||||
#define is_mount_point mingw_is_mount_point
|
||||
#define CAN_UNLINK_MOUNT_POINTS 1
|
||||
#define PATH_SEP ';'
|
||||
char *mingw_query_user_email(void);
|
||||
#define query_user_email mingw_query_user_email
|
||||
@@ -577,6 +581,16 @@ extern CRITICAL_SECTION pinfo_cs;
|
||||
int wmain(int argc, const wchar_t **w_argv);
|
||||
int main(int argc, const char **argv);
|
||||
|
||||
/*
|
||||
* For debugging: if a problem occurs, say, in a Git process that is spawned
|
||||
* from another Git process which in turn is spawned from yet another Git
|
||||
* process, it can be quite daunting to figure out what is going on.
|
||||
*
|
||||
* Call this function to open a new MinTTY (this assumes you are in Git for
|
||||
* Windows' SDK) with a GDB that attaches to the current process right away.
|
||||
*/
|
||||
extern void open_in_gdb(void);
|
||||
|
||||
/*
|
||||
* Used by Pthread API implementation for Windows
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include "compat/terminal.h"
|
||||
#include "sigchain.h"
|
||||
#include "strbuf.h"
|
||||
#include "run-command.h"
|
||||
#include "string-list.h"
|
||||
#include "hashmap.h"
|
||||
|
||||
#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
|
||||
|
||||
@@ -32,7 +35,7 @@ static void restore_term(void)
|
||||
term_fd = -1;
|
||||
}
|
||||
|
||||
static int disable_echo(void)
|
||||
static int disable_bits(tcflag_t bits)
|
||||
{
|
||||
struct termios t;
|
||||
|
||||
@@ -43,7 +46,7 @@ static int disable_echo(void)
|
||||
old_term = t;
|
||||
sigchain_push_common(restore_term_on_signal);
|
||||
|
||||
t.c_lflag &= ~ECHO;
|
||||
t.c_lflag &= ~bits;
|
||||
if (!tcsetattr(term_fd, TCSAFLUSH, &t))
|
||||
return 0;
|
||||
|
||||
@@ -53,17 +56,44 @@ error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int disable_echo(void)
|
||||
{
|
||||
return disable_bits(ECHO);
|
||||
}
|
||||
|
||||
static int enable_non_canonical(void)
|
||||
{
|
||||
return disable_bits(ICANON | ECHO);
|
||||
}
|
||||
|
||||
#elif defined(GIT_WINDOWS_NATIVE)
|
||||
|
||||
#define INPUT_PATH "CONIN$"
|
||||
#define OUTPUT_PATH "CONOUT$"
|
||||
#define FORCE_TEXT "t"
|
||||
|
||||
static int use_stty = 1;
|
||||
static struct string_list stty_restore = STRING_LIST_INIT_DUP;
|
||||
static HANDLE hconin = INVALID_HANDLE_VALUE;
|
||||
static DWORD cmode;
|
||||
|
||||
static void restore_term(void)
|
||||
{
|
||||
if (use_stty) {
|
||||
int i;
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
|
||||
if (stty_restore.nr == 0)
|
||||
return;
|
||||
|
||||
argv_array_push(&cp.args, "stty");
|
||||
for (i = 0; i < stty_restore.nr; i++)
|
||||
argv_array_push(&cp.args, stty_restore.items[i].string);
|
||||
run_command(&cp);
|
||||
string_list_clear(&stty_restore, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hconin == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
@@ -72,8 +102,39 @@ static void restore_term(void)
|
||||
hconin = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
static int disable_echo(void)
|
||||
static int disable_bits(DWORD bits)
|
||||
{
|
||||
if (use_stty) {
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
|
||||
argv_array_push(&cp.args, "stty");
|
||||
|
||||
if (bits & ENABLE_LINE_INPUT) {
|
||||
string_list_append(&stty_restore, "icanon");
|
||||
argv_array_push(&cp.args, "-icanon");
|
||||
}
|
||||
|
||||
if (bits & ENABLE_ECHO_INPUT) {
|
||||
string_list_append(&stty_restore, "echo");
|
||||
argv_array_push(&cp.args, "-echo");
|
||||
}
|
||||
|
||||
if (bits & ENABLE_PROCESSED_INPUT) {
|
||||
string_list_append(&stty_restore, "-ignbrk");
|
||||
string_list_append(&stty_restore, "intr");
|
||||
string_list_append(&stty_restore, "^c");
|
||||
argv_array_push(&cp.args, "ignbrk");
|
||||
argv_array_push(&cp.args, "intr");
|
||||
argv_array_push(&cp.args, "");
|
||||
}
|
||||
|
||||
if (run_command(&cp) == 0)
|
||||
return 0;
|
||||
|
||||
/* `stty` could not be executed; access the Console directly */
|
||||
use_stty = 0;
|
||||
}
|
||||
|
||||
hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
@@ -82,7 +143,7 @@ static int disable_echo(void)
|
||||
|
||||
GetConsoleMode(hconin, &cmode);
|
||||
sigchain_push_common(restore_term_on_signal);
|
||||
if (!SetConsoleMode(hconin, cmode & (~ENABLE_ECHO_INPUT))) {
|
||||
if (!SetConsoleMode(hconin, cmode & ~bits)) {
|
||||
CloseHandle(hconin);
|
||||
hconin = INVALID_HANDLE_VALUE;
|
||||
return -1;
|
||||
@@ -91,6 +152,47 @@ static int disable_echo(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int disable_echo(void)
|
||||
{
|
||||
return disable_bits(ENABLE_ECHO_INPUT);
|
||||
}
|
||||
|
||||
static int enable_non_canonical(void)
|
||||
{
|
||||
return disable_bits(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Override `getchar()`, as the default implementation does not use
|
||||
* `ReadFile()`.
|
||||
*
|
||||
* This poses a problem when we want to see whether the standard
|
||||
* input has more characters, as the default of Git for Windows is to start the
|
||||
* Bash in a MinTTY, which uses a named pipe to emulate a pty, in which case
|
||||
* our `poll()` emulation calls `PeekNamedPipe()`, which seems to require
|
||||
* `ReadFile()` to be called first to work properly (it only reports 0
|
||||
* available bytes, otherwise).
|
||||
*
|
||||
* So let's just override `getchar()` with a version backed by `ReadFile()` and
|
||||
* go our merry ways from here.
|
||||
*/
|
||||
static int mingw_getchar(void)
|
||||
{
|
||||
DWORD read = 0;
|
||||
unsigned char ch;
|
||||
|
||||
if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), &ch, 1, &read, NULL))
|
||||
return EOF;
|
||||
|
||||
if (!read) {
|
||||
error("Unexpected 0 read");
|
||||
return EOF;
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
#define getchar mingw_getchar
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef FORCE_TEXT
|
||||
@@ -137,6 +239,126 @@ char *git_terminal_prompt(const char *prompt, int echo)
|
||||
return buf.buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* The `is_known_escape_sequence()` function returns 1 if the passed string
|
||||
* corresponds to an Escape sequence that the terminal capabilities contains.
|
||||
*
|
||||
* To avoid depending on ncurses or other platform-specific libraries, we rely
|
||||
* on the presence of the `infocmp` executable to do the job for us (failing
|
||||
* silently if the program is not available or refused to run).
|
||||
*/
|
||||
struct escape_sequence_entry {
|
||||
struct hashmap_entry entry;
|
||||
char sequence[FLEX_ARRAY];
|
||||
};
|
||||
|
||||
static int sequence_entry_cmp(const void *hashmap_cmp_fn_data,
|
||||
const struct escape_sequence_entry *e1,
|
||||
const struct escape_sequence_entry *e2,
|
||||
const void *keydata)
|
||||
{
|
||||
return strcmp(e1->sequence, keydata ? keydata : e2->sequence);
|
||||
}
|
||||
|
||||
static int is_known_escape_sequence(const char *sequence)
|
||||
{
|
||||
static struct hashmap sequences;
|
||||
static int initialized;
|
||||
|
||||
if (!initialized) {
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
char *p, *eol;
|
||||
|
||||
hashmap_init(&sequences, (hashmap_cmp_fn)sequence_entry_cmp,
|
||||
NULL, 0);
|
||||
|
||||
argv_array_pushl(&cp.args, "infocmp", "-L", "-1", NULL);
|
||||
if (pipe_command(&cp, NULL, 0, &buf, 0, NULL, 0))
|
||||
strbuf_setlen(&buf, 0);
|
||||
|
||||
for (eol = p = buf.buf; *p; p = eol + 1) {
|
||||
p = strchr(p, '=');
|
||||
if (!p)
|
||||
break;
|
||||
p++;
|
||||
eol = strchrnul(p, '\n');
|
||||
|
||||
if (starts_with(p, "\\E")) {
|
||||
char *comma = memchr(p, ',', eol - p);
|
||||
struct escape_sequence_entry *e;
|
||||
|
||||
p[0] = '^';
|
||||
p[1] = '[';
|
||||
FLEX_ALLOC_MEM(e, sequence, p, comma - p);
|
||||
hashmap_entry_init(&e->entry,
|
||||
strhash(e->sequence));
|
||||
hashmap_add(&sequences, &e->entry);
|
||||
}
|
||||
if (!*eol)
|
||||
break;
|
||||
}
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
return !!hashmap_get_from_hash(&sequences, strhash(sequence), sequence);
|
||||
}
|
||||
|
||||
int read_key_without_echo(struct strbuf *buf)
|
||||
{
|
||||
static int warning_displayed;
|
||||
int ch;
|
||||
|
||||
if (warning_displayed || enable_non_canonical() < 0) {
|
||||
if (!warning_displayed) {
|
||||
warning("reading single keystrokes not supported on "
|
||||
"this platform; reading line instead");
|
||||
warning_displayed = 1;
|
||||
}
|
||||
|
||||
return strbuf_getline(buf, stdin);
|
||||
}
|
||||
|
||||
strbuf_reset(buf);
|
||||
ch = getchar();
|
||||
if (ch == EOF) {
|
||||
restore_term();
|
||||
return EOF;
|
||||
}
|
||||
strbuf_addch(buf, ch);
|
||||
|
||||
if (ch == '\033' /* ESC */) {
|
||||
/*
|
||||
* We are most likely looking at an Escape sequence. Let's try
|
||||
* to read more bytes, waiting at most half a second, assuming
|
||||
* that the sequence is complete if we did not receive any byte
|
||||
* within that time.
|
||||
*
|
||||
* Start by replacing the Escape byte with ^[ */
|
||||
strbuf_splice(buf, buf->len - 1, 1, "^[", 2);
|
||||
|
||||
/*
|
||||
* Query the terminal capabilities once about all the Escape
|
||||
* sequences it knows about, so that we can avoid waiting for
|
||||
* half a second when we know that the sequence is complete.
|
||||
*/
|
||||
while (!is_known_escape_sequence(buf->buf)) {
|
||||
struct pollfd pfd = { .fd = 0, .events = POLLIN };
|
||||
|
||||
if (poll(&pfd, 1, 500) < 1)
|
||||
break;
|
||||
|
||||
ch = getchar();
|
||||
if (ch == EOF)
|
||||
return 0;
|
||||
strbuf_addch(buf, ch);
|
||||
}
|
||||
}
|
||||
|
||||
restore_term();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
char *git_terminal_prompt(const char *prompt, int echo)
|
||||
@@ -144,4 +366,23 @@ char *git_terminal_prompt(const char *prompt, int echo)
|
||||
return getpass(prompt);
|
||||
}
|
||||
|
||||
int read_key_without_echo(struct strbuf *buf)
|
||||
{
|
||||
static int warning_displayed;
|
||||
const char *res;
|
||||
|
||||
if (!warning_displayed) {
|
||||
warning("reading single keystrokes not supported on this "
|
||||
"platform; reading line instead");
|
||||
warning_displayed = 1;
|
||||
}
|
||||
|
||||
res = getpass("");
|
||||
strbuf_reset(buf);
|
||||
if (!res)
|
||||
return EOF;
|
||||
strbuf_addstr(buf, res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,4 +3,7 @@
|
||||
|
||||
char *git_terminal_prompt(const char *prompt, int echo);
|
||||
|
||||
/* Read a single keystroke, without echoing it to the terminal */
|
||||
int read_key_without_echo(struct strbuf *buf);
|
||||
|
||||
#endif /* COMPAT_TERMINAL_H */
|
||||
|
||||
@@ -36,6 +36,13 @@ REM ================================================================
|
||||
|
||||
dir vcpkg\vcpkg.exe >nul 2>nul && GOTO :install_libraries
|
||||
|
||||
git.exe version 2>nul
|
||||
IF ERRORLEVEL 1 (
|
||||
echo "***"
|
||||
echo "Git not found. Please adjust your CMD path or Git install option."
|
||||
echo "***"
|
||||
EXIT /B 1 )
|
||||
|
||||
echo Fetching vcpkg in %cwd%vcpkg
|
||||
git.exe clone https://github.com/Microsoft/vcpkg vcpkg
|
||||
IF ERRORLEVEL 1 ( EXIT /B 1 )
|
||||
@@ -73,6 +80,12 @@ REM ================================================================
|
||||
:sub__install_one
|
||||
echo Installing package %1...
|
||||
|
||||
REM vcpkg may not be reliable on slow, intermittent or proxy
|
||||
REM connections, see e.g.
|
||||
REM https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/4a8f7be5-5e15-4213-a7bb-ddf424a954e6/winhttpsendrequest-ends-with-12002-errorhttptimeout-after-21-seconds-no-matter-what-timeout?forum=windowssdk
|
||||
REM which explains the hidden 21 second timeout
|
||||
REM (last post by Dave : Microsoft - Windows Networking team)
|
||||
|
||||
.\vcpkg.exe install %1:%arch%
|
||||
IF ERRORLEVEL 1 ( EXIT /B 1 )
|
||||
|
||||
|
||||
@@ -662,10 +662,20 @@ void winansi_init(void)
|
||||
*/
|
||||
HANDLE winansi_get_osfhandle(int fd)
|
||||
{
|
||||
HANDLE ret;
|
||||
|
||||
if (fd == 1 && (fd_is_interactive[1] & FD_SWAPPED))
|
||||
return hconsole1;
|
||||
if (fd == 2 && (fd_is_interactive[2] & FD_SWAPPED))
|
||||
return hconsole2;
|
||||
|
||||
return (HANDLE)_get_osfhandle(fd);
|
||||
ret = (HANDLE)_get_osfhandle(fd);
|
||||
|
||||
/*
|
||||
* There are obviously circumstances under which _get_osfhandle()
|
||||
* returns (HANDLE)-2. This is not documented anywhere, but that is so
|
||||
* clearly an invalid handle value that we can just work around this
|
||||
* and return the correct value for invalid handles.
|
||||
*/
|
||||
return ret == (HANDLE)-2 ? INVALID_HANDLE_VALUE : ret;
|
||||
}
|
||||
|
||||
6
config.c
6
config.c
@@ -1657,9 +1657,11 @@ static int git_config_from_blob_ref(config_fn_t fn,
|
||||
|
||||
const char *git_etc_gitconfig(void)
|
||||
{
|
||||
static const char *system_wide;
|
||||
if (!system_wide)
|
||||
static char *system_wide;
|
||||
if (!system_wide) {
|
||||
system_wide = system_path(ETC_GITCONFIG);
|
||||
normalize_path_copy(system_wide, system_wide);
|
||||
}
|
||||
return system_wide;
|
||||
}
|
||||
|
||||
|
||||
@@ -424,6 +424,11 @@ ifeq ($(uname_S),Windows)
|
||||
NO_POSIX_GOODIES = UnfortunatelyYes
|
||||
NATIVE_CRLF = YesPlease
|
||||
DEFAULT_HELP_FORMAT = html
|
||||
ifeq (/mingw64,$(subst 32,64,$(prefix)))
|
||||
# Move system config into top-level /etc/
|
||||
ETC_GITCONFIG = ../etc/gitconfig
|
||||
ETC_GITATTRIBUTES = ../etc/gitattributes
|
||||
endif
|
||||
|
||||
CC = compat/vcbuild/scripts/clink.pl
|
||||
AR = compat/vcbuild/scripts/lib.pl
|
||||
@@ -666,10 +671,15 @@ else
|
||||
HAVE_LIBCHARSET_H = YesPlease
|
||||
NO_GETTEXT =
|
||||
USE_GETTEXT_SCHEME = fallthrough
|
||||
USE_LIBPCRE= YesPlease
|
||||
NO_LIBPCRE1_JIT = UnfortunatelyYes
|
||||
USE_LIBPCRE = YesPlease
|
||||
NO_CURL =
|
||||
USE_NED_ALLOCATOR = YesPlease
|
||||
NO_PYTHON =
|
||||
ifeq (/mingw64,$(subst 32,64,$(prefix)))
|
||||
# Move system config into top-level /etc/
|
||||
ETC_GITCONFIG = ../etc/gitconfig
|
||||
ETC_GITATTRIBUTES = ../etc/gitattributes
|
||||
endif
|
||||
else
|
||||
COMPAT_CFLAGS += -D__USE_MINGW_ANSI_STDIO
|
||||
NO_CURL = YesPlease
|
||||
|
||||
39
diff.c
39
diff.c
@@ -2495,22 +2495,6 @@ static void pprint_rename(struct strbuf *name, const char *a, const char *b)
|
||||
}
|
||||
}
|
||||
|
||||
struct diffstat_t {
|
||||
int nr;
|
||||
int alloc;
|
||||
struct diffstat_file {
|
||||
char *from_name;
|
||||
char *name;
|
||||
char *print_name;
|
||||
const char *comments;
|
||||
unsigned is_unmerged:1;
|
||||
unsigned is_binary:1;
|
||||
unsigned is_renamed:1;
|
||||
unsigned is_interesting:1;
|
||||
uintmax_t added, deleted;
|
||||
} **files;
|
||||
};
|
||||
|
||||
static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
|
||||
const char *name_a,
|
||||
const char *name_b)
|
||||
@@ -3157,7 +3141,7 @@ static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *o
|
||||
gather_dirstat(options, &dir, changed, "", 0);
|
||||
}
|
||||
|
||||
static void free_diffstat_info(struct diffstat_t *diffstat)
|
||||
void free_diffstat_info(struct diffstat_t *diffstat)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < diffstat->nr; i++) {
|
||||
@@ -6283,12 +6267,7 @@ void diff_flush(struct diff_options *options)
|
||||
dirstat_by_line) {
|
||||
struct diffstat_t diffstat;
|
||||
|
||||
memset(&diffstat, 0, sizeof(struct diffstat_t));
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filepair *p = q->queue[i];
|
||||
if (check_pair_status(p))
|
||||
diff_flush_stat(p, options, &diffstat);
|
||||
}
|
||||
compute_diffstat(options, &diffstat, q);
|
||||
if (output_format & DIFF_FORMAT_NUMSTAT)
|
||||
show_numstat(&diffstat, options);
|
||||
if (output_format & DIFF_FORMAT_DIFFSTAT)
|
||||
@@ -6621,6 +6600,20 @@ static int is_submodule_ignored(const char *path, struct diff_options *options)
|
||||
return ignored;
|
||||
}
|
||||
|
||||
void compute_diffstat(struct diff_options *options,
|
||||
struct diffstat_t *diffstat,
|
||||
struct diff_queue_struct *q)
|
||||
{
|
||||
int i;
|
||||
|
||||
memset(diffstat, 0, sizeof(struct diffstat_t));
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filepair *p = q->queue[i];
|
||||
if (check_pair_status(p))
|
||||
diff_flush_stat(p, options, diffstat);
|
||||
}
|
||||
}
|
||||
|
||||
void diff_addremove(struct diff_options *options,
|
||||
int addremove, unsigned mode,
|
||||
const struct object_id *oid,
|
||||
|
||||
20
diff.h
20
diff.h
@@ -245,6 +245,22 @@ void diff_emit_submodule_error(struct diff_options *o, const char *err);
|
||||
void diff_emit_submodule_pipethrough(struct diff_options *o,
|
||||
const char *line, int len);
|
||||
|
||||
struct diffstat_t {
|
||||
int nr;
|
||||
int alloc;
|
||||
struct diffstat_file {
|
||||
char *from_name;
|
||||
char *name;
|
||||
char *print_name;
|
||||
const char *comments;
|
||||
unsigned is_unmerged:1;
|
||||
unsigned is_binary:1;
|
||||
unsigned is_renamed:1;
|
||||
unsigned is_interesting:1;
|
||||
uintmax_t added, deleted;
|
||||
} **files;
|
||||
};
|
||||
|
||||
enum color_diff {
|
||||
DIFF_RESET = 0,
|
||||
DIFF_CONTEXT = 1,
|
||||
@@ -334,6 +350,10 @@ void diff_change(struct diff_options *,
|
||||
|
||||
struct diff_filepair *diff_unmerge(struct diff_options *, const char *path);
|
||||
|
||||
void compute_diffstat(struct diff_options *options, struct diffstat_t *diffstat,
|
||||
struct diff_queue_struct *q);
|
||||
void free_diffstat_info(struct diffstat_t *diffstat);
|
||||
|
||||
#define DIFF_SETUP_REVERSE 1
|
||||
#define DIFF_SETUP_USE_SIZE_CACHE 4
|
||||
|
||||
|
||||
@@ -82,6 +82,18 @@ static struct diff_rename_src *register_rename_src(struct diff_filepair *p)
|
||||
|
||||
first = 0;
|
||||
last = rename_src_nr;
|
||||
|
||||
if (last > 0) {
|
||||
struct diff_rename_src *src = &(rename_src[last-1]);
|
||||
int cmp = strcmp(one->path, src->p->one->path);
|
||||
if (!cmp)
|
||||
return src;
|
||||
if (cmp > 0) {
|
||||
first = last;
|
||||
goto append_it;
|
||||
}
|
||||
}
|
||||
|
||||
while (last > first) {
|
||||
int next = first + ((last - first) >> 1);
|
||||
struct diff_rename_src *src = &(rename_src[next]);
|
||||
@@ -95,6 +107,7 @@ static struct diff_rename_src *register_rename_src(struct diff_filepair *p)
|
||||
first = next+1;
|
||||
}
|
||||
|
||||
append_it:
|
||||
/* insert to make it at "first" */
|
||||
ALLOC_GROW(rename_src, rename_src_nr + 1, rename_src_alloc);
|
||||
rename_src_nr++;
|
||||
|
||||
@@ -177,7 +177,9 @@ sub run_cmd_pipe {
|
||||
} else {
|
||||
my $fh = undef;
|
||||
open($fh, '-|', @_) or die;
|
||||
return <$fh>;
|
||||
my @out = <$fh>;
|
||||
close $fh || die "Cannot close @_ ($!)";
|
||||
return @out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +226,7 @@ my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), __('path'))
|
||||
sub get_empty_tree {
|
||||
return $empty_tree if defined $empty_tree;
|
||||
|
||||
$empty_tree = run_cmd_pipe(qw(git hash-object -t tree /dev/null));
|
||||
($empty_tree) = run_cmd_pipe(qw(git hash-object -t tree /dev/null));
|
||||
chomp $empty_tree;
|
||||
return $empty_tree;
|
||||
}
|
||||
@@ -1127,7 +1129,7 @@ aborted and the hunk is left unchanged.
|
||||
EOF2
|
||||
close $fh;
|
||||
|
||||
chomp(my $editor = run_cmd_pipe(qw(git var GIT_EDITOR)));
|
||||
chomp(my ($editor) = run_cmd_pipe(qw(git var GIT_EDITOR)));
|
||||
system('sh', '-c', $editor.' "$@"', $editor, $hunkfile);
|
||||
|
||||
if ($? != 0) {
|
||||
|
||||
@@ -405,6 +405,10 @@ static inline char *git_find_last_dir_sep(const char *path)
|
||||
#define find_last_dir_sep git_find_last_dir_sep
|
||||
#endif
|
||||
|
||||
#ifndef is_mount_point
|
||||
#define is_mount_point is_mount_point_via_stat
|
||||
#endif
|
||||
|
||||
#ifndef query_user_email
|
||||
#define query_user_email() NULL
|
||||
#endif
|
||||
|
||||
@@ -193,7 +193,8 @@ create_stash () {
|
||||
GIT_INDEX_FILE="$TMPindex" &&
|
||||
export GIT_INDEX_FILE &&
|
||||
git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" &&
|
||||
git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
|
||||
git update-index --ignore-skip-worktree-entries \
|
||||
-z --add --remove --stdin <"$TMP-stagenames" &&
|
||||
git write-tree &&
|
||||
rm -f "$TMPindex"
|
||||
) ) ||
|
||||
@@ -206,13 +207,13 @@ create_stash () {
|
||||
|
||||
# find out what the user wants
|
||||
GIT_INDEX_FILE="$TMP-index" \
|
||||
git add--interactive --patch=stash -- "$@" &&
|
||||
git add --patch=stash -- "$@" &&
|
||||
|
||||
# state of the working tree
|
||||
w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
|
||||
die "$(gettext "Cannot save the current worktree state")"
|
||||
|
||||
git diff-tree -p HEAD $w_tree -- >"$TMP-patch" &&
|
||||
git diff-tree -p -U1 HEAD $w_tree -- >"$TMP-patch" &&
|
||||
test -s "$TMP-patch" ||
|
||||
die "$(gettext "No changes selected")"
|
||||
|
||||
|
||||
@@ -354,6 +354,16 @@ proc parseviewrevs {view revs} {
|
||||
return $ret
|
||||
}
|
||||
|
||||
# Escapes a list of filter paths to be passed to git log via stdin. Note that
|
||||
# paths must not be quoted.
|
||||
proc escape_filter_paths {paths} {
|
||||
set escaped [list]
|
||||
foreach path $paths {
|
||||
lappend escaped [string map {\\ \\\\ "\ " "\\\ "} $path]
|
||||
}
|
||||
return $escaped
|
||||
}
|
||||
|
||||
# Start off a git log process and arrange to read its output
|
||||
proc start_rev_list {view} {
|
||||
global startmsecs commitidx viewcomplete curview
|
||||
@@ -406,14 +416,17 @@ proc start_rev_list {view} {
|
||||
if {$revs eq {}} {
|
||||
return 0
|
||||
}
|
||||
set args [concat $vflags($view) $revs]
|
||||
set args $vflags($view)
|
||||
} else {
|
||||
set revs {}
|
||||
set args $vorigargs($view)
|
||||
}
|
||||
|
||||
if {[catch {
|
||||
set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
|
||||
--parents --boundary $args "--" $files] r]
|
||||
--parents --boundary $args --stdin \
|
||||
"<<[join [concat $revs "--" \
|
||||
[escape_filter_paths $files]] "\\n"]"] r]
|
||||
} err]} {
|
||||
error_popup "[mc "Error executing git log:"] $err"
|
||||
return 0
|
||||
@@ -555,13 +568,20 @@ proc updatecommits {} {
|
||||
set revs $newrevs
|
||||
set vposids($view) [lsort -unique [concat $oldpos $vposids($view)]]
|
||||
}
|
||||
set args [concat $vflags($view) $revs --not $oldpos]
|
||||
set args $vflags($view)
|
||||
foreach r $oldpos {
|
||||
lappend revs "^$r"
|
||||
}
|
||||
} else {
|
||||
set revs {}
|
||||
set args $vorigargs($view)
|
||||
}
|
||||
if {[catch {
|
||||
set fd [open [concat | git log --no-color -z --pretty=raw $show_notes \
|
||||
--parents --boundary $args "--" $vfilelimit($view)] r]
|
||||
--parents --boundary $args --stdin \
|
||||
"<<[join [concat $revs "--" \
|
||||
[escape_filter_paths
|
||||
$vfilelimit($view)]] "\\n"]"] r]
|
||||
} err]} {
|
||||
error_popup "[mc "Error executing git log:"] $err"
|
||||
return
|
||||
@@ -10210,10 +10230,16 @@ proc getallcommits {} {
|
||||
foreach id $seeds {
|
||||
lappend ids "^$id"
|
||||
}
|
||||
lappend ids "--"
|
||||
}
|
||||
}
|
||||
if {$ids ne {}} {
|
||||
set fd [open [concat $cmd $ids] r]
|
||||
if {$ids eq "--all"} {
|
||||
set cmd [concat $cmd "--all"]
|
||||
} else {
|
||||
set cmd [concat $cmd --stdin "<<[join $ids "\\n"]"]
|
||||
}
|
||||
set fd [open $cmd r]
|
||||
fconfigure $fd -blocking 0
|
||||
incr allcommits
|
||||
nowbusy allcommits
|
||||
|
||||
44
path.c
44
path.c
@@ -11,6 +11,7 @@
|
||||
#include "path.h"
|
||||
#include "packfile.h"
|
||||
#include "object-store.h"
|
||||
#include "exec-cmd.h"
|
||||
|
||||
static int get_st_mode_bits(const char *path, int *mode)
|
||||
{
|
||||
@@ -714,6 +715,10 @@ char *expand_user_path(const char *path, int real_home)
|
||||
|
||||
if (path == NULL)
|
||||
goto return_null;
|
||||
#ifdef __MINGW32__
|
||||
if (path[0] == '/')
|
||||
return system_path(path + 1);
|
||||
#endif
|
||||
if (path[0] == '~') {
|
||||
const char *first_slash = strchrnul(path, '/');
|
||||
const char *username = path + 1;
|
||||
@@ -1269,6 +1274,45 @@ char *strip_path_suffix(const char *path, const char *suffix)
|
||||
return offset == -1 ? NULL : xstrndup(path, offset);
|
||||
}
|
||||
|
||||
int is_mount_point_via_stat(struct strbuf *path)
|
||||
{
|
||||
size_t len = path->len;
|
||||
unsigned int current_dev;
|
||||
struct stat st;
|
||||
|
||||
if (!strcmp("/", path->buf))
|
||||
return 1;
|
||||
|
||||
strbuf_addstr(path, "/.");
|
||||
if (lstat(path->buf, &st)) {
|
||||
/*
|
||||
* If we cannot access the current directory, we cannot say
|
||||
* that it is a bind mount.
|
||||
*/
|
||||
strbuf_setlen(path, len);
|
||||
return 0;
|
||||
}
|
||||
current_dev = st.st_dev;
|
||||
|
||||
/* Now look at the parent directory */
|
||||
strbuf_addch(path, '.');
|
||||
if (lstat(path->buf, &st)) {
|
||||
/*
|
||||
* If we cannot access the parent directory, we cannot say
|
||||
* that it is a bind mount.
|
||||
*/
|
||||
strbuf_setlen(path, len);
|
||||
return 0;
|
||||
}
|
||||
strbuf_setlen(path, len);
|
||||
|
||||
/*
|
||||
* If the device ID differs between current and parent directory,
|
||||
* then it is a bind mount.
|
||||
*/
|
||||
return current_dev != st.st_dev;
|
||||
}
|
||||
|
||||
int daemon_avoid_alias(const char *p)
|
||||
{
|
||||
int sl, ndot;
|
||||
|
||||
14
send-pack.c
14
send-pack.c
@@ -38,6 +38,16 @@ int option_parse_push_signed(const struct option *opt,
|
||||
die("bad %s argument: %s", opt->long_name, arg);
|
||||
}
|
||||
|
||||
static int config_use_sideband = 1;
|
||||
|
||||
static int send_pack_config(const char *var, const char *value, void *unused)
|
||||
{
|
||||
if (!strcmp("sendpack.sideband", var))
|
||||
config_use_sideband = git_config_bool(var, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void feed_object(const struct object_id *oid, FILE *fh, int negative)
|
||||
{
|
||||
if (negative &&
|
||||
@@ -391,6 +401,8 @@ int send_pack(struct send_pack_args *args,
|
||||
const char *push_cert_nonce = NULL;
|
||||
struct packet_reader reader;
|
||||
|
||||
git_config(send_pack_config, NULL);
|
||||
|
||||
/* Does the other end support the reporting? */
|
||||
if (server_supports("report-status"))
|
||||
status_report = 1;
|
||||
@@ -398,7 +410,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;
|
||||
|
||||
12
sequencer.c
12
sequencer.c
@@ -4598,8 +4598,18 @@ static int make_script_with_merges(struct pretty_print_context *pp,
|
||||
else
|
||||
strbuf_addbuf(&label, &oneline);
|
||||
|
||||
/*
|
||||
* Sanitize labels by replacing non-alpha-numeric characters
|
||||
* (including white-space ones) by dashes, as they might be
|
||||
* illegal in file names (and hence in ref names).
|
||||
*
|
||||
* Note that we retain non-ASCII UTF-8 characters (identified
|
||||
* via the most significant bit). They should be all acceptable
|
||||
* in file names. We do not validate the UTF-8 here, that's not
|
||||
* the job of this function.
|
||||
*/
|
||||
for (p1 = label.buf; *p1; p1++)
|
||||
if (isspace(*p1))
|
||||
if (!(*p1 & 0x80) && !isalnum(*p1))
|
||||
*(char *)p1 = '-';
|
||||
|
||||
strbuf_reset(&buf);
|
||||
|
||||
4
t/README
4
t/README
@@ -397,6 +397,10 @@ GIT_TEST_STASH_USE_BUILTIN=<boolean>, when false, disables the
|
||||
built-in version of git-stash. See 'stash.useBuiltin' in
|
||||
git-config(1).
|
||||
|
||||
GIT_TEST_ADD_I_USE_BUILTIN=<boolean>, when true, enables the
|
||||
built-in version of git add -i. See 'add.interactive.useBuiltin' in
|
||||
git-config(1).
|
||||
|
||||
GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading
|
||||
of the index for the whole test suite by bypassing the default number of
|
||||
cache entries and thread minimums. Setting this to 1 will make the
|
||||
|
||||
@@ -200,6 +200,46 @@ static int testsuite(int argc, const char **argv)
|
||||
return !!ret;
|
||||
}
|
||||
|
||||
static int inherit_handle(const char *argv0)
|
||||
{
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
char path[PATH_MAX];
|
||||
int tmp;
|
||||
|
||||
/* First, open an inheritable handle */
|
||||
xsnprintf(path, sizeof(path), "out-XXXXXX");
|
||||
tmp = xmkstemp(path);
|
||||
|
||||
argv_array_pushl(&cp.args,
|
||||
"test-tool", argv0, "inherited-handle-child", NULL);
|
||||
cp.in = -1;
|
||||
cp.no_stdout = cp.no_stderr = 1;
|
||||
if (start_command(&cp) < 0)
|
||||
die("Could not start child process");
|
||||
|
||||
/* Then close it, and try to delete it. */
|
||||
close(tmp);
|
||||
if (unlink(path))
|
||||
die("Could not delete '%s'", path);
|
||||
|
||||
if (close(cp.in) < 0 || finish_command(&cp) < 0)
|
||||
die("Child did not finish");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inherit_handle_child(void)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
if (strbuf_read(&buf, 0, 0) < 0)
|
||||
die("Could not read stdin");
|
||||
printf("Received %s\n", buf.buf);
|
||||
strbuf_release(&buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd__run_command(int argc, const char **argv)
|
||||
{
|
||||
struct child_process proc = CHILD_PROCESS_INIT;
|
||||
@@ -207,6 +247,10 @@ int cmd__run_command(int argc, const char **argv)
|
||||
|
||||
if (argc > 1 && !strcmp(argv[1], "testsuite"))
|
||||
exit(testsuite(argc - 1, argv + 1));
|
||||
if (!strcmp(argv[1], "inherited-handle"))
|
||||
exit(inherit_handle(argv[0]));
|
||||
if (!strcmp(argv[1], "inherited-handle-child"))
|
||||
exit(inherit_handle_child());
|
||||
|
||||
if (argc < 3)
|
||||
return 1;
|
||||
|
||||
@@ -395,7 +395,7 @@ test_expect_success SYMLINKS 're-init to move gitdir symlink' '
|
||||
# Tests for the hidden file attribute on windows
|
||||
is_hidden () {
|
||||
# Use the output of `attrib`, ignore the absolute path
|
||||
case "$(attrib "$1")" in *H*?:*) return 0;; esac
|
||||
case "$("$SYSTEMROOT"/system32/attrib "$1")" in *H*?:*) return 0;; esac
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@ cat >hello-script <<-EOF
|
||||
cat hello-script
|
||||
EOF
|
||||
|
||||
test_expect_success MINGW 'subprocess inherits only std handles' '
|
||||
test-tool run-command inherited-handle
|
||||
'
|
||||
|
||||
test_expect_success 'start_command reports ENOENT (slash)' '
|
||||
test-tool run-command start-command-ENOENT ./does-not-exist 2>err &&
|
||||
test_i18ngrep "\./does-not-exist" err
|
||||
|
||||
@@ -468,4 +468,10 @@ test_expect_success '--rebase-merges with strategies' '
|
||||
test_cmp expect G.t
|
||||
'
|
||||
|
||||
test_expect_success '--rebase-merges with commit that can generate bad characters for filename' '
|
||||
git checkout -b colon-in-label E &&
|
||||
git merge -m "colon: this should work" G &&
|
||||
git rebase --rebase-merges --force-rebase E
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -23,6 +23,17 @@ diff_cmp () {
|
||||
test_cmp "$1.filtered" "$2.filtered"
|
||||
}
|
||||
|
||||
# This function uses a trick to manipulate the interactive add to use color:
|
||||
# the `want_color()` function special-cases the situation where a pager was
|
||||
# spawned and Git now wants to output colored text: to detect that situation,
|
||||
# the environment variable `GIT_PAGER_IN_USE` is set. However, color is
|
||||
# suppressed despite that environment variable if the `TERM` variable
|
||||
# indicates a dumb terminal, so we set that variable, too.
|
||||
|
||||
force_color () {
|
||||
env GIT_PAGER_IN_USE=true TERM=vt100 "$@"
|
||||
}
|
||||
|
||||
test_expect_success 'setup (initial)' '
|
||||
echo content >file &&
|
||||
git add file &&
|
||||
@@ -94,7 +105,6 @@ test_expect_success 'revert works (commit)' '
|
||||
grep "unchanged *+3/-0 file" output
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'setup expected' '
|
||||
cat >expected <<-\EOF
|
||||
EOF
|
||||
@@ -263,6 +273,35 @@ test_expect_success FILEMODE 'stage mode and hunk' '
|
||||
|
||||
# end of tests disabled when filemode is not usable
|
||||
|
||||
test_expect_success 'different prompts for mode change/deleted' '
|
||||
git reset --hard &&
|
||||
>file &&
|
||||
>deleted &&
|
||||
git add --chmod=+x file deleted &&
|
||||
echo changed >file &&
|
||||
rm deleted &&
|
||||
test_write_lines n n n |
|
||||
git -c core.filemode=true add -p >actual &&
|
||||
sed -n "s/^\(([0-9/]*) Stage .*?\).*/\1/p" actual >actual.filtered &&
|
||||
cat >expect <<-\EOF &&
|
||||
(1/1) Stage deletion [y,n,q,a,d,?]?
|
||||
(1/2) Stage mode change [y,n,q,a,d,j,J,g,/,?]?
|
||||
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]?
|
||||
EOF
|
||||
test_cmp expect actual.filtered
|
||||
'
|
||||
|
||||
test_expect_success 'correct message when there is nothing to do' '
|
||||
git reset --hard &&
|
||||
git add -p 2>err &&
|
||||
test_i18ngrep "No changes" err &&
|
||||
printf "\\0123" >binary &&
|
||||
git add binary &&
|
||||
printf "\\0abc" >binary &&
|
||||
git add -p 2>err &&
|
||||
test_i18ngrep "Only binary files changed" err
|
||||
'
|
||||
|
||||
test_expect_success 'setup again' '
|
||||
git reset --hard &&
|
||||
test_chmod +x file &&
|
||||
@@ -374,6 +413,36 @@ test_expect_success 'split hunk setup' '
|
||||
test_write_lines 10 15 20 21 22 23 24 30 40 50 60 >test
|
||||
'
|
||||
|
||||
test_expect_success 'goto hunk' '
|
||||
test_when_finished "git reset" &&
|
||||
tr _ " " >expect <<-EOF &&
|
||||
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? + 1: -1,2 +1,3 +15
|
||||
_ 2: -2,4 +3,8 +21
|
||||
go to which hunk? @@ -1,2 +1,3 @@
|
||||
_10
|
||||
+15
|
||||
_20
|
||||
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
|
||||
EOF
|
||||
test_write_lines s y g 1 | git add -p >actual &&
|
||||
tail -n 7 <actual >actual.trimmed &&
|
||||
test_cmp expect actual.trimmed
|
||||
'
|
||||
|
||||
test_expect_success 'navigate to hunk via regex' '
|
||||
test_when_finished "git reset" &&
|
||||
tr _ " " >expect <<-EOF &&
|
||||
(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? @@ -1,2 +1,3 @@
|
||||
_10
|
||||
+15
|
||||
_20
|
||||
(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
|
||||
EOF
|
||||
test_write_lines s y /1,2 | git add -p >actual &&
|
||||
tail -n 5 <actual >actual.trimmed &&
|
||||
test_cmp expect actual.trimmed
|
||||
'
|
||||
|
||||
test_expect_success 'split hunk "add -p (edit)"' '
|
||||
# Split, say Edit and do nothing. Then:
|
||||
#
|
||||
@@ -403,6 +472,40 @@ test_expect_failure 'split hunk "add -p (no, yes, edit)"' '
|
||||
! grep "^+31" actual
|
||||
'
|
||||
|
||||
test_expect_success 'split hunk with incomplete line at end' '
|
||||
git reset --hard &&
|
||||
printf "missing LF" >>test &&
|
||||
git add test &&
|
||||
test_write_lines before 10 20 30 40 50 60 70 >test &&
|
||||
git grep --cached missing &&
|
||||
test_write_lines s n y q | git add -p &&
|
||||
test_must_fail git grep --cached missing &&
|
||||
git grep before &&
|
||||
test_must_fail git grep --cached before
|
||||
'
|
||||
|
||||
test_expect_failure 'edit, adding lines to the first hunk' '
|
||||
test_write_lines 10 11 20 30 40 50 51 60 >test &&
|
||||
git reset &&
|
||||
tr _ " " >patch <<-EOF &&
|
||||
@@ -1,5 +1,6 @@
|
||||
_10
|
||||
+11
|
||||
+12
|
||||
_20
|
||||
+21
|
||||
+22
|
||||
_30
|
||||
EOF
|
||||
# test sequence is s(plit), e(dit), n(o)
|
||||
# q n q q is there to make sure we exit at the end.
|
||||
printf "%s\n" s e n q n q q |
|
||||
EDITOR=./fake_editor.sh git add -p 2>error &&
|
||||
test_must_be_empty error &&
|
||||
git diff --cached >actual &&
|
||||
grep "^+22" actual
|
||||
'
|
||||
|
||||
test_expect_success 'patch mode ignores unmerged entries' '
|
||||
git reset --hard &&
|
||||
test_commit conflict &&
|
||||
@@ -429,35 +532,48 @@ test_expect_success 'patch mode ignores unmerged entries' '
|
||||
diff_cmp expected diff
|
||||
'
|
||||
|
||||
test_expect_success TTY 'diffs can be colorized' '
|
||||
test_expect_success 'diffs can be colorized' '
|
||||
git reset --hard &&
|
||||
|
||||
echo content >test &&
|
||||
printf y | test_terminal git add -p >output 2>&1 &&
|
||||
printf y >y &&
|
||||
force_color git add -p >output 2>&1 <y &&
|
||||
|
||||
# We do not want to depend on the exact coloring scheme
|
||||
# git uses for diffs, so just check that we saw some kind of color.
|
||||
grep "$(printf "\\033")" output
|
||||
'
|
||||
|
||||
test_expect_success TTY 'diffFilter filters diff' '
|
||||
test_expect_success 'diffFilter filters diff' '
|
||||
git reset --hard &&
|
||||
|
||||
echo content >test &&
|
||||
test_config interactive.diffFilter "sed s/^/foo:/" &&
|
||||
printf y | test_terminal git add -p >output 2>&1 &&
|
||||
printf y >y &&
|
||||
force_color git add -p >output 2>&1 <y &&
|
||||
|
||||
# avoid depending on the exact coloring or content of the prompts,
|
||||
# and just make sure we saw our diff prefixed
|
||||
grep foo:.*content output
|
||||
'
|
||||
|
||||
test_expect_success TTY 'detect bogus diffFilter output' '
|
||||
test_expect_success 'detect bogus diffFilter output' '
|
||||
git reset --hard &&
|
||||
|
||||
echo content >test &&
|
||||
test_config interactive.diffFilter "echo too-short" &&
|
||||
printf y | test_must_fail test_terminal git add -p
|
||||
printf y >y &&
|
||||
test_must_fail force_color git add -p <y
|
||||
'
|
||||
|
||||
test_expect_success 'diff.algorithm is passed to `git diff-files`' '
|
||||
git reset --hard &&
|
||||
|
||||
>file &&
|
||||
git add file &&
|
||||
echo changed >file &&
|
||||
test_must_fail git -c diff.algorithm=bogus add -p 2>err &&
|
||||
test_i18ngrep "error: option diff-algorithm accepts " err
|
||||
'
|
||||
|
||||
test_expect_success 'patch-mode via -i prompts for files' '
|
||||
@@ -647,4 +763,29 @@ test_expect_success 'checkout -p works with pathological context lines' '
|
||||
test_write_lines a b a b a a b a b a >expect &&
|
||||
test_cmp expect a
|
||||
'
|
||||
|
||||
test_expect_success 'show help from add--helper' '
|
||||
git reset --hard &&
|
||||
cat >expect <<-EOF &&
|
||||
|
||||
<BOLD>*** Commands ***<RESET>
|
||||
1: <BOLD;BLUE>s<RESET>tatus 2: <BOLD;BLUE>u<RESET>pdate 3: <BOLD;BLUE>r<RESET>evert 4: <BOLD;BLUE>a<RESET>dd untracked
|
||||
5: <BOLD;BLUE>p<RESET>atch 6: <BOLD;BLUE>d<RESET>iff 7: <BOLD;BLUE>q<RESET>uit 8: <BOLD;BLUE>h<RESET>elp
|
||||
<BOLD;BLUE>What now<RESET>> <BOLD;RED>status - show paths with changes<RESET>
|
||||
<BOLD;RED>update - add working tree state to the staged set of changes<RESET>
|
||||
<BOLD;RED>revert - revert staged set of changes back to the HEAD version<RESET>
|
||||
<BOLD;RED>patch - pick hunks and update selectively<RESET>
|
||||
<BOLD;RED>diff - view diff between HEAD and index<RESET>
|
||||
<BOLD;RED>add untracked - add contents of untracked files to the staged set of changes<RESET>
|
||||
<BOLD>*** Commands ***<RESET>
|
||||
1: <BOLD;BLUE>s<RESET>tatus 2: <BOLD;BLUE>u<RESET>pdate 3: <BOLD;BLUE>r<RESET>evert 4: <BOLD;BLUE>a<RESET>dd untracked
|
||||
5: <BOLD;BLUE>p<RESET>atch 6: <BOLD;BLUE>d<RESET>iff 7: <BOLD;BLUE>q<RESET>uit 8: <BOLD;BLUE>h<RESET>elp
|
||||
<BOLD;BLUE>What now<RESET>>$SP
|
||||
Bye.
|
||||
EOF
|
||||
test_write_lines h | force_color git add -i >actual.colored &&
|
||||
test_decode_color <actual.colored >actual &&
|
||||
test_i18ncmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -1269,4 +1269,15 @@ test_expect_success 'stash apply should succeed with unmodified file' '
|
||||
git stash apply
|
||||
'
|
||||
|
||||
test_expect_success 'stash handles skip-worktree entries nicely' '
|
||||
test_commit A &&
|
||||
echo changed >A.t &&
|
||||
git add A.t &&
|
||||
git update-index --skip-worktree A.t &&
|
||||
rm A.t &&
|
||||
git stash &&
|
||||
|
||||
git rev-parse --verify refs/stash:A.t
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -89,7 +89,7 @@ test_expect_success 'none of this moved HEAD' '
|
||||
verify_saved_head
|
||||
'
|
||||
|
||||
test_expect_failure 'stash -p with split hunk' '
|
||||
test_expect_success 'stash -p with split hunk' '
|
||||
git reset --hard &&
|
||||
cat >test <<-\EOF &&
|
||||
aaa
|
||||
@@ -106,8 +106,8 @@ test_expect_failure 'stash -p with split hunk' '
|
||||
ccc
|
||||
EOF
|
||||
printf "%s\n" s n y q |
|
||||
test_might_fail git stash -p 2>error &&
|
||||
! test_must_be_empty error &&
|
||||
git stash -p 2>error &&
|
||||
test_must_be_empty error &&
|
||||
grep "added line 1" test &&
|
||||
! grep "added line 2" test
|
||||
'
|
||||
|
||||
@@ -17,14 +17,11 @@ fi
|
||||
UNCPATH="$(winpwd)"
|
||||
case "$UNCPATH" in
|
||||
[A-Z]:*)
|
||||
WITHOUTDRIVE="${UNCPATH#?:}"
|
||||
# Use administrative share e.g. \\localhost\C$\git-sdk-64\usr\src\git
|
||||
# (we use forward slashes here because MSYS2 and Git accept them, and
|
||||
# they are easier on the eyes)
|
||||
UNCPATH="//localhost/${UNCPATH%%:*}\$/${UNCPATH#?:}"
|
||||
test -d "$UNCPATH" || {
|
||||
skip_all='could not access administrative share; skipping'
|
||||
test_done
|
||||
}
|
||||
UNCPATH="//localhost/${UNCPATH%%:*}\$$WITHOUTDRIVE"
|
||||
;;
|
||||
*)
|
||||
skip_all='skipping UNC path tests, cannot determine current path as UNC'
|
||||
@@ -32,6 +29,18 @@ case "$UNCPATH" in
|
||||
;;
|
||||
esac
|
||||
|
||||
test_expect_success 'clone into absolute path lacking a drive prefix' '
|
||||
USINGBACKSLASHES="$(echo "$WITHOUTDRIVE"/without-drive-prefix |
|
||||
tr / \\\\)" &&
|
||||
git clone . "$USINGBACKSLASHES" &&
|
||||
test -f without-drive-prefix/.git/HEAD
|
||||
'
|
||||
|
||||
test -d "$UNCPATH" || {
|
||||
skip_all='could not access administrative share; skipping'
|
||||
test_done
|
||||
}
|
||||
|
||||
test_expect_success setup '
|
||||
test_commit initial
|
||||
'
|
||||
@@ -40,11 +49,23 @@ test_expect_success clone '
|
||||
git clone "file://$UNCPATH" clone
|
||||
'
|
||||
|
||||
test_expect_success 'clone without file://' '
|
||||
git clone "$UNCPATH" clone-without-file
|
||||
'
|
||||
|
||||
test_expect_success 'clone with backslashed path' '
|
||||
BACKSLASHED="$(echo "$UNCPATH" | tr / \\\\)" &&
|
||||
git clone "$BACKSLASHED" backslashed
|
||||
'
|
||||
|
||||
test_expect_success fetch '
|
||||
git init to-fetch &&
|
||||
(
|
||||
cd to-fetch &&
|
||||
git fetch "$UNCPATH" master
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success push '
|
||||
(
|
||||
cd clone &&
|
||||
@@ -95,7 +95,7 @@ test_expect_success 'clone -c remote.<remote>.fetch=<refspec> --origin=<name>' '
|
||||
# Tests for the hidden file attribute on windows
|
||||
is_hidden () {
|
||||
# Use the output of `attrib`, ignore the absolute path
|
||||
case "$(attrib "$1")" in *H*?:*) return 0;; esac
|
||||
case "$("$SYSTEMROOT"/system32/attrib "$1")" in *H*?:*) return 0;; esac
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -230,7 +230,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
|
||||
|
||||
@@ -134,6 +134,21 @@ test_expect_success 'git-clean, dirty case' '
|
||||
test_i18ncmp expected result
|
||||
'
|
||||
|
||||
test_expect_success '--ignore-skip-worktree-entries leaves worktree alone' '
|
||||
test_commit keep-me &&
|
||||
git update-index --skip-worktree keep-me.t &&
|
||||
rm keep-me.t &&
|
||||
|
||||
: ignoring the worktree &&
|
||||
git update-index --remove --ignore-skip-worktree-entries keep-me.t &&
|
||||
git diff-index --cached --exit-code HEAD &&
|
||||
|
||||
: not ignoring the worktree, a deletion is staged &&
|
||||
git update-index --remove keep-me.t &&
|
||||
test_must_fail git diff-index --cached --exit-code HEAD \
|
||||
--diff-filter=D -- keep-me.t
|
||||
'
|
||||
|
||||
#TODO test_expect_failure 'git-apply adds file' false
|
||||
#TODO test_expect_failure 'git-apply updates file' false
|
||||
#TODO test_expect_failure 'git-apply removes file' false
|
||||
|
||||
32
t/t7108-reset-stdin.sh
Executable file
32
t/t7108-reset-stdin.sh
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='reset --stdin'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'reset --stdin' '
|
||||
test_commit hello &&
|
||||
git rm hello.t &&
|
||||
test -z "$(git ls-files hello.t)" &&
|
||||
echo hello.t | git reset --stdin &&
|
||||
test hello.t = "$(git ls-files hello.t)"
|
||||
'
|
||||
|
||||
test_expect_success 'reset --stdin -z' '
|
||||
test_commit world &&
|
||||
git rm hello.t world.t &&
|
||||
test -z "$(git ls-files hello.t world.t)" &&
|
||||
printf world.tQworld.tQhello.tQ | q_to_nul | git reset --stdin -z &&
|
||||
printf "hello.t\nworld.t\n" >expect &&
|
||||
git ls-files >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--stdin requires --mixed' '
|
||||
echo hello.t >list &&
|
||||
test_must_fail git reset --soft --stdin <list &&
|
||||
test_must_fail git reset --hard --stdin <list &&
|
||||
git reset --mixed --stdin <list
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -737,4 +737,14 @@ test_expect_success MINGW 'handle clean & core.longpaths = false nicely' '
|
||||
test_i18ngrep "too long" .git/err
|
||||
'
|
||||
|
||||
test_expect_success MINGW 'clean does not traverse mount points' '
|
||||
mkdir target &&
|
||||
>target/dont-clean-me &&
|
||||
git init with-mountpoint &&
|
||||
cmd //c "mklink /j with-mountpoint\\mountpoint target" &&
|
||||
git -C with-mountpoint clean -dfx &&
|
||||
test_path_is_missing with-mountpoint/mountpoint &&
|
||||
test_path_is_file target/dont-clean-me
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -1194,8 +1194,8 @@ test_expect_success $PREREQ 'in-reply-to but no threading' '
|
||||
--to=nobody@example.com \
|
||||
--in-reply-to="<in-reply-id@example.com>" \
|
||||
--no-thread \
|
||||
$patches |
|
||||
grep "In-Reply-To: <in-reply-id@example.com>"
|
||||
$patches >out &&
|
||||
grep "In-Reply-To: <in-reply-id@example.com>" out
|
||||
'
|
||||
|
||||
test_expect_success $PREREQ 'no in-reply-to and no threading' '
|
||||
|
||||
@@ -43,14 +43,18 @@ test_expect_success 'setup repository and import' '
|
||||
|
||||
test_expect_success 'run log' "
|
||||
git reset --hard origin/a &&
|
||||
git svn log -r2 origin/trunk | grep ^r2 &&
|
||||
git svn log -r4 origin/trunk | grep ^r4 &&
|
||||
git svn log -r3 | grep ^r3
|
||||
git svn log -r2 origin/trunk >out &&
|
||||
grep ^r2 out &&
|
||||
git svn log -r4 origin/trunk >out &&
|
||||
grep ^r4 out &&
|
||||
git svn log -r3 >out &&
|
||||
grep ^r3 out
|
||||
"
|
||||
|
||||
test_expect_success 'run log against a from trunk' "
|
||||
git reset --hard origin/trunk &&
|
||||
git svn log -r3 origin/a | grep ^r3
|
||||
git svn log -r3 origin/a >out &&
|
||||
grep ^r3 out
|
||||
"
|
||||
|
||||
printf 'r1 \nr2 \nr4 \n' > expected-range-r1-r2-r4
|
||||
|
||||
@@ -746,4 +746,15 @@ test_expect_success 'merge commit gets exported with --import-marks' '
|
||||
)
|
||||
'
|
||||
|
||||
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
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "protocol.h"
|
||||
|
||||
static int debug;
|
||||
/* TODO: put somewhere sensible, e.g. git_transport_options? */
|
||||
static int auto_gc = 1;
|
||||
|
||||
struct helper_data {
|
||||
const char *name;
|
||||
@@ -472,10 +474,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)
|
||||
{
|
||||
@@ -512,6 +529,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
|
||||
@@ -545,6 +563,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;
|
||||
}
|
||||
|
||||
@@ -1018,6 +1042,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;
|
||||
|
||||
|
||||
20
usage.c
20
usage.c
@@ -9,14 +9,26 @@
|
||||
void vreportf(const char *prefix, const char *err, va_list params)
|
||||
{
|
||||
char msg[4096];
|
||||
char *p;
|
||||
char *p, *pend = msg + sizeof(msg);
|
||||
size_t prefix_len = strlen(prefix);
|
||||
|
||||
vsnprintf(msg, sizeof(msg), err, params);
|
||||
for (p = msg; *p; p++) {
|
||||
if (sizeof(msg) <= prefix_len) {
|
||||
fprintf(stderr, "BUG!!! too long a prefix '%s'\n", prefix);
|
||||
abort();
|
||||
}
|
||||
memcpy(msg, prefix, prefix_len);
|
||||
p = msg + prefix_len;
|
||||
if (vsnprintf(p, pend - p, err, params) < 0)
|
||||
*p = '\0'; /* vsnprintf() failed, clip at prefix */
|
||||
|
||||
for (; p != pend - 1 && *p; p++) {
|
||||
if (iscntrl(*p) && *p != '\t' && *p != '\n')
|
||||
*p = '?';
|
||||
}
|
||||
fprintf(stderr, "%s%s\n", prefix, msg);
|
||||
|
||||
*(p++) = '\n'; /* we no longer need a NUL */
|
||||
fflush(stderr);
|
||||
write_in_full(2, msg, p - msg);
|
||||
}
|
||||
|
||||
static NORETURN void usage_builtin(const char *err, va_list params)
|
||||
|
||||
Reference in New Issue
Block a user