mirror of
https://github.com/git-for-windows/git.git
synced 2026-05-27 03:51:17 -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
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.
|
||||
@@ -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
|
||||
--------
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
413
add-patch.c
413
add-patch.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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;
|
||||
|
||||
1
cache.h
1
cache.h
@@ -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);
|
||||
|
||||
/*
|
||||
|
||||
@@ -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)
|
||||
|
||||
260
compat/mingw.c
260
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;
|
||||
@@ -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]);
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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 )
|
||||
|
||||
|
||||
6
config.c
6
config.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")"
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
12
read-cache.c
12
read-cache.c
@@ -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);
|
||||
|
||||
|
||||
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 &&
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user