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:
Johannes Schindelin
2018-10-11 13:38:58 +02:00
41 changed files with 1337 additions and 131 deletions

View File

@@ -429,6 +429,8 @@ include::config/reset.txt[]
include::config/sendemail.txt[]
include::config/sendpack.txt[]
include::config/sequencer.txt[]
include::config/showbranch.txt[]

View 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.

View File

@@ -12,6 +12,7 @@ SYNOPSIS
'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
DEPRECATED: 'git reset' [-q] [--stdin [-z]] [<tree-ish>]
DESCRIPTION
-----------
@@ -124,6 +125,16 @@ OPTIONS
+
For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
--stdin::
DEPRECATED (use `--pathspec-from-file=-` instead): 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::
DEPRECATED (use `--pathspec-file-nul` instead): Only meaningful with
`--stdin`; paths are separated with NUL character instead of LF.
EXAMPLES
--------

View File

@@ -52,6 +52,24 @@ void init_add_i_state(struct add_i_state *s, struct repository *r)
diff_get_color(s->use_color, DIFF_FILE_OLD));
init_color(r, s, "new", s->file_new_color,
diff_get_color(s->use_color, DIFF_FILE_NEW));
FREE_AND_NULL(s->interactive_diff_filter);
git_config_get_string("interactive.difffilter",
&s->interactive_diff_filter);
FREE_AND_NULL(s->interactive_diff_algorithm);
git_config_get_string("diff.algorithm",
&s->interactive_diff_algorithm);
git_config_get_bool("interactive.singlekey", &s->use_single_key);
}
void clear_add_i_state(struct add_i_state *s)
{
FREE_AND_NULL(s->interactive_diff_filter);
FREE_AND_NULL(s->interactive_diff_algorithm);
memset(s, 0, sizeof(*s));
s->use_color = -1;
}
/*
@@ -924,7 +942,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
parse_pathspec(&ps_selected,
PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
PATHSPEC_LITERAL_PATH, "", args.argv);
res = run_add_p(s->r, &ps_selected);
res = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected);
argv_array_clear(&args);
clear_pathspec(&ps_selected);
}
@@ -1149,6 +1167,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
strbuf_release(&print_file_item_data.worktree);
strbuf_release(&header);
prefix_item_list_clear(&commands);
clear_add_i_state(&s);
return res;
}

View File

@@ -15,13 +15,27 @@ struct add_i_state {
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);
struct repository;
struct pathspec;
int run_add_i(struct repository *r, const struct pathspec *ps);
int run_add_p(struct repository *r, const struct pathspec *ps);
enum add_p_mode {
ADD_P_ADD,
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

View File

@@ -6,15 +6,218 @@
#include "pathspec.h"
#include "color.h"
#include "diff.h"
#include "compat/terminal.h"
enum prompt_mode_type {
PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK
PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK,
PROMPT_MODE_MAX, /* must be last */
};
static const char *prompt_mode[] = {
N_("Stage mode change [y,n,a,q,d%s,?]? "),
N_("Stage deletion [y,n,a,q,d%s,?]? "),
N_("Stage this hunk [y,n,a,q,d%s,?]? ")
struct patch_mode {
/*
* The magic constant 4 is chosen such that all patch modes
* provide enough space for three command-line arguments followed by a
* trailing `NULL`.
*/
const char *diff_cmd[4], *apply_args[4], *apply_check_args[4];
unsigned is_reverse:1, index_only:1, apply_for_checkout:1;
const char *prompt_mode[PROMPT_MODE_MAX];
const char *edit_hunk_hint, *help_patch_text;
};
static struct patch_mode patch_mode_add = {
.diff_cmd = { "diff-files", NULL },
.apply_args = { "--cached", NULL },
.apply_check_args = { "--cached", NULL },
.prompt_mode = {
N_("Stage mode change [y,n,q,a,d%s,?]? "),
N_("Stage deletion [y,n,q,a,d%s,?]? "),
N_("Stage this hunk [y,n,q,a,d%s,?]? ")
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
"will immediately be marked for staging."),
.help_patch_text =
N_("y - stage this hunk\n"
"n - do not stage this hunk\n"
"q - quit; do not stage this hunk or any of the remaining "
"ones\n"
"a - stage this hunk and all later hunks in the file\n"
"d - do not stage this hunk or any of the later hunks in "
"the file\n")
};
static struct patch_mode patch_mode_stash = {
.diff_cmd = { "diff-index", "HEAD", NULL },
.apply_args = { "--cached", NULL },
.apply_check_args = { "--cached", NULL },
.prompt_mode = {
N_("Stash mode change [y,n,q,a,d%s,?]? "),
N_("Stash deletion [y,n,q,a,d%s,?]? "),
N_("Stash this hunk [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
"will immediately be marked for stashing."),
.help_patch_text =
N_("y - stash this hunk\n"
"n - do not stash this hunk\n"
"q - quit; do not stash this hunk or any of the remaining "
"ones\n"
"a - stash this hunk and all later hunks in the file\n"
"d - do not stash this hunk or any of the later hunks in "
"the file\n"),
};
static struct patch_mode patch_mode_reset_head = {
.diff_cmd = { "diff-index", "--cached", NULL },
.apply_args = { "-R", "--cached", NULL },
.apply_check_args = { "-R", "--cached", NULL },
.is_reverse = 1,
.index_only = 1,
.prompt_mode = {
N_("Unstage mode change [y,n,q,a,d%s,?]? "),
N_("Unstage deletion [y,n,q,a,d%s,?]? "),
N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
"will immediately be marked for unstaging."),
.help_patch_text =
N_("y - unstage this hunk\n"
"n - do not unstage this hunk\n"
"q - quit; do not unstage this hunk or any of the remaining "
"ones\n"
"a - unstage this hunk and all later hunks in the file\n"
"d - do not unstage this hunk or any of the later hunks in "
"the file\n"),
};
static struct patch_mode patch_mode_reset_nothead = {
.diff_cmd = { "diff-index", "-R", "--cached", NULL },
.apply_args = { "--cached", NULL },
.apply_check_args = { "--cached", NULL },
.index_only = 1,
.prompt_mode = {
N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
"will immediately be marked for applying."),
.help_patch_text =
N_("y - apply this hunk to index\n"
"n - do not apply this hunk to index\n"
"q - quit; do not apply this hunk or any of the remaining "
"ones\n"
"a - apply this hunk and all later hunks in the file\n"
"d - do not apply this hunk or any of the later hunks in "
"the file\n"),
};
static struct patch_mode patch_mode_checkout_index = {
.diff_cmd = { "diff-files", NULL },
.apply_args = { "-R", NULL },
.apply_check_args = { "-R", NULL },
.is_reverse = 1,
.prompt_mode = {
N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
"will immediately be marked for discarding."),
.help_patch_text =
N_("y - discard this hunk from worktree\n"
"n - do not discard this hunk from worktree\n"
"q - quit; do not discard this hunk or any of the remaining "
"ones\n"
"a - discard this hunk and all later hunks in the file\n"
"d - do not discard this hunk or any of the later hunks in "
"the file\n"),
};
static struct patch_mode patch_mode_checkout_head = {
.diff_cmd = { "diff-index", NULL },
.apply_for_checkout = 1,
.apply_check_args = { "-R", NULL },
.is_reverse = 1,
.prompt_mode = {
N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
"will immediately be marked for discarding."),
.help_patch_text =
N_("y - discard this hunk from index and worktree\n"
"n - do not discard this hunk from index and worktree\n"
"q - quit; do not discard this hunk or any of the remaining "
"ones\n"
"a - discard this hunk and all later hunks in the file\n"
"d - do not discard this hunk or any of the later hunks in "
"the file\n"),
};
static struct patch_mode patch_mode_checkout_nothead = {
.diff_cmd = { "diff-index", "-R", NULL },
.apply_for_checkout = 1,
.apply_check_args = { NULL },
.prompt_mode = {
N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
"will immediately be marked for applying."),
.help_patch_text =
N_("y - apply this hunk to index and worktree\n"
"n - do not apply this hunk to index and worktree\n"
"q - quit; do not apply this hunk or any of the remaining "
"ones\n"
"a - apply this hunk and all later hunks in the file\n"
"d - do not apply this hunk or any of the later hunks in "
"the file\n"),
};
static struct patch_mode patch_mode_worktree_head = {
.diff_cmd = { "diff-index", NULL },
.apply_args = { "-R", NULL },
.apply_check_args = { "-R", NULL },
.is_reverse = 1,
.prompt_mode = {
N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
"will immediately be marked for discarding."),
.help_patch_text =
N_("y - discard this hunk from worktree\n"
"n - do not discard this hunk from worktree\n"
"q - quit; do not discard this hunk or any of the remaining "
"ones\n"
"a - discard this hunk and all later hunks in the file\n"
"d - do not discard this hunk or any of the later hunks in "
"the file\n"),
};
static struct patch_mode patch_mode_worktree_nothead = {
.diff_cmd = { "diff-index", "-R", NULL },
.apply_args = { NULL },
.apply_check_args = { NULL },
.prompt_mode = {
N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
"will immediately be marked for applying."),
.help_patch_text =
N_("y - apply this hunk to worktree\n"
"n - do not apply this hunk to worktree\n"
"q - quit; do not apply this hunk or any of the remaining "
"ones\n"
"a - apply this hunk and all later hunks in the file\n"
"d - do not apply this hunk or any of the later hunks in "
"the file\n"),
};
struct hunk_header {
@@ -47,6 +250,10 @@ struct add_p_state {
unsigned deleted:1, mode_change:1,binary:1;
} *file_diff;
size_t file_diff_nr;
/* patch mode */
struct patch_mode *mode;
const char *revision;
};
static void err(struct add_p_state *s, const char *fmt, ...)
@@ -154,6 +361,7 @@ static int is_octal(const char *p, size_t len)
static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
{
struct argv_array args = ARGV_ARRAY_INIT;
const char *diff_algorithm = s->s.interactive_diff_algorithm;
struct strbuf *plain = &s->plain, *colored = NULL;
struct child_process cp = CHILD_PROCESS_INIT;
char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0';
@@ -162,9 +370,20 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
struct hunk *hunk = NULL;
int res;
argv_array_pushv(&args, s->mode->diff_cmd);
if (diff_algorithm)
argv_array_pushf(&args, "--diff-algorithm=%s", diff_algorithm);
if (s->revision) {
struct object_id oid;
argv_array_push(&args,
/* could be on an unborn branch */
!strcmp("HEAD", s->revision) &&
get_oid("HEAD", &oid) ?
empty_tree_oid_hex() : s->revision);
}
color_arg_index = args.argc;
/* Use `--no-color` explicitly, just in case `diff.color = always`. */
argv_array_pushl(&args, "diff-files", "-p", "--no-color", "--", NULL);
color_arg_index = args.argc - 2;
argv_array_pushl(&args, "--no-color", "-p", "--", NULL);
for (i = 0; i < ps->nr; i++)
argv_array_push(&args, ps->items[i].original);
@@ -183,6 +402,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
if (want_color_fd(1, -1)) {
struct child_process colored_cp = CHILD_PROCESS_INIT;
const char *diff_filter = s->s.interactive_diff_filter;
setup_child_process(s, &colored_cp, NULL);
xsnprintf((char *)args.argv[color_arg_index], 8, "--color");
@@ -192,6 +412,24 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
argv_array_clear(&args);
if (res)
return error(_("could not parse colored diff"));
if (diff_filter) {
struct child_process filter_cp = CHILD_PROCESS_INIT;
setup_child_process(s, &filter_cp,
diff_filter, NULL);
filter_cp.git_cmd = 0;
filter_cp.use_shell = 1;
strbuf_reset(&s->buf);
if (pipe_command(&filter_cp,
colored->buf, colored->len,
&s->buf, colored->len,
NULL, 0) < 0)
return error(_("failed to run '%s'"),
diff_filter);
strbuf_swap(colored, &s->buf);
}
strbuf_complete_line(colored);
colored_p = colored->buf;
colored_pend = colored_p + colored->len;
@@ -316,6 +554,9 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
colored_pend - colored_p);
if (colored_eol)
colored_p = colored_eol + 1;
else if (p != pend)
/* colored shorter than non-colored? */
goto mismatched_output;
else
colored_p = colored_pend;
@@ -340,6 +581,15 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
*/
hunk->splittable_into++;
/* non-colored shorter than colored? */
if (colored_p != colored_pend) {
mismatched_output:
error(_("mismatched output from interactive.diffFilter"));
advise(_("Your filter must maintain a one-to-one correspondence\n"
"between its input and output lines."));
return -1;
}
return 0;
}
@@ -382,7 +632,10 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk,
- header->colored_extra_start;
}
new_offset += delta;
if (s->mode->is_reverse)
old_offset -= delta;
else
new_offset += delta;
strbuf_addf(out, "@@ -%lu,%lu +%lu,%lu @@",
old_offset, header->old_count,
@@ -805,11 +1058,10 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
"(context).\n"
"To remove '%c' lines, delete them.\n"
"Lines starting with %c will be removed.\n"),
'-', '+', comment_line_char);
strbuf_commented_addf(&s->buf,
_("If the patch applies cleanly, the edited hunk "
"will immediately be\n"
"marked for staging.\n"));
s->mode->is_reverse ? '+' : '-',
s->mode->is_reverse ? '-' : '+',
comment_line_char);
strbuf_commented_addf(&s->buf, "%s", _(s->mode->edit_hunk_hint));
/*
* TRANSLATORS: 'it' refers to the patch mentioned in the previous
* messages.
@@ -890,21 +1142,35 @@ static int run_apply_check(struct add_p_state *s,
reassemble_patch(s, file_diff, 1, &s->buf);
setup_child_process(s, &cp,
"apply", "--cached", "--check", NULL);
"apply", "--check", NULL);
argv_array_pushv(&cp.args, s->mode->apply_check_args);
if (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))
return error(_("'git apply --cached' failed"));
return 0;
}
static int read_single_character(struct add_p_state *s)
{
if (s->s.use_single_key) {
int res = read_key_without_echo(&s->answer);
printf("%s\n", res == EOF ? "" : s->answer.buf);
return res;
}
if (strbuf_getline(&s->answer, stdin) == EOF)
return EOF;
strbuf_trim_trailing_newline(&s->answer);
return 0;
}
static int prompt_yesno(struct add_p_state *s, const char *prompt)
{
for (;;) {
color_fprintf(stdout, s->s.prompt_color, "%s", _(prompt));
fflush(stdout);
if (strbuf_getline(&s->answer, stdin) == EOF)
if (read_single_character(s) == EOF)
return -1;
strbuf_trim_trailing_newline(&s->answer);
switch (tolower(s->answer.buf[0])) {
case 'n': return 0;
case 'y': return 1;
@@ -957,6 +1223,57 @@ static int edit_hunk_loop(struct add_p_state *s,
}
}
static int apply_for_checkout(struct add_p_state *s, struct strbuf *diff,
int is_reverse)
{
const char *reverse = is_reverse ? "-R" : NULL;
struct child_process check_index = CHILD_PROCESS_INIT;
struct child_process check_worktree = CHILD_PROCESS_INIT;
struct child_process apply_index = CHILD_PROCESS_INIT;
struct child_process apply_worktree = CHILD_PROCESS_INIT;
int applies_index, applies_worktree;
setup_child_process(s, &check_index,
"apply", "--cached", "--check", reverse, NULL);
applies_index = !pipe_command(&check_index, diff->buf, diff->len,
NULL, 0, NULL, 0);
setup_child_process(s, &check_worktree,
"apply", "--check", reverse, NULL);
applies_worktree = !pipe_command(&check_worktree, diff->buf, diff->len,
NULL, 0, NULL, 0);
if (applies_worktree && applies_index) {
setup_child_process(s, &apply_index,
"apply", "--cached", reverse, NULL);
pipe_command(&apply_index, diff->buf, diff->len,
NULL, 0, NULL, 0);
setup_child_process(s, &apply_worktree,
"apply", reverse, NULL);
pipe_command(&apply_worktree, diff->buf, diff->len,
NULL, 0, NULL, 0);
return 1;
}
if (!applies_index) {
err(s, _("The selected hunks do not apply to the index!"));
if (prompt_yesno(s, _("Apply them to the worktree "
"anyway? ")) > 0) {
setup_child_process(s, &apply_worktree,
"apply", reverse, NULL);
return pipe_command(&apply_worktree, diff->buf,
diff->len, NULL, 0, NULL, 0);
}
err(s, _("Nothing was applied.\n"));
} else
/* As a last resort, show the diff to the user */
fwrite(diff->buf, diff->len, 1, stderr);
return 0;
}
#define SUMMARY_HEADER_WIDTH 20
#define SUMMARY_LINE_WIDTH 80
static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
@@ -1005,13 +1322,6 @@ static size_t display_hunks(struct add_p_state *s,
return end_index;
}
static const char help_patch_text[] =
N_("y - stage this hunk\n"
"n - do not stage this hunk\n"
"q - quit; do not stage this hunk or any of the remaining ones\n"
"a - stage this and all the remaining hunks\n"
"d - do not stage this hunk nor any of the remaining hunks\n");
static const char help_patch_remainder[] =
N_("j - leave this hunk undecided, see next undecided hunk\n"
"J - leave this hunk undecided, see next hunk\n"
@@ -1097,11 +1407,11 @@ static int patch_update_file(struct add_p_state *s,
(uintmax_t)hunk_index + 1,
(uintmax_t)file_diff->hunk_nr);
color_fprintf(stdout, s->s.prompt_color,
_(prompt_mode[prompt_mode_type]), s->buf.buf);
_(s->mode->prompt_mode[prompt_mode_type]),
s->buf.buf);
fflush(stdout);
if (strbuf_getline(&s->answer, stdin) == EOF)
if (read_single_character(s) == EOF)
break;
strbuf_trim_trailing_newline(&s->answer);
if (!s->answer.len)
continue;
@@ -1254,7 +1564,7 @@ soft_increment:
const char *p = _(help_patch_remainder), *eol = p;
color_fprintf(stdout, s->s.help_color, "%s",
_(help_patch_text));
_(s->mode->help_patch_text));
/*
* Show only those lines of the remainder that are
@@ -1288,10 +1598,16 @@ soft_increment:
reassemble_patch(s, file_diff, 0, &s->buf);
discard_index(s->s.r->index);
setup_child_process(s, &cp, "apply", "--cached", NULL);
if (pipe_command(&cp, s->buf.buf, s->buf.len,
NULL, 0, NULL, 0))
error(_("'git apply --cached' failed"));
if (s->mode->apply_for_checkout)
apply_for_checkout(s, &s->buf,
s->mode->is_reverse);
else {
setup_child_process(s, &cp, "apply", NULL);
argv_array_pushv(&cp.args, s->mode->apply_args);
if (pipe_command(&cp, s->buf.buf, s->buf.len,
NULL, 0, NULL, 0))
error(_("'git apply' failed"));
}
if (!repo_read_index(s->s.r))
repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
1, NULL, NULL, NULL);
@@ -1301,7 +1617,8 @@ soft_increment:
return quit;
}
int run_add_p(struct repository *r, const struct pathspec *ps)
int run_add_p(struct repository *r, enum add_p_mode mode,
const char *revision, const struct pathspec *ps)
{
struct add_p_state s = {
{ r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
@@ -1310,12 +1627,39 @@ int run_add_p(struct repository *r, const struct pathspec *ps)
init_add_i_state(&s.s, r);
if (mode == ADD_P_STASH)
s.mode = &patch_mode_stash;
else if (mode == ADD_P_RESET) {
if (!revision || !strcmp(revision, "HEAD"))
s.mode = &patch_mode_reset_head;
else
s.mode = &patch_mode_reset_nothead;
} else if (mode == ADD_P_CHECKOUT) {
if (!revision)
s.mode = &patch_mode_checkout_index;
else if (!strcmp(revision, "HEAD"))
s.mode = &patch_mode_checkout_head;
else
s.mode = &patch_mode_checkout_nothead;
} else if (mode == ADD_P_WORKTREE) {
if (!revision)
s.mode = &patch_mode_checkout_index;
else if (!strcmp(revision, "HEAD"))
s.mode = &patch_mode_worktree_head;
else
s.mode = &patch_mode_worktree_nothead;
} else
s.mode = &patch_mode_add;
s.revision = revision;
if (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||
repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
NULL, NULL, NULL) < 0 ||
(!s.mode->index_only &&
repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
NULL, NULL, NULL) < 0) ||
parse_diff(&s, ps) < 0) {
strbuf_release(&s.plain);
strbuf_release(&s.colored);
clear_add_i_state(&s.s);
return -1;
}
@@ -1334,5 +1678,6 @@ int run_add_p(struct repository *r, const struct pathspec *ps)
strbuf_release(&s.buf);
strbuf_release(&s.plain);
strbuf_release(&s.colored);
clear_add_i_state(&s.s);
return 0;
}

View File

@@ -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;

View File

@@ -31,6 +31,7 @@ static int take_worktree_changes;
static int add_renormalize;
static int pathspec_file_nul;
static const char *pathspec_from_file;
static int legacy_stash_p; /* support for the scripted `git stash` */
struct update_callback_data {
int flags;
@@ -196,12 +197,25 @@ int run_add_interactive(const char *revision, const char *patch_mode,
&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"))
die("'%s' not yet supported in the built-in add -p",
patch_mode);
return !!run_add_p(the_repository, pathspec);
if (!strcmp(patch_mode, "--patch"))
mode = ADD_P_ADD;
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");
@@ -329,6 +343,8 @@ static struct option builtin_add_options[] = {
N_("warn when adding an embedded repository")),
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_HIDDEN_BOOL(0, "legacy-stash-p", &legacy_stash_p,
N_("backend for `git stash -p`")),
OPT_END(),
};
@@ -429,6 +445,18 @@ int cmd_add(int argc, const char **argv, const char *prefix)
exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
}
if (legacy_stash_p) {
struct pathspec pathspec;
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_FULL |
PATHSPEC_SYMLINK_LEADING_PATH |
PATHSPEC_PREFIX_ORIGIN,
prefix, argv);
return run_add_interactive(NULL, "--patch=stash", &pathspec);
}
if (edit_interactive) {
if (pathspec_from_file)
die(_("--pathspec-from-file is incompatible with --edit"));

View File

@@ -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, &quoted);
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, &quoted);
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 {

View File

@@ -1223,7 +1223,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 =

View File

@@ -367,7 +367,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);
@@ -375,12 +375,16 @@ 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)
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

View File

@@ -25,6 +25,8 @@
#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)
@@ -33,6 +35,7 @@ static const char * const git_reset_usage[] = {
N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."),
N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"),
N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"),
N_("DEPRECATED: git reset [-q] [--stdin [-z]] [<tree-ish>]"),
NULL
};
@@ -290,6 +293,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
struct object_id oid;
struct pathspec pathspec;
int intent_to_add = 0;
int nul_term_line = 0, read_from_stdin = 0;
const struct option options[] = {
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
OPT_SET_INT(0, "mixed", &reset_type,
@@ -309,6 +313,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
N_("record only the fact that removed paths will be added later")),
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_BOOL('z', NULL, &nul_term_line,
N_("DEPRECATED (use --pathspec-file-nul instead): paths are separated with NUL character")),
OPT_BOOL(0, "stdin", &read_from_stdin,
N_("DEPRECATED (use --pathspec-from-file=- instead): read paths from <stdin>")),
OPT_END()
};
@@ -319,6 +327,12 @@ 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) {
pathspec_from_file = "-";
if (nul_term_line)
pathspec_file_nul = 1;
}
if (pathspec_from_file) {
if (patch_mode)
die(_("--pathspec-from-file is incompatible with --patch"));

View File

@@ -998,9 +998,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);
@@ -1014,16 +1014,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,
@@ -1033,7 +1036,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;

View File

@@ -1325,6 +1325,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);
/*

View File

@@ -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)

View File

@@ -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;
@@ -447,8 +460,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
@@ -967,11 +991,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;
}
@@ -1038,18 +1070,16 @@ char *mingw_getcwd(char *pointer, int len)
{
wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];
DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
HANDLE hnd;
if (!ret || ret >= ARRAY_SIZE(cwd)) {
errno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError());
return NULL;
}
ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));
if (!ret && GetLastError() == ERROR_ACCESS_DENIED) {
HANDLE hnd = CreateFileW(cwd, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hnd == INVALID_HANDLE_VALUE)
return NULL;
hnd = CreateFileW(cwd, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hnd != INVALID_HANDLE_VALUE) {
ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);
CloseHandle(hnd);
if (!ret || ret >= ARRAY_SIZE(wpointer))
@@ -1058,9 +1088,7 @@ char *mingw_getcwd(char *pointer, int len)
return NULL;
return pointer;
}
if (!ret || ret >= ARRAY_SIZE(wpointer))
return NULL;
if (xwcstoutf(pointer, wpointer, len) < 0)
if (xwcstoutf(pointer, cwd, len) < 0)
return NULL;
convert_slashes(pointer);
return pointer;
@@ -1466,6 +1494,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
const char *(*quote_arg)(const char *arg) =
is_msys2_sh(cmd ? cmd : *argv) ?
quote_arg_msys2 : quote_arg_msvc;
const char *strace_env;
/* Make sure to override previous errors, if any */
errno = 0;
@@ -1549,6 +1578,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);
@@ -1907,18 +1961,150 @@ static void ensure_socket_initialization(void)
initialized = 1;
}
static int winsock_error_to_errno(DWORD err)
{
switch (err) {
case WSAEINTR: return EINTR;
case WSAEBADF: return EBADF;
case WSAEACCES: return EACCES;
case WSAEFAULT: return EFAULT;
case WSAEINVAL: return EINVAL;
case WSAEMFILE: return EMFILE;
case WSAEWOULDBLOCK: return EWOULDBLOCK;
case WSAEINPROGRESS: return EINPROGRESS;
case WSAEALREADY: return EALREADY;
case WSAENOTSOCK: return ENOTSOCK;
case WSAEDESTADDRREQ: return EDESTADDRREQ;
case WSAEMSGSIZE: return EMSGSIZE;
case WSAEPROTOTYPE: return EPROTOTYPE;
case WSAENOPROTOOPT: return ENOPROTOOPT;
case WSAEPROTONOSUPPORT: return EPROTONOSUPPORT;
case WSAEOPNOTSUPP: return EOPNOTSUPP;
case WSAEAFNOSUPPORT: return EAFNOSUPPORT;
case WSAEADDRINUSE: return EADDRINUSE;
case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL;
case WSAENETDOWN: return ENETDOWN;
case WSAENETUNREACH: return ENETUNREACH;
case WSAENETRESET: return ENETRESET;
case WSAECONNABORTED: return ECONNABORTED;
case WSAECONNRESET: return ECONNRESET;
case WSAENOBUFS: return ENOBUFS;
case WSAEISCONN: return EISCONN;
case WSAENOTCONN: return ENOTCONN;
case WSAETIMEDOUT: return ETIMEDOUT;
case WSAECONNREFUSED: return ECONNREFUSED;
case WSAELOOP: return ELOOP;
case WSAENAMETOOLONG: return ENAMETOOLONG;
case WSAEHOSTUNREACH: return EHOSTUNREACH;
case WSAENOTEMPTY: return ENOTEMPTY;
/* No errno equivalent; default to EIO */
case WSAESOCKTNOSUPPORT:
case WSAEPFNOSUPPORT:
case WSAESHUTDOWN:
case WSAETOOMANYREFS:
case WSAEHOSTDOWN:
case WSAEPROCLIM:
case WSAEUSERS:
case WSAEDQUOT:
case WSAESTALE:
case WSAEREMOTE:
case WSASYSNOTREADY:
case WSAVERNOTSUPPORTED:
case WSANOTINITIALISED:
case WSAEDISCON:
case WSAENOMORE:
case WSAECANCELLED:
case WSAEINVALIDPROCTABLE:
case WSAEINVALIDPROVIDER:
case WSAEPROVIDERFAILEDINIT:
case WSASYSCALLFAILURE:
case WSASERVICE_NOT_FOUND:
case WSATYPE_NOT_FOUND:
case WSA_E_NO_MORE:
case WSA_E_CANCELLED:
case WSAEREFUSED:
case WSAHOST_NOT_FOUND:
case WSATRY_AGAIN:
case WSANO_RECOVERY:
case WSANO_DATA:
case WSA_QOS_RECEIVERS:
case WSA_QOS_SENDERS:
case WSA_QOS_NO_SENDERS:
case WSA_QOS_NO_RECEIVERS:
case WSA_QOS_REQUEST_CONFIRMED:
case WSA_QOS_ADMISSION_FAILURE:
case WSA_QOS_POLICY_FAILURE:
case WSA_QOS_BAD_STYLE:
case WSA_QOS_BAD_OBJECT:
case WSA_QOS_TRAFFIC_CTRL_ERROR:
case WSA_QOS_GENERIC_ERROR:
case WSA_QOS_ESERVICETYPE:
case WSA_QOS_EFLOWSPEC:
case WSA_QOS_EPROVSPECBUF:
case WSA_QOS_EFILTERSTYLE:
case WSA_QOS_EFILTERTYPE:
case WSA_QOS_EFILTERCOUNT:
case WSA_QOS_EOBJLENGTH:
case WSA_QOS_EFLOWCOUNT:
#ifndef _MSC_VER
case WSA_QOS_EUNKNOWNPSOBJ:
#endif
case WSA_QOS_EPOLICYOBJ:
case WSA_QOS_EFLOWDESC:
case WSA_QOS_EPSFLOWSPEC:
case WSA_QOS_EPSFILTERSPEC:
case WSA_QOS_ESDMODEOBJ:
case WSA_QOS_ESHAPERATEOBJ:
case WSA_QOS_RESERVED_PETYPE:
default: return EIO;
}
}
/*
* On Windows, `errno` is a global macro to a function call.
* This makes it difficult to debug and single-step our mappings.
*/
static inline void set_wsa_errno(void)
{
DWORD wsa = WSAGetLastError();
int e = winsock_error_to_errno(wsa);
errno = e;
#ifdef DEBUG_WSA_ERRNO
fprintf(stderr, "winsock error: %d -> %d\n", wsa, e);
fflush(stderr);
#endif
}
static inline int winsock_return(int ret)
{
if (ret < 0)
set_wsa_errno();
return ret;
}
#define WINSOCK_RETURN(x) do { return winsock_return(x); } while (0)
#undef gethostname
int mingw_gethostname(char *name, int namelen)
{
ensure_socket_initialization();
return gethostname(name, namelen);
ensure_socket_initialization();
WINSOCK_RETURN(gethostname(name, namelen));
}
#undef gethostbyname
struct hostent *mingw_gethostbyname(const char *host)
{
struct hostent *ret;
ensure_socket_initialization();
return gethostbyname(host);
ret = gethostbyname(host);
if (!ret)
set_wsa_errno();
return ret;
}
#undef getaddrinfo
@@ -1926,7 +2112,7 @@ int mingw_getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints, struct addrinfo **res)
{
ensure_socket_initialization();
return getaddrinfo(node, service, hints, res);
WINSOCK_RETURN(getaddrinfo(node, service, hints, res));
}
int mingw_socket(int domain, int type, int protocol)
@@ -1946,7 +2132,7 @@ int mingw_socket(int domain, int type, int protocol)
* in errno so that _if_ someone looks up the code somewhere,
* then it is at least the number that are usually listed.
*/
errno = WSAGetLastError();
set_wsa_errno();
return -1;
}
/* convert into a file descriptor */
@@ -1962,35 +2148,35 @@ int mingw_socket(int domain, int type, int protocol)
int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz)
{
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
return connect(s, sa, sz);
WINSOCK_RETURN(connect(s, sa, sz));
}
#undef bind
int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz)
{
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
return bind(s, sa, sz);
WINSOCK_RETURN(bind(s, sa, sz));
}
#undef setsockopt
int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen)
{
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
return setsockopt(s, lvl, optname, (const char*)optval, optlen);
WINSOCK_RETURN(setsockopt(s, lvl, optname, (const char*)optval, optlen));
}
#undef shutdown
int mingw_shutdown(int sockfd, int how)
{
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
return shutdown(s, how);
WINSOCK_RETURN(shutdown(s, how));
}
#undef listen
int mingw_listen(int sockfd, int backlog)
{
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
return listen(s, backlog);
WINSOCK_RETURN(listen(s, backlog));
}
#undef accept
@@ -2001,6 +2187,11 @@ int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz)
SOCKET s1 = (SOCKET)_get_osfhandle(sockfd1);
SOCKET s2 = accept(s1, sa, sz);
if (s2 == INVALID_SOCKET) {
set_wsa_errno();
return -1;
}
/* convert into a file descriptor */
if ((sockfd2 = _open_osfhandle(s2, O_RDWR|O_BINARY)) < 0) {
int err = errno;
@@ -2395,6 +2586,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;
@@ -2773,6 +2986,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]);

View File

@@ -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
@@ -598,6 +602,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
*/

View File

@@ -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

View File

@@ -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 */

View File

@@ -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 )

View File

@@ -1662,9 +1662,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;
}

View File

@@ -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
@@ -665,10 +670,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

View File

@@ -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++;

View File

@@ -389,6 +389,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

View File

@@ -207,13 +207,13 @@ create_stash () {
# find out what the user wants
GIT_INDEX_FILE="$TMP-index" \
git add--interactive --patch=stash -- "$@" &&
git add --legacy-stash-p -- "$@" &&
# 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")"

View File

@@ -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
View File

@@ -12,6 +12,7 @@
#include "packfile.h"
#include "object-store.h"
#include "lockfile.h"
#include "exec-cmd.h"
static int get_st_mode_bits(const char *path, int *mode)
{
@@ -732,6 +733,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;
@@ -1287,6 +1292,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;

View File

@@ -959,7 +959,7 @@ static int verify_dotfile(const char *rest, unsigned mode)
int verify_path(const char *path, unsigned mode)
{
char c;
char c = 0;
if (has_dos_drive_prefix(path))
return 0;
@@ -974,6 +974,7 @@ int verify_path(const char *path, unsigned mode)
if (is_dir_sep(c)) {
inside:
if (protect_hfs) {
if (is_hfs_dotgit(path))
return 0;
if (S_ISLNK(mode)) {
@@ -982,6 +983,10 @@ inside:
}
}
if (protect_ntfs) {
#ifdef GIT_WINDOWS_NATIVE
if (c == '\\')
return 0;
#endif
if (is_ntfs_dotgit(path))
return 0;
if (S_ISLNK(mode)) {
@@ -1278,11 +1283,6 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
int new_only = option & ADD_CACHE_NEW_ONLY;
#ifdef GIT_WINDOWS_NATIVE
if (protect_ntfs && strchr(ce->name, '\\'))
return error(_("filename in tree entry contains backslash: '%s'"), ce->name);
#endif
if (!(option & ADD_CACHE_KEEP_CACHE_TREE))
cache_tree_invalidate_path(istate, ce->name);

View File

@@ -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 &&
@@ -393,6 +403,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;
@@ -400,7 +412,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;

View File

@@ -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
}

View File

@@ -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
'

View File

@@ -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 &&

View File

@@ -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
}

View File

@@ -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

32
t/t7108-reset-stdin.sh Executable file
View 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

View File

@@ -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

View File

@@ -209,7 +209,7 @@ test_expect_success MINGW 'prevent git~1 squatting on Windows' '
hash="$(echo x | git hash-object -w --stdin)" &&
test_must_fail git update-index --add \
--cacheinfo 160000,$rev,d\\a 2>err &&
test_i18ngrep backslash err &&
test_i18ngrep "Invalid path" err &&
git -c core.protectNTFS=false update-index --add \
--cacheinfo 100644,$modules,.gitmodules \
--cacheinfo 160000,$rev,c \

View File

@@ -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' '

View File

@@ -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

View File

@@ -747,4 +747,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

View File

@@ -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;
@@ -473,10 +475,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)
{
@@ -513,6 +530,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
@@ -546,6 +564,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;
}
@@ -1019,6 +1043,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;