2019-04-22 04:14:43 +02:00
|
|
|
#define USE_THE_INDEX_COMPATIBILITY_MACROS
|
2019-02-26 00:16:15 +01:00
|
|
|
#include "builtin.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "parse-options.h"
|
|
|
|
#include "refs.h"
|
|
|
|
#include "lockfile.h"
|
|
|
|
#include "cache-tree.h"
|
|
|
|
#include "unpack-trees.h"
|
|
|
|
#include "merge-recursive.h"
|
2020-07-28 22:23:39 +02:00
|
|
|
#include "strvec.h"
|
2019-02-26 00:16:15 +01:00
|
|
|
#include "run-command.h"
|
|
|
|
#include "dir.h"
|
2021-04-02 23:43:14 +02:00
|
|
|
#include "entry.h"
|
2019-02-26 00:16:15 +01:00
|
|
|
#include "rerere.h"
|
2019-02-26 00:16:20 +01:00
|
|
|
#include "revision.h"
|
|
|
|
#include "log-tree.h"
|
2019-02-26 00:16:22 +01:00
|
|
|
#include "diffcore.h"
|
2019-02-26 00:16:30 +01:00
|
|
|
#include "exec-cmd.h"
|
2021-03-23 15:19:32 +01:00
|
|
|
#include "entry.h"
|
2019-02-26 00:16:22 +01:00
|
|
|
|
|
|
|
#define INCLUDE_ALL_FILES 2
|
2019-02-26 00:16:15 +01:00
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
static const char * const git_stash_usage[] = {
|
|
|
|
N_("git stash list [<options>]"),
|
|
|
|
N_("git stash show [<options>] [<stash>]"),
|
|
|
|
N_("git stash drop [-q|--quiet] [<stash>]"),
|
|
|
|
N_("git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"),
|
|
|
|
N_("git stash branch <branchname> [<stash>]"),
|
2021-05-16 23:57:04 +02:00
|
|
|
"git stash clear",
|
2021-10-18 18:09:06 +02:00
|
|
|
N_("git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
|
2019-02-26 00:16:23 +01:00
|
|
|
" [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
|
2020-02-17 18:25:22 +01:00
|
|
|
" [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
|
2019-02-26 00:16:23 +01:00
|
|
|
" [--] [<pathspec>...]]"),
|
2021-10-18 18:09:06 +02:00
|
|
|
N_("git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
|
2019-02-26 00:16:25 +01:00
|
|
|
" [-u|--include-untracked] [-a|--all] [<message>]"),
|
2019-02-26 00:16:16 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
static const char * const git_stash_list_usage[] = {
|
|
|
|
N_("git stash list [<options>]"),
|
2019-02-26 00:16:19 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
static const char * const git_stash_show_usage[] = {
|
|
|
|
N_("git stash show [<options>] [<stash>]"),
|
2019-02-26 00:16:20 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
static const char * const git_stash_drop_usage[] = {
|
|
|
|
N_("git stash drop [-q|--quiet] [<stash>]"),
|
2019-02-26 00:16:15 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
static const char * const git_stash_pop_usage[] = {
|
|
|
|
N_("git stash pop [--index] [-q|--quiet] [<stash>]"),
|
2019-02-26 00:16:18 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
static const char * const git_stash_apply_usage[] = {
|
|
|
|
N_("git stash apply [--index] [-q|--quiet] [<stash>]"),
|
2019-02-26 00:16:15 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
static const char * const git_stash_branch_usage[] = {
|
|
|
|
N_("git stash branch <branchname> [<stash>]"),
|
2019-02-26 00:16:17 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
static const char * const git_stash_clear_usage[] = {
|
2021-05-16 23:57:04 +02:00
|
|
|
"git stash clear",
|
2019-02-26 00:16:16 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
static const char * const git_stash_store_usage[] = {
|
|
|
|
N_("git stash store [-m|--message <message>] [-q|--quiet] <commit>"),
|
2019-02-26 00:16:21 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
static const char * const git_stash_push_usage[] = {
|
|
|
|
N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
|
2019-02-26 00:16:23 +01:00
|
|
|
" [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
|
|
|
|
" [--] [<pathspec>...]]"),
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
static const char * const git_stash_save_usage[] = {
|
|
|
|
N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
|
2021-09-13 02:13:19 +02:00
|
|
|
" [-u|--include-untracked] [-a|--all] [<message>]"),
|
2019-02-26 00:16:25 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2021-02-09 08:28:53 +01:00
|
|
|
static const char ref_stash[] = "refs/stash";
|
2019-02-26 00:16:15 +01:00
|
|
|
static struct strbuf stash_index_path = STRBUF_INIT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* w_commit is set to the commit containing the working tree
|
|
|
|
* b_commit is set to the base commit
|
|
|
|
* i_commit is set to the commit containing the index tree
|
|
|
|
* u_commit is set to the commit containing the untracked files tree
|
|
|
|
* w_tree is set to the working tree
|
|
|
|
* b_tree is set to the base tree
|
|
|
|
* i_tree is set to the index tree
|
|
|
|
* u_tree is set to the untracked files tree
|
|
|
|
*/
|
|
|
|
struct stash_info {
|
|
|
|
struct object_id w_commit;
|
|
|
|
struct object_id b_commit;
|
|
|
|
struct object_id i_commit;
|
|
|
|
struct object_id u_commit;
|
|
|
|
struct object_id w_tree;
|
|
|
|
struct object_id b_tree;
|
|
|
|
struct object_id i_tree;
|
|
|
|
struct object_id u_tree;
|
|
|
|
struct strbuf revision;
|
|
|
|
int is_stash_ref;
|
|
|
|
int has_u;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void free_stash_info(struct stash_info *info)
|
|
|
|
{
|
|
|
|
strbuf_release(&info->revision);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void assert_stash_like(struct stash_info *info, const char *revision)
|
|
|
|
{
|
|
|
|
if (get_oidf(&info->b_commit, "%s^1", revision) ||
|
|
|
|
get_oidf(&info->w_tree, "%s:", revision) ||
|
|
|
|
get_oidf(&info->b_tree, "%s^1:", revision) ||
|
|
|
|
get_oidf(&info->i_tree, "%s^2:", revision))
|
|
|
|
die(_("'%s' is not a stash-like commit"), revision);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_stash_info(struct stash_info *info, int argc, const char **argv)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
char *end_of_rev;
|
|
|
|
char *expanded_ref;
|
|
|
|
const char *revision;
|
|
|
|
const char *commit = NULL;
|
|
|
|
struct object_id dummy;
|
|
|
|
struct strbuf symbolic = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (argc > 1) {
|
|
|
|
int i;
|
|
|
|
struct strbuf refs_msg = STRBUF_INIT;
|
|
|
|
|
|
|
|
for (i = 0; i < argc; i++)
|
|
|
|
strbuf_addf(&refs_msg, " '%s'", argv[i]);
|
|
|
|
|
|
|
|
fprintf_ln(stderr, _("Too many revisions specified:%s"),
|
|
|
|
refs_msg.buf);
|
|
|
|
strbuf_release(&refs_msg);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc == 1)
|
|
|
|
commit = argv[0];
|
|
|
|
|
|
|
|
strbuf_init(&info->revision, 0);
|
|
|
|
if (!commit) {
|
|
|
|
if (!ref_exists(ref_stash)) {
|
|
|
|
free_stash_info(info);
|
|
|
|
fprintf_ln(stderr, _("No stash entries found."));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_addf(&info->revision, "%s@{0}", ref_stash);
|
|
|
|
} else if (strspn(commit, "0123456789") == strlen(commit)) {
|
|
|
|
strbuf_addf(&info->revision, "%s@{%s}", ref_stash, commit);
|
|
|
|
} else {
|
|
|
|
strbuf_addstr(&info->revision, commit);
|
|
|
|
}
|
|
|
|
|
|
|
|
revision = info->revision.buf;
|
|
|
|
|
|
|
|
if (get_oid(revision, &info->w_commit)) {
|
|
|
|
error(_("%s is not a valid reference"), revision);
|
|
|
|
free_stash_info(info);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_stash_like(info, revision);
|
|
|
|
|
|
|
|
info->has_u = !get_oidf(&info->u_tree, "%s^3:", revision);
|
|
|
|
|
|
|
|
end_of_rev = strchrnul(revision, '@');
|
|
|
|
strbuf_add(&symbolic, revision, end_of_rev - revision);
|
|
|
|
|
2020-09-02 00:28:09 +02:00
|
|
|
ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref, 0);
|
2019-02-26 00:16:15 +01:00
|
|
|
strbuf_release(&symbolic);
|
|
|
|
switch (ret) {
|
|
|
|
case 0: /* Not found, but valid ref */
|
|
|
|
info->is_stash_ref = 0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
|
|
|
|
break;
|
|
|
|
default: /* Invalid or ambiguous */
|
|
|
|
free_stash_info(info);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(expanded_ref);
|
|
|
|
return !(ret == 0 || ret == 1);
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:16 +01:00
|
|
|
static int do_clear_stash(void)
|
|
|
|
{
|
|
|
|
struct object_id obj;
|
|
|
|
if (get_oid(ref_stash, &obj))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return delete_ref(NULL, ref_stash, &obj, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int clear_stash(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
|
|
|
struct option options[] = {
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
argc = parse_options(argc, argv, prefix, options,
|
2019-02-26 00:16:28 +01:00
|
|
|
git_stash_clear_usage,
|
2019-02-26 00:16:16 +01:00
|
|
|
PARSE_OPT_STOP_AT_NON_OPTION);
|
|
|
|
|
|
|
|
if (argc)
|
2021-02-23 22:11:32 +01:00
|
|
|
return error(_("git stash clear with arguments is "
|
2019-02-26 00:16:16 +01:00
|
|
|
"unimplemented"));
|
|
|
|
|
|
|
|
return do_clear_stash();
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:15 +01:00
|
|
|
static int reset_tree(struct object_id *i_tree, int update, int reset)
|
|
|
|
{
|
|
|
|
int nr_trees = 1;
|
|
|
|
struct unpack_trees_options opts;
|
|
|
|
struct tree_desc t[MAX_UNPACK_TREES];
|
|
|
|
struct tree *tree;
|
|
|
|
struct lock_file lock_file = LOCK_INIT;
|
|
|
|
|
|
|
|
read_cache_preload(NULL);
|
|
|
|
if (refresh_cache(REFRESH_QUIET))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
|
|
|
|
|
|
|
|
memset(&opts, 0, sizeof(opts));
|
|
|
|
|
|
|
|
tree = parse_tree_indirect(i_tree);
|
|
|
|
if (parse_tree(tree))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
init_tree_desc(t, tree->buffer, tree->size);
|
|
|
|
|
|
|
|
opts.head_idx = 1;
|
|
|
|
opts.src_index = &the_index;
|
|
|
|
opts.dst_index = &the_index;
|
|
|
|
opts.merge = 1;
|
2021-09-27 18:33:44 +02:00
|
|
|
opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
|
2019-02-26 00:16:15 +01:00
|
|
|
opts.update = update;
|
2021-09-27 18:33:44 +02:00
|
|
|
if (update)
|
2021-09-27 18:33:43 +02:00
|
|
|
opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
|
2019-02-26 00:16:15 +01:00
|
|
|
opts.fn = oneway_merge;
|
|
|
|
|
|
|
|
if (unpack_trees(nr_trees, t, &opts))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
|
|
|
|
return error(_("unable to write new index file"));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit)
|
|
|
|
{
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
const char *w_commit_hex = oid_to_hex(w_commit);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Diff-tree would not be very hard to replace with a native function,
|
|
|
|
* however it should be done together with apply_cached.
|
|
|
|
*/
|
|
|
|
cp.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp.args, "diff-tree", "--binary", NULL);
|
|
|
|
strvec_pushf(&cp.args, "%s^2^..%s^2", w_commit_hex, w_commit_hex);
|
2019-02-26 00:16:15 +01:00
|
|
|
|
|
|
|
return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int apply_cached(struct strbuf *out)
|
|
|
|
{
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Apply currently only reads either from stdin or a file, thus
|
|
|
|
* apply_all_patches would have to be updated to optionally take a
|
|
|
|
* buffer.
|
|
|
|
*/
|
|
|
|
cp.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp.args, "apply", "--cached", NULL);
|
2019-02-26 00:16:15 +01:00
|
|
|
return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int reset_head(void)
|
|
|
|
{
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset is overall quite simple, however there is no current public
|
|
|
|
* API for resetting.
|
|
|
|
*/
|
|
|
|
cp.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(&cp.args, "reset");
|
2019-02-26 00:16:15 +01:00
|
|
|
|
|
|
|
return run_command(&cp);
|
|
|
|
}
|
|
|
|
|
2021-09-10 12:29:55 +02:00
|
|
|
static int is_path_a_directory(const char *path)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* This function differs from abspath.c:is_directory() in that
|
|
|
|
* here we use lstat() instead of stat(); we do not want to
|
|
|
|
* follow symbolic links here.
|
|
|
|
*/
|
|
|
|
struct stat st;
|
|
|
|
return (!lstat(path, &st) && S_ISDIR(st.st_mode));
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:22 +01:00
|
|
|
static void add_diff_to_buf(struct diff_queue_struct *q,
|
|
|
|
struct diff_options *options,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < q->nr; i++) {
|
2021-09-10 12:29:55 +02:00
|
|
|
if (is_path_a_directory(q->queue[i]->one->path))
|
|
|
|
continue;
|
|
|
|
|
2019-02-26 00:16:22 +01:00
|
|
|
strbuf_addstr(data, q->queue[i]->one->path);
|
|
|
|
|
|
|
|
/* NUL-terminate: will be fed to update-index -z */
|
|
|
|
strbuf_addch(data, '\0');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:15 +01:00
|
|
|
static int restore_untracked(struct object_id *u_tree)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to run restore files from a given index, but without
|
|
|
|
* affecting the current index, so we use GIT_INDEX_FILE with
|
|
|
|
* run_command to fork processes that will not interfere.
|
|
|
|
*/
|
|
|
|
cp.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(&cp.args, "read-tree");
|
|
|
|
strvec_push(&cp.args, oid_to_hex(u_tree));
|
|
|
|
strvec_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
stash_index_path.buf);
|
2019-02-26 00:16:15 +01:00
|
|
|
if (run_command(&cp)) {
|
|
|
|
remove_path(stash_index_path.buf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
child_process_init(&cp);
|
|
|
|
cp.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp.args, "checkout-index", "--all", NULL);
|
|
|
|
strvec_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
stash_index_path.buf);
|
2019-02-26 00:16:15 +01:00
|
|
|
|
|
|
|
res = run_command(&cp);
|
|
|
|
remove_path(stash_index_path.buf);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
stash: remove unnecessary process forking
When stash was converted from shell to a builtin, it merely
transliterated the forking of various git commands from shell to a C
program that would fork the same commands. Some of those were converted
over to actual library calls, but much of the pipeline-of-commands
design still remains. Fix some of this by replacing the portion
corresponding to
git diff-index --cached --name-only --diff-filter=A $CTREE >"$a"
git read-tree --reset $CTREE
git update-index --add --stdin <"$a"
rm -f "$a"
into a library function that does the same thing. (The read-tree
--reset was already partially converted over to a library call, but as
an independent piece.) Note here that this came after a merge operation
was performed. The merge machinery always stages anything that cleanly
merges, and the above code only runs if there are no conflicts. Its
purpose is to make it so that when there are no conflicts, all the
changes from the stash are unstaged. However, that causes brand new
files from the stash to become untracked, so the code above first saves
those files off and then re-adds them afterwards.
We replace the whole series of commands with a simple function that will
unstage files that are not newly added. This doesn't fix any bugs in
the usage of these commands, it simply matches the existing behavior but
makes it into a single atomic operation that we can then operate on as a
whole. A subsequent commit will take advantage of this to fix issues
with these commands in sparse-checkouts.
This conversion incidentally fixes t3906.1, because the separate
update-index process would die with the following error messages:
error: uninitialized_sub: is a directory - add files inside instead
fatal: Unable to process path uninitialized_sub
The unstaging of the directory as a submodule meant it was no longer
tracked, and thus as an uninitialized directory it could not be added
back using `git update-index --add`, thus resulting in this error and
early abort. Most of the submodule tests in 3906 continue to fail after
this change, this change was just enough to push the first of those
tests to success.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-12-01 23:25:17 +01:00
|
|
|
static void unstage_changes_unless_new(struct object_id *orig_tree)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* When we enter this function, there has been a clean merge of
|
|
|
|
* relevant trees, and the merge logic always stages whatever merges
|
|
|
|
* cleanly. We want to unstage those changes, unless it corresponds
|
|
|
|
* to a file that didn't exist as of orig_tree.
|
stash: fix stash application in sparse-checkouts
sparse-checkouts are built on the patterns in the
$GIT_DIR/info/sparse-checkout file, where commands have modified
behavior for paths that do not match those patterns. The differences in
behavior, as far as the bugs concerned here, fall into three different
categories (with git subcommands that fall into each category listed):
* commands that only look at files matching the patterns:
* status
* diff
* clean
* update-index
* commands that remove files from the working tree that do not match
the patterns, and restore files that do match them:
* read-tree
* switch
* checkout
* reset (--hard)
* commands that omit writing files to the working tree that do not
match the patterns, unless those files are not clean:
* merge
* rebase
* cherry-pick
* revert
There are some caveats above, e.g. a plain `git diff` ignores files
outside the sparsity patterns but will show diffs for paths outside the
sparsity patterns when revision arguments are passed. (Technically,
diff is treating the sparse paths as matching HEAD.) So, there is some
internal inconsistency among these commands. There are also additional
commands that should behave differently in the face of sparse-checkouts,
as the sparse-checkout documentation alludes to, but the above is
sufficient for me to explain how `git stash` is affected.
What is relevant here is that logically 'stash' should behave like a
merge; it three-way merges the changes the user had in progress at stash
creation time, the HEAD at the time the stash was created, and the
current HEAD, in order to get the stashed changes applied to the current
branch. However, this simplistic view doesn't quite work in practice,
because stash tweaks it a bit due to two factors: (1) flags like
--keep-index and --include-untracked (why we used two different verbs,
'keep' and 'include', is a rant for another day) modify what should be
staged at the end and include more things that should be quasi-merged,
(2) stash generally wants changes to NOT be staged. It only provides
exceptions when (a) some of the changes had conflicts and thus we want
to use stages to denote the clean merges and higher order stages to
mark the conflicts, or (b) if there is a brand new file we don't want
it to become untracked.
stash has traditionally gotten this special behavior by first doing a
merge, and then when it's clean, applying a pipeline of commands to
modify the result. This series of commands for
unstaging-non-newly-added-files came from the following commands:
git diff-index --cached --name-only --diff-filter=A $CTREE >"$a"
git read-tree --reset $CTREE
git update-index --add --stdin <"$a"
rm -f "$a"
Looking back at the different types of special sparsity handling listed
at the beginning of this message, you may note that we have at least one
of each type covered here: merge, diff-index, and read-tree. The weird
mix-and-match led to 3 different bugs:
(1) If a path merged cleanly and it didn't match the sparsity patterns,
the merge backend would know to avoid writing it to the working tree and
keep the SKIP_WORKTREE bit, simply only updating it in the index.
Unfortunately, the subsequent commands would essentially undo the
changes in the index and thus simply toss the changes altogether since
there was nothing left in the working tree. This means the stash is
only partially applied.
(2) If a path existed in the worktree before `git stash apply` despite
having the SKIP_WORKTREE bit set, then the `git read-tree --reset` would
print an error message of the form
error: Entry 'modified' not uptodate. Cannot merge.
and cause stash to abort early.
(3) If there was a brand new file added by the stash, then the
diff-index command would save that pathname to the temporary file, the
read-tree --reset would remove it from the index, and the update-index
command would barf due to no such file being present in the working
copy; it would print a message of the form:
error: NEWFILE: does not exist and --remove not passed
fatal: Unable to process path NEWFILE
and then cause stash to abort early.
Basically, the whole idea of unstage-unless-brand-new requires special
care when you are dealing with a sparse-checkout. Fix these problems
by applying the following simple rule:
When we unstage files, if they have the SKIP_WORKTREE bit set,
clear that bit and write the file out to the working directory.
(*) If there's already a file present in the way, rename it first.
This fixes all three problems in t7012.13 and allows us to mark it as
passing.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-12-01 23:25:18 +01:00
|
|
|
*
|
|
|
|
* However, if any SKIP_WORKTREE path is modified relative to
|
|
|
|
* orig_tree, then we want to clear the SKIP_WORKTREE bit and write
|
|
|
|
* it to the worktree before unstaging.
|
stash: remove unnecessary process forking
When stash was converted from shell to a builtin, it merely
transliterated the forking of various git commands from shell to a C
program that would fork the same commands. Some of those were converted
over to actual library calls, but much of the pipeline-of-commands
design still remains. Fix some of this by replacing the portion
corresponding to
git diff-index --cached --name-only --diff-filter=A $CTREE >"$a"
git read-tree --reset $CTREE
git update-index --add --stdin <"$a"
rm -f "$a"
into a library function that does the same thing. (The read-tree
--reset was already partially converted over to a library call, but as
an independent piece.) Note here that this came after a merge operation
was performed. The merge machinery always stages anything that cleanly
merges, and the above code only runs if there are no conflicts. Its
purpose is to make it so that when there are no conflicts, all the
changes from the stash are unstaged. However, that causes brand new
files from the stash to become untracked, so the code above first saves
those files off and then re-adds them afterwards.
We replace the whole series of commands with a simple function that will
unstage files that are not newly added. This doesn't fix any bugs in
the usage of these commands, it simply matches the existing behavior but
makes it into a single atomic operation that we can then operate on as a
whole. A subsequent commit will take advantage of this to fix issues
with these commands in sparse-checkouts.
This conversion incidentally fixes t3906.1, because the separate
update-index process would die with the following error messages:
error: uninitialized_sub: is a directory - add files inside instead
fatal: Unable to process path uninitialized_sub
The unstaging of the directory as a submodule meant it was no longer
tracked, and thus as an uninitialized directory it could not be added
back using `git update-index --add`, thus resulting in this error and
early abort. Most of the submodule tests in 3906 continue to fail after
this change, this change was just enough to push the first of those
tests to success.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-12-01 23:25:17 +01:00
|
|
|
*/
|
|
|
|
|
stash: fix stash application in sparse-checkouts
sparse-checkouts are built on the patterns in the
$GIT_DIR/info/sparse-checkout file, where commands have modified
behavior for paths that do not match those patterns. The differences in
behavior, as far as the bugs concerned here, fall into three different
categories (with git subcommands that fall into each category listed):
* commands that only look at files matching the patterns:
* status
* diff
* clean
* update-index
* commands that remove files from the working tree that do not match
the patterns, and restore files that do match them:
* read-tree
* switch
* checkout
* reset (--hard)
* commands that omit writing files to the working tree that do not
match the patterns, unless those files are not clean:
* merge
* rebase
* cherry-pick
* revert
There are some caveats above, e.g. a plain `git diff` ignores files
outside the sparsity patterns but will show diffs for paths outside the
sparsity patterns when revision arguments are passed. (Technically,
diff is treating the sparse paths as matching HEAD.) So, there is some
internal inconsistency among these commands. There are also additional
commands that should behave differently in the face of sparse-checkouts,
as the sparse-checkout documentation alludes to, but the above is
sufficient for me to explain how `git stash` is affected.
What is relevant here is that logically 'stash' should behave like a
merge; it three-way merges the changes the user had in progress at stash
creation time, the HEAD at the time the stash was created, and the
current HEAD, in order to get the stashed changes applied to the current
branch. However, this simplistic view doesn't quite work in practice,
because stash tweaks it a bit due to two factors: (1) flags like
--keep-index and --include-untracked (why we used two different verbs,
'keep' and 'include', is a rant for another day) modify what should be
staged at the end and include more things that should be quasi-merged,
(2) stash generally wants changes to NOT be staged. It only provides
exceptions when (a) some of the changes had conflicts and thus we want
to use stages to denote the clean merges and higher order stages to
mark the conflicts, or (b) if there is a brand new file we don't want
it to become untracked.
stash has traditionally gotten this special behavior by first doing a
merge, and then when it's clean, applying a pipeline of commands to
modify the result. This series of commands for
unstaging-non-newly-added-files came from the following commands:
git diff-index --cached --name-only --diff-filter=A $CTREE >"$a"
git read-tree --reset $CTREE
git update-index --add --stdin <"$a"
rm -f "$a"
Looking back at the different types of special sparsity handling listed
at the beginning of this message, you may note that we have at least one
of each type covered here: merge, diff-index, and read-tree. The weird
mix-and-match led to 3 different bugs:
(1) If a path merged cleanly and it didn't match the sparsity patterns,
the merge backend would know to avoid writing it to the working tree and
keep the SKIP_WORKTREE bit, simply only updating it in the index.
Unfortunately, the subsequent commands would essentially undo the
changes in the index and thus simply toss the changes altogether since
there was nothing left in the working tree. This means the stash is
only partially applied.
(2) If a path existed in the worktree before `git stash apply` despite
having the SKIP_WORKTREE bit set, then the `git read-tree --reset` would
print an error message of the form
error: Entry 'modified' not uptodate. Cannot merge.
and cause stash to abort early.
(3) If there was a brand new file added by the stash, then the
diff-index command would save that pathname to the temporary file, the
read-tree --reset would remove it from the index, and the update-index
command would barf due to no such file being present in the working
copy; it would print a message of the form:
error: NEWFILE: does not exist and --remove not passed
fatal: Unable to process path NEWFILE
and then cause stash to abort early.
Basically, the whole idea of unstage-unless-brand-new requires special
care when you are dealing with a sparse-checkout. Fix these problems
by applying the following simple rule:
When we unstage files, if they have the SKIP_WORKTREE bit set,
clear that bit and write the file out to the working directory.
(*) If there's already a file present in the way, rename it first.
This fixes all three problems in t7012.13 and allows us to mark it as
passing.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-12-01 23:25:18 +01:00
|
|
|
struct checkout state = CHECKOUT_INIT;
|
stash: remove unnecessary process forking
When stash was converted from shell to a builtin, it merely
transliterated the forking of various git commands from shell to a C
program that would fork the same commands. Some of those were converted
over to actual library calls, but much of the pipeline-of-commands
design still remains. Fix some of this by replacing the portion
corresponding to
git diff-index --cached --name-only --diff-filter=A $CTREE >"$a"
git read-tree --reset $CTREE
git update-index --add --stdin <"$a"
rm -f "$a"
into a library function that does the same thing. (The read-tree
--reset was already partially converted over to a library call, but as
an independent piece.) Note here that this came after a merge operation
was performed. The merge machinery always stages anything that cleanly
merges, and the above code only runs if there are no conflicts. Its
purpose is to make it so that when there are no conflicts, all the
changes from the stash are unstaged. However, that causes brand new
files from the stash to become untracked, so the code above first saves
those files off and then re-adds them afterwards.
We replace the whole series of commands with a simple function that will
unstage files that are not newly added. This doesn't fix any bugs in
the usage of these commands, it simply matches the existing behavior but
makes it into a single atomic operation that we can then operate on as a
whole. A subsequent commit will take advantage of this to fix issues
with these commands in sparse-checkouts.
This conversion incidentally fixes t3906.1, because the separate
update-index process would die with the following error messages:
error: uninitialized_sub: is a directory - add files inside instead
fatal: Unable to process path uninitialized_sub
The unstaging of the directory as a submodule meant it was no longer
tracked, and thus as an uninitialized directory it could not be added
back using `git update-index --add`, thus resulting in this error and
early abort. Most of the submodule tests in 3906 continue to fail after
this change, this change was just enough to push the first of those
tests to success.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-12-01 23:25:17 +01:00
|
|
|
struct diff_options diff_opts;
|
|
|
|
struct lock_file lock = LOCK_INIT;
|
|
|
|
int i;
|
|
|
|
|
stash: fix stash application in sparse-checkouts
sparse-checkouts are built on the patterns in the
$GIT_DIR/info/sparse-checkout file, where commands have modified
behavior for paths that do not match those patterns. The differences in
behavior, as far as the bugs concerned here, fall into three different
categories (with git subcommands that fall into each category listed):
* commands that only look at files matching the patterns:
* status
* diff
* clean
* update-index
* commands that remove files from the working tree that do not match
the patterns, and restore files that do match them:
* read-tree
* switch
* checkout
* reset (--hard)
* commands that omit writing files to the working tree that do not
match the patterns, unless those files are not clean:
* merge
* rebase
* cherry-pick
* revert
There are some caveats above, e.g. a plain `git diff` ignores files
outside the sparsity patterns but will show diffs for paths outside the
sparsity patterns when revision arguments are passed. (Technically,
diff is treating the sparse paths as matching HEAD.) So, there is some
internal inconsistency among these commands. There are also additional
commands that should behave differently in the face of sparse-checkouts,
as the sparse-checkout documentation alludes to, but the above is
sufficient for me to explain how `git stash` is affected.
What is relevant here is that logically 'stash' should behave like a
merge; it three-way merges the changes the user had in progress at stash
creation time, the HEAD at the time the stash was created, and the
current HEAD, in order to get the stashed changes applied to the current
branch. However, this simplistic view doesn't quite work in practice,
because stash tweaks it a bit due to two factors: (1) flags like
--keep-index and --include-untracked (why we used two different verbs,
'keep' and 'include', is a rant for another day) modify what should be
staged at the end and include more things that should be quasi-merged,
(2) stash generally wants changes to NOT be staged. It only provides
exceptions when (a) some of the changes had conflicts and thus we want
to use stages to denote the clean merges and higher order stages to
mark the conflicts, or (b) if there is a brand new file we don't want
it to become untracked.
stash has traditionally gotten this special behavior by first doing a
merge, and then when it's clean, applying a pipeline of commands to
modify the result. This series of commands for
unstaging-non-newly-added-files came from the following commands:
git diff-index --cached --name-only --diff-filter=A $CTREE >"$a"
git read-tree --reset $CTREE
git update-index --add --stdin <"$a"
rm -f "$a"
Looking back at the different types of special sparsity handling listed
at the beginning of this message, you may note that we have at least one
of each type covered here: merge, diff-index, and read-tree. The weird
mix-and-match led to 3 different bugs:
(1) If a path merged cleanly and it didn't match the sparsity patterns,
the merge backend would know to avoid writing it to the working tree and
keep the SKIP_WORKTREE bit, simply only updating it in the index.
Unfortunately, the subsequent commands would essentially undo the
changes in the index and thus simply toss the changes altogether since
there was nothing left in the working tree. This means the stash is
only partially applied.
(2) If a path existed in the worktree before `git stash apply` despite
having the SKIP_WORKTREE bit set, then the `git read-tree --reset` would
print an error message of the form
error: Entry 'modified' not uptodate. Cannot merge.
and cause stash to abort early.
(3) If there was a brand new file added by the stash, then the
diff-index command would save that pathname to the temporary file, the
read-tree --reset would remove it from the index, and the update-index
command would barf due to no such file being present in the working
copy; it would print a message of the form:
error: NEWFILE: does not exist and --remove not passed
fatal: Unable to process path NEWFILE
and then cause stash to abort early.
Basically, the whole idea of unstage-unless-brand-new requires special
care when you are dealing with a sparse-checkout. Fix these problems
by applying the following simple rule:
When we unstage files, if they have the SKIP_WORKTREE bit set,
clear that bit and write the file out to the working directory.
(*) If there's already a file present in the way, rename it first.
This fixes all three problems in t7012.13 and allows us to mark it as
passing.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-12-01 23:25:18 +01:00
|
|
|
/* If any entries have skip_worktree set, we'll have to check 'em out */
|
|
|
|
state.force = 1;
|
|
|
|
state.quiet = 1;
|
|
|
|
state.refresh_cache = 1;
|
|
|
|
state.istate = &the_index;
|
|
|
|
|
stash: remove unnecessary process forking
When stash was converted from shell to a builtin, it merely
transliterated the forking of various git commands from shell to a C
program that would fork the same commands. Some of those were converted
over to actual library calls, but much of the pipeline-of-commands
design still remains. Fix some of this by replacing the portion
corresponding to
git diff-index --cached --name-only --diff-filter=A $CTREE >"$a"
git read-tree --reset $CTREE
git update-index --add --stdin <"$a"
rm -f "$a"
into a library function that does the same thing. (The read-tree
--reset was already partially converted over to a library call, but as
an independent piece.) Note here that this came after a merge operation
was performed. The merge machinery always stages anything that cleanly
merges, and the above code only runs if there are no conflicts. Its
purpose is to make it so that when there are no conflicts, all the
changes from the stash are unstaged. However, that causes brand new
files from the stash to become untracked, so the code above first saves
those files off and then re-adds them afterwards.
We replace the whole series of commands with a simple function that will
unstage files that are not newly added. This doesn't fix any bugs in
the usage of these commands, it simply matches the existing behavior but
makes it into a single atomic operation that we can then operate on as a
whole. A subsequent commit will take advantage of this to fix issues
with these commands in sparse-checkouts.
This conversion incidentally fixes t3906.1, because the separate
update-index process would die with the following error messages:
error: uninitialized_sub: is a directory - add files inside instead
fatal: Unable to process path uninitialized_sub
The unstaging of the directory as a submodule meant it was no longer
tracked, and thus as an uninitialized directory it could not be added
back using `git update-index --add`, thus resulting in this error and
early abort. Most of the submodule tests in 3906 continue to fail after
this change, this change was just enough to push the first of those
tests to success.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-12-01 23:25:17 +01:00
|
|
|
/*
|
|
|
|
* Step 1: get a difference between orig_tree (which corresponding
|
|
|
|
* to the index before a merge was run) and the current index
|
|
|
|
* (reflecting the changes brought in by the merge).
|
|
|
|
*/
|
|
|
|
diff_setup(&diff_opts);
|
|
|
|
diff_opts.flags.recursive = 1;
|
|
|
|
diff_opts.detect_rename = 0;
|
|
|
|
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
|
|
|
diff_setup_done(&diff_opts);
|
|
|
|
|
|
|
|
do_diff_cache(orig_tree, &diff_opts);
|
|
|
|
diffcore_std(&diff_opts);
|
|
|
|
|
|
|
|
/* Iterate over the paths that changed due to the merge... */
|
|
|
|
for (i = 0; i < diff_queued_diff.nr; i++) {
|
|
|
|
struct diff_filepair *p;
|
|
|
|
struct cache_entry *ce;
|
|
|
|
int pos;
|
|
|
|
|
|
|
|
/* Look up the path's position in the current index. */
|
|
|
|
p = diff_queued_diff.queue[i];
|
|
|
|
pos = index_name_pos(&the_index, p->two->path,
|
|
|
|
strlen(p->two->path));
|
|
|
|
|
|
|
|
/*
|
stash: fix stash application in sparse-checkouts
sparse-checkouts are built on the patterns in the
$GIT_DIR/info/sparse-checkout file, where commands have modified
behavior for paths that do not match those patterns. The differences in
behavior, as far as the bugs concerned here, fall into three different
categories (with git subcommands that fall into each category listed):
* commands that only look at files matching the patterns:
* status
* diff
* clean
* update-index
* commands that remove files from the working tree that do not match
the patterns, and restore files that do match them:
* read-tree
* switch
* checkout
* reset (--hard)
* commands that omit writing files to the working tree that do not
match the patterns, unless those files are not clean:
* merge
* rebase
* cherry-pick
* revert
There are some caveats above, e.g. a plain `git diff` ignores files
outside the sparsity patterns but will show diffs for paths outside the
sparsity patterns when revision arguments are passed. (Technically,
diff is treating the sparse paths as matching HEAD.) So, there is some
internal inconsistency among these commands. There are also additional
commands that should behave differently in the face of sparse-checkouts,
as the sparse-checkout documentation alludes to, but the above is
sufficient for me to explain how `git stash` is affected.
What is relevant here is that logically 'stash' should behave like a
merge; it three-way merges the changes the user had in progress at stash
creation time, the HEAD at the time the stash was created, and the
current HEAD, in order to get the stashed changes applied to the current
branch. However, this simplistic view doesn't quite work in practice,
because stash tweaks it a bit due to two factors: (1) flags like
--keep-index and --include-untracked (why we used two different verbs,
'keep' and 'include', is a rant for another day) modify what should be
staged at the end and include more things that should be quasi-merged,
(2) stash generally wants changes to NOT be staged. It only provides
exceptions when (a) some of the changes had conflicts and thus we want
to use stages to denote the clean merges and higher order stages to
mark the conflicts, or (b) if there is a brand new file we don't want
it to become untracked.
stash has traditionally gotten this special behavior by first doing a
merge, and then when it's clean, applying a pipeline of commands to
modify the result. This series of commands for
unstaging-non-newly-added-files came from the following commands:
git diff-index --cached --name-only --diff-filter=A $CTREE >"$a"
git read-tree --reset $CTREE
git update-index --add --stdin <"$a"
rm -f "$a"
Looking back at the different types of special sparsity handling listed
at the beginning of this message, you may note that we have at least one
of each type covered here: merge, diff-index, and read-tree. The weird
mix-and-match led to 3 different bugs:
(1) If a path merged cleanly and it didn't match the sparsity patterns,
the merge backend would know to avoid writing it to the working tree and
keep the SKIP_WORKTREE bit, simply only updating it in the index.
Unfortunately, the subsequent commands would essentially undo the
changes in the index and thus simply toss the changes altogether since
there was nothing left in the working tree. This means the stash is
only partially applied.
(2) If a path existed in the worktree before `git stash apply` despite
having the SKIP_WORKTREE bit set, then the `git read-tree --reset` would
print an error message of the form
error: Entry 'modified' not uptodate. Cannot merge.
and cause stash to abort early.
(3) If there was a brand new file added by the stash, then the
diff-index command would save that pathname to the temporary file, the
read-tree --reset would remove it from the index, and the update-index
command would barf due to no such file being present in the working
copy; it would print a message of the form:
error: NEWFILE: does not exist and --remove not passed
fatal: Unable to process path NEWFILE
and then cause stash to abort early.
Basically, the whole idea of unstage-unless-brand-new requires special
care when you are dealing with a sparse-checkout. Fix these problems
by applying the following simple rule:
When we unstage files, if they have the SKIP_WORKTREE bit set,
clear that bit and write the file out to the working directory.
(*) If there's already a file present in the way, rename it first.
This fixes all three problems in t7012.13 and allows us to mark it as
passing.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-12-01 23:25:18 +01:00
|
|
|
* Step 2: Place changes in the working tree
|
|
|
|
*
|
|
|
|
* Stash is about restoring changes *to the working tree*.
|
|
|
|
* So if the merge successfully got a new version of some
|
|
|
|
* path, but left it out of the working tree, then clear the
|
|
|
|
* SKIP_WORKTREE bit and write it to the working tree.
|
|
|
|
*/
|
|
|
|
if (pos >= 0 && ce_skip_worktree(active_cache[pos])) {
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
ce = active_cache[pos];
|
|
|
|
if (!lstat(ce->name, &st)) {
|
|
|
|
/* Conflicting path present; relocate it */
|
|
|
|
struct strbuf new_path = STRBUF_INIT;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
strbuf_addf(&new_path,
|
|
|
|
"%s.stash.XXXXXX", ce->name);
|
|
|
|
fd = xmkstemp(new_path.buf);
|
|
|
|
close(fd);
|
|
|
|
printf(_("WARNING: Untracked file in way of "
|
|
|
|
"tracked file! Renaming\n "
|
|
|
|
" %s -> %s\n"
|
|
|
|
" to make room.\n"),
|
|
|
|
ce->name, new_path.buf);
|
|
|
|
if (rename(ce->name, new_path.buf))
|
|
|
|
die("Failed to move %s to %s\n",
|
|
|
|
ce->name, new_path.buf);
|
|
|
|
strbuf_release(&new_path);
|
|
|
|
}
|
|
|
|
checkout_entry(ce, &state, NULL, NULL);
|
|
|
|
ce->ce_flags &= ~CE_SKIP_WORKTREE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Step 3: "unstage" changes, as long as they are still tracked
|
stash: remove unnecessary process forking
When stash was converted from shell to a builtin, it merely
transliterated the forking of various git commands from shell to a C
program that would fork the same commands. Some of those were converted
over to actual library calls, but much of the pipeline-of-commands
design still remains. Fix some of this by replacing the portion
corresponding to
git diff-index --cached --name-only --diff-filter=A $CTREE >"$a"
git read-tree --reset $CTREE
git update-index --add --stdin <"$a"
rm -f "$a"
into a library function that does the same thing. (The read-tree
--reset was already partially converted over to a library call, but as
an independent piece.) Note here that this came after a merge operation
was performed. The merge machinery always stages anything that cleanly
merges, and the above code only runs if there are no conflicts. Its
purpose is to make it so that when there are no conflicts, all the
changes from the stash are unstaged. However, that causes brand new
files from the stash to become untracked, so the code above first saves
those files off and then re-adds them afterwards.
We replace the whole series of commands with a simple function that will
unstage files that are not newly added. This doesn't fix any bugs in
the usage of these commands, it simply matches the existing behavior but
makes it into a single atomic operation that we can then operate on as a
whole. A subsequent commit will take advantage of this to fix issues
with these commands in sparse-checkouts.
This conversion incidentally fixes t3906.1, because the separate
update-index process would die with the following error messages:
error: uninitialized_sub: is a directory - add files inside instead
fatal: Unable to process path uninitialized_sub
The unstaging of the directory as a submodule meant it was no longer
tracked, and thus as an uninitialized directory it could not be added
back using `git update-index --add`, thus resulting in this error and
early abort. Most of the submodule tests in 3906 continue to fail after
this change, this change was just enough to push the first of those
tests to success.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-12-01 23:25:17 +01:00
|
|
|
*/
|
|
|
|
if (p->one->oid_valid) {
|
|
|
|
/*
|
|
|
|
* Path existed in orig_tree; restore index entry
|
|
|
|
* from that tree in order to "unstage" the changes.
|
|
|
|
*/
|
|
|
|
int option = ADD_CACHE_OK_TO_REPLACE;
|
|
|
|
if (pos < 0)
|
|
|
|
option = ADD_CACHE_OK_TO_ADD;
|
|
|
|
|
|
|
|
ce = make_cache_entry(&the_index,
|
|
|
|
p->one->mode,
|
|
|
|
&p->one->oid,
|
|
|
|
p->one->path,
|
|
|
|
0, 0);
|
|
|
|
add_index_entry(&the_index, ce, option);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
diff_flush(&diff_opts);
|
|
|
|
|
|
|
|
/*
|
stash: fix stash application in sparse-checkouts
sparse-checkouts are built on the patterns in the
$GIT_DIR/info/sparse-checkout file, where commands have modified
behavior for paths that do not match those patterns. The differences in
behavior, as far as the bugs concerned here, fall into three different
categories (with git subcommands that fall into each category listed):
* commands that only look at files matching the patterns:
* status
* diff
* clean
* update-index
* commands that remove files from the working tree that do not match
the patterns, and restore files that do match them:
* read-tree
* switch
* checkout
* reset (--hard)
* commands that omit writing files to the working tree that do not
match the patterns, unless those files are not clean:
* merge
* rebase
* cherry-pick
* revert
There are some caveats above, e.g. a plain `git diff` ignores files
outside the sparsity patterns but will show diffs for paths outside the
sparsity patterns when revision arguments are passed. (Technically,
diff is treating the sparse paths as matching HEAD.) So, there is some
internal inconsistency among these commands. There are also additional
commands that should behave differently in the face of sparse-checkouts,
as the sparse-checkout documentation alludes to, but the above is
sufficient for me to explain how `git stash` is affected.
What is relevant here is that logically 'stash' should behave like a
merge; it three-way merges the changes the user had in progress at stash
creation time, the HEAD at the time the stash was created, and the
current HEAD, in order to get the stashed changes applied to the current
branch. However, this simplistic view doesn't quite work in practice,
because stash tweaks it a bit due to two factors: (1) flags like
--keep-index and --include-untracked (why we used two different verbs,
'keep' and 'include', is a rant for another day) modify what should be
staged at the end and include more things that should be quasi-merged,
(2) stash generally wants changes to NOT be staged. It only provides
exceptions when (a) some of the changes had conflicts and thus we want
to use stages to denote the clean merges and higher order stages to
mark the conflicts, or (b) if there is a brand new file we don't want
it to become untracked.
stash has traditionally gotten this special behavior by first doing a
merge, and then when it's clean, applying a pipeline of commands to
modify the result. This series of commands for
unstaging-non-newly-added-files came from the following commands:
git diff-index --cached --name-only --diff-filter=A $CTREE >"$a"
git read-tree --reset $CTREE
git update-index --add --stdin <"$a"
rm -f "$a"
Looking back at the different types of special sparsity handling listed
at the beginning of this message, you may note that we have at least one
of each type covered here: merge, diff-index, and read-tree. The weird
mix-and-match led to 3 different bugs:
(1) If a path merged cleanly and it didn't match the sparsity patterns,
the merge backend would know to avoid writing it to the working tree and
keep the SKIP_WORKTREE bit, simply only updating it in the index.
Unfortunately, the subsequent commands would essentially undo the
changes in the index and thus simply toss the changes altogether since
there was nothing left in the working tree. This means the stash is
only partially applied.
(2) If a path existed in the worktree before `git stash apply` despite
having the SKIP_WORKTREE bit set, then the `git read-tree --reset` would
print an error message of the form
error: Entry 'modified' not uptodate. Cannot merge.
and cause stash to abort early.
(3) If there was a brand new file added by the stash, then the
diff-index command would save that pathname to the temporary file, the
read-tree --reset would remove it from the index, and the update-index
command would barf due to no such file being present in the working
copy; it would print a message of the form:
error: NEWFILE: does not exist and --remove not passed
fatal: Unable to process path NEWFILE
and then cause stash to abort early.
Basically, the whole idea of unstage-unless-brand-new requires special
care when you are dealing with a sparse-checkout. Fix these problems
by applying the following simple rule:
When we unstage files, if they have the SKIP_WORKTREE bit set,
clear that bit and write the file out to the working directory.
(*) If there's already a file present in the way, rename it first.
This fixes all three problems in t7012.13 and allows us to mark it as
passing.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-12-01 23:25:18 +01:00
|
|
|
* Step 4: write the new index to disk
|
stash: remove unnecessary process forking
When stash was converted from shell to a builtin, it merely
transliterated the forking of various git commands from shell to a C
program that would fork the same commands. Some of those were converted
over to actual library calls, but much of the pipeline-of-commands
design still remains. Fix some of this by replacing the portion
corresponding to
git diff-index --cached --name-only --diff-filter=A $CTREE >"$a"
git read-tree --reset $CTREE
git update-index --add --stdin <"$a"
rm -f "$a"
into a library function that does the same thing. (The read-tree
--reset was already partially converted over to a library call, but as
an independent piece.) Note here that this came after a merge operation
was performed. The merge machinery always stages anything that cleanly
merges, and the above code only runs if there are no conflicts. Its
purpose is to make it so that when there are no conflicts, all the
changes from the stash are unstaged. However, that causes brand new
files from the stash to become untracked, so the code above first saves
those files off and then re-adds them afterwards.
We replace the whole series of commands with a simple function that will
unstage files that are not newly added. This doesn't fix any bugs in
the usage of these commands, it simply matches the existing behavior but
makes it into a single atomic operation that we can then operate on as a
whole. A subsequent commit will take advantage of this to fix issues
with these commands in sparse-checkouts.
This conversion incidentally fixes t3906.1, because the separate
update-index process would die with the following error messages:
error: uninitialized_sub: is a directory - add files inside instead
fatal: Unable to process path uninitialized_sub
The unstaging of the directory as a submodule meant it was no longer
tracked, and thus as an uninitialized directory it could not be added
back using `git update-index --add`, thus resulting in this error and
early abort. Most of the submodule tests in 3906 continue to fail after
this change, this change was just enough to push the first of those
tests to success.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-12-01 23:25:17 +01:00
|
|
|
*/
|
|
|
|
repo_hold_locked_index(the_repository, &lock, LOCK_DIE_ON_ERROR);
|
|
|
|
if (write_locked_index(&the_index, &lock,
|
|
|
|
COMMIT_LOCK | SKIP_IF_UNCHANGED))
|
|
|
|
die(_("Unable to write index."));
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:15 +01:00
|
|
|
static int do_apply_stash(const char *prefix, struct stash_info *info,
|
|
|
|
int index, int quiet)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int has_index = index;
|
|
|
|
struct merge_options o;
|
|
|
|
struct object_id c_tree;
|
|
|
|
struct object_id index_tree;
|
|
|
|
struct commit *result;
|
|
|
|
const struct object_id *bases[1];
|
|
|
|
|
|
|
|
read_cache_preload(NULL);
|
2019-09-11 20:20:27 +02:00
|
|
|
if (refresh_and_write_cache(REFRESH_QUIET, 0, 0))
|
2019-02-26 00:16:15 +01:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (write_cache_as_tree(&c_tree, 0, NULL))
|
|
|
|
return error(_("cannot apply a stash in the middle of a merge"));
|
|
|
|
|
|
|
|
if (index) {
|
|
|
|
if (oideq(&info->b_tree, &info->i_tree) ||
|
|
|
|
oideq(&c_tree, &info->i_tree)) {
|
|
|
|
has_index = 0;
|
|
|
|
} else {
|
|
|
|
struct strbuf out = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (diff_tree_binary(&out, &info->w_commit)) {
|
|
|
|
strbuf_release(&out);
|
|
|
|
return error(_("could not generate diff %s^!."),
|
|
|
|
oid_to_hex(&info->w_commit));
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = apply_cached(&out);
|
|
|
|
strbuf_release(&out);
|
|
|
|
if (ret)
|
2020-11-24 01:52:12 +01:00
|
|
|
return error(_("conflicts in index. "
|
2019-02-26 00:16:15 +01:00
|
|
|
"Try without --index."));
|
|
|
|
|
|
|
|
discard_cache();
|
|
|
|
read_cache();
|
|
|
|
if (write_cache_as_tree(&index_tree, 0, NULL))
|
|
|
|
return error(_("could not save index tree"));
|
|
|
|
|
|
|
|
reset_head();
|
Ensure index matches head before invoking merge machinery, round N
This is the bug that just won't die; there always seems to be another
form of it somewhere. See the commit message of 55f39cf7551b ("merge:
fix misleading pre-merge check documentation", 2018-06-30) for a more
detailed explanation), but in short:
<quick summary>
builtin/merge.c contains this important requirement for merge
strategies:
...the index must be in sync with the head commit. The strategies are
responsible to ensure this.
This condition is important to enforce because there are two likely
failure cases when the index isn't in sync with the head commit:
* we silently throw away changes the user had staged before the merge
* we accidentally (and silently) include changes in the merge that
were not part of either of the branches/trees being merged
Discarding users' work and mis-merging are both bad outcomes, especially
when done silently, so naturally this rule was stated sternly -- but,
unfortunately totally ignored in practice unless and until actual bugs
were found. But, fear not: the bugs from this were fixed in commit
ee6566e8d70d ("[PATCH] Rewrite read-tree", 2005-09-05)
through a rewrite of read-tree (again, commit 55f39cf7551b has a more
detailed explanation of how this affected merge). And it was fixed
again in commit
160252f81626 ("git-merge-ours: make sure our index matches HEAD", 2005-11-03)
...and it was fixed again in commit
3ec62ad9ffba ("merge-octopus: abort if index does not match HEAD", 2016-04-09)
...and again in commit
65170c07d466 ("merge-recursive: avoid incorporating uncommitted changes in a merge", 2017-12-21)
...and again in commit
eddd1a411d93 ("merge-recursive: enforce rule that index matches head before merging", 2018-06-30)
...with multiple testcases added to the testsuite that could be
enumerated in even more commits.
Then, finally, in a patch in the same series as the last fix above, the
documentation about this requirement was fixed in commit 55f39cf7551b
("merge: fix misleading pre-merge check documentation", 2018-06-30), and
we all lived happily ever after...
</quick summary>
Unfortunately, "ever after" apparently denotes a limited time and it
expired today. The merge-recursive rule to enforce that index matches
head was at the beginning of merge_trees() and would only trigger when
opt->call_depth was 0. Since merge_recursive() doesn't call
merge_trees() until after returning from recursing, this meant that the
check wasn't triggered by merge_recursive() until it had first finished
all the intermediate merges to create virtual merge bases. That is a
potentially HUGE amount of computation (and writing of intermediate
merge results into the .git/objects directory) before it errors out and
says, in effect, "Sorry, I can't do any merging because you have some
local changes that would be overwritten."
Trying to enforce that all of merge_trees(), merge_recursive(), and
merge_recursive_generic() checked the index == head condition earlier
resulted in a bunch of broken tests. It turns out that
merge_recursive() has code to drop and reload the cache while recursing
to create intermediate virtual merge bases, but unfortunately that code
runs even when no recursion is necessary. This unconditional dropping
and reloading of the cache masked a few bugs:
* builtin/merge-recursive.c: didn't even bother loading the index.
* builtin/stash.c: feels like a fake 'builtin' because it repeatedly
invokes git subprocesses all over the place, mixed with other
operations. In particular, invoking "git reset" will reset the
index on disk, but the parent process that invoked it won't
automatically have its in-memory index updated.
* t3030-merge-recursive.h: this test has always been broken in that it
didn't make sure to make index match head before running. But, it
didn't care about the index or even the merge result, just the
verbose output while running. While commit eddd1a411d93
("merge-recursive: enforce rule that index matches head before
merging", 2018-06-30) should have uncovered this broken test, it
used a test_must_fail wrapper around the merge-recursive call
because it was known that the merge resulted in a rename/rename
conflict. Thus, that fix only made this test fail for a different
reason, and since the index == head check didn't happen until after
coming all the way back out of the recursion, the testcase had
enough information to pass the one check that it did perform.
So, load the index in builtin/merge-recursive.c, reload the in-memory
index in builtin/stash.c, and modify the t3030 testcase to correctly
setup the index and make sure that the test fails in the expected way
(meaning it reports a rename/rename conflict). This makes sure that
all callers actually make the index match head. The next commit will
then enforce the condition that index matches head earlier so this
problem doesn't return in the future.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-08-17 20:41:28 +02:00
|
|
|
discard_cache();
|
|
|
|
read_cache();
|
2019-02-26 00:16:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-22 04:14:43 +02:00
|
|
|
init_merge_options(&o, the_repository);
|
2019-02-26 00:16:15 +01:00
|
|
|
|
|
|
|
o.branch1 = "Updated upstream";
|
|
|
|
o.branch2 = "Stashed changes";
|
|
|
|
|
|
|
|
if (oideq(&info->b_tree, &c_tree))
|
|
|
|
o.branch1 = "Version stash was based on";
|
|
|
|
|
|
|
|
if (quiet)
|
|
|
|
o.verbosity = 0;
|
|
|
|
|
|
|
|
if (o.verbosity >= 3)
|
|
|
|
printf_ln(_("Merging %s with %s"), o.branch1, o.branch2);
|
|
|
|
|
|
|
|
bases[0] = &info->b_tree;
|
|
|
|
|
|
|
|
ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,
|
|
|
|
&result);
|
|
|
|
if (ret) {
|
|
|
|
rerere(0);
|
|
|
|
|
|
|
|
if (index)
|
|
|
|
fprintf_ln(stderr, _("Index was not unstashed."));
|
|
|
|
|
2022-01-05 00:04:58 +01:00
|
|
|
goto restore_untracked;
|
2019-02-26 00:16:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (has_index) {
|
|
|
|
if (reset_tree(&index_tree, 0, 0))
|
2022-01-05 00:04:58 +01:00
|
|
|
ret = -1;
|
2019-02-26 00:16:15 +01:00
|
|
|
} else {
|
stash: remove unnecessary process forking
When stash was converted from shell to a builtin, it merely
transliterated the forking of various git commands from shell to a C
program that would fork the same commands. Some of those were converted
over to actual library calls, but much of the pipeline-of-commands
design still remains. Fix some of this by replacing the portion
corresponding to
git diff-index --cached --name-only --diff-filter=A $CTREE >"$a"
git read-tree --reset $CTREE
git update-index --add --stdin <"$a"
rm -f "$a"
into a library function that does the same thing. (The read-tree
--reset was already partially converted over to a library call, but as
an independent piece.) Note here that this came after a merge operation
was performed. The merge machinery always stages anything that cleanly
merges, and the above code only runs if there are no conflicts. Its
purpose is to make it so that when there are no conflicts, all the
changes from the stash are unstaged. However, that causes brand new
files from the stash to become untracked, so the code above first saves
those files off and then re-adds them afterwards.
We replace the whole series of commands with a simple function that will
unstage files that are not newly added. This doesn't fix any bugs in
the usage of these commands, it simply matches the existing behavior but
makes it into a single atomic operation that we can then operate on as a
whole. A subsequent commit will take advantage of this to fix issues
with these commands in sparse-checkouts.
This conversion incidentally fixes t3906.1, because the separate
update-index process would die with the following error messages:
error: uninitialized_sub: is a directory - add files inside instead
fatal: Unable to process path uninitialized_sub
The unstaging of the directory as a submodule meant it was no longer
tracked, and thus as an uninitialized directory it could not be added
back using `git update-index --add`, thus resulting in this error and
early abort. Most of the submodule tests in 3906 continue to fail after
this change, this change was just enough to push the first of those
tests to success.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-12-01 23:25:17 +01:00
|
|
|
unstage_changes_unless_new(&c_tree);
|
2019-02-26 00:16:15 +01:00
|
|
|
}
|
|
|
|
|
2022-01-05 00:04:58 +01:00
|
|
|
restore_untracked:
|
2021-09-10 12:29:56 +02:00
|
|
|
if (info->has_u && restore_untracked(&info->u_tree))
|
2022-01-05 00:04:58 +01:00
|
|
|
ret = error(_("could not restore untracked files from stash"));
|
2021-09-10 12:29:56 +02:00
|
|
|
|
stash: make sure we have a valid index before writing it
In 'do_apply_stash()' we refresh the index in the end. Since
34933d0eff ("stash: make sure to write refreshed cache", 2019-09-11),
we also write that refreshed index when --quiet is given to 'git stash
apply'.
However if '--index' is not given to 'git stash apply', we also
discard the index in the else clause just before. We need to do so
because we use an external 'git update-index --add --stdin', which
leads to an out of date in-core index.
Later we call 'refresh_and_write_cache', which now leads to writing
the discarded index, which means we essentially write an empty index
file. This is obviously not correct, or the behaviour the user
wanted. We should not modify the users index without being asked to
do so.
Make sure to re-read the index after discarding the current in-core
index, to avoid dealing with outdated information. Instead we could
also drop the 'discard_cache()' + 'read_cache()', however that would
make it easy to fall into the same trap as 34933d0eff did, so it's
better to avoid that.
We can also drop the 'refresh_and_write_cache' completely in the quiet
case. Previously in legacy stash we relied on 'git status' to refresh
the index after calling 'git read-tree' when '--index' was passed to
'git apply'. However the 'reset_tree()' call that replaced 'git
read-tree' always passes options that are equivalent to '-m', making
the refresh of the index unnecessary.
Reported-by: Grzegorz Rajchman <rayman17@gmail.com>
Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-13 16:01:36 +01:00
|
|
|
if (!quiet) {
|
2019-02-26 00:16:15 +01:00
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Status is quite simple and could be replaced with calls to
|
|
|
|
* wt_status in the future, but it adds complexities which may
|
|
|
|
* require more tests.
|
|
|
|
*/
|
|
|
|
cp.git_cmd = 1;
|
|
|
|
cp.dir = prefix;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushf(&cp.env_array, GIT_WORK_TREE_ENVIRONMENT"=%s",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
absolute_path(get_git_work_tree()));
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushf(&cp.env_array, GIT_DIR_ENVIRONMENT"=%s",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
absolute_path(get_git_dir()));
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(&cp.args, "status");
|
2019-02-26 00:16:15 +01:00
|
|
|
run_command(&cp);
|
|
|
|
}
|
|
|
|
|
2022-01-05 00:04:58 +01:00
|
|
|
return ret;
|
2019-02-26 00:16:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int apply_stash(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int quiet = 0;
|
|
|
|
int index = 0;
|
|
|
|
struct stash_info info;
|
|
|
|
struct option options[] = {
|
|
|
|
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
|
|
|
|
OPT_BOOL(0, "index", &index,
|
|
|
|
N_("attempt to recreate the index")),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
argc = parse_options(argc, argv, prefix, options,
|
2019-02-26 00:16:28 +01:00
|
|
|
git_stash_apply_usage, 0);
|
2019-02-26 00:16:15 +01:00
|
|
|
|
|
|
|
if (get_stash_info(&info, argc, argv))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
ret = do_apply_stash(prefix, &info, index, quiet);
|
|
|
|
free_stash_info(&info);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-10-24 19:06:48 +02:00
|
|
|
static int reject_reflog_ent(struct object_id *ooid, struct object_id *noid,
|
|
|
|
const char *email, timestamp_t timestamp, int tz,
|
|
|
|
const char *message, void *cb_data)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int reflog_is_empty(const char *refname)
|
|
|
|
{
|
|
|
|
return !for_each_reflog_ent(refname, reject_reflog_ent, NULL);
|
|
|
|
}
|
|
|
|
|
2019-03-09 19:30:21 +01:00
|
|
|
static int do_drop_stash(struct stash_info *info, int quiet)
|
2019-02-26 00:16:16 +01:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct child_process cp_reflog = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* reflog does not provide a simple function for deleting refs. One will
|
|
|
|
* need to be added to avoid implementing too much reflog code here
|
|
|
|
*/
|
|
|
|
|
|
|
|
cp_reflog.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
"--rewrite", NULL);
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(&cp_reflog.args, info->revision.buf);
|
2019-02-26 00:16:16 +01:00
|
|
|
ret = run_command(&cp_reflog);
|
|
|
|
if (!ret) {
|
|
|
|
if (!quiet)
|
|
|
|
printf_ln(_("Dropped %s (%s)"), info->revision.buf,
|
|
|
|
oid_to_hex(&info->w_commit));
|
|
|
|
} else {
|
|
|
|
return error(_("%s: Could not drop stash entry"),
|
|
|
|
info->revision.buf);
|
|
|
|
}
|
|
|
|
|
2020-10-24 19:06:48 +02:00
|
|
|
if (reflog_is_empty(ref_stash))
|
2019-02-26 00:16:16 +01:00
|
|
|
do_clear_stash();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void assert_stash_ref(struct stash_info *info)
|
|
|
|
{
|
|
|
|
if (!info->is_stash_ref) {
|
|
|
|
error(_("'%s' is not a stash reference"), info->revision.buf);
|
|
|
|
free_stash_info(info);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int drop_stash(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int quiet = 0;
|
|
|
|
struct stash_info info;
|
|
|
|
struct option options[] = {
|
|
|
|
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
argc = parse_options(argc, argv, prefix, options,
|
2019-02-26 00:16:28 +01:00
|
|
|
git_stash_drop_usage, 0);
|
2019-02-26 00:16:16 +01:00
|
|
|
|
|
|
|
if (get_stash_info(&info, argc, argv))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
assert_stash_ref(&info);
|
|
|
|
|
2019-03-09 19:30:21 +01:00
|
|
|
ret = do_drop_stash(&info, quiet);
|
2019-02-26 00:16:16 +01:00
|
|
|
free_stash_info(&info);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:18 +01:00
|
|
|
static int pop_stash(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int index = 0;
|
|
|
|
int quiet = 0;
|
|
|
|
struct stash_info info;
|
|
|
|
struct option options[] = {
|
|
|
|
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
|
|
|
|
OPT_BOOL(0, "index", &index,
|
|
|
|
N_("attempt to recreate the index")),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
argc = parse_options(argc, argv, prefix, options,
|
2019-02-26 00:16:28 +01:00
|
|
|
git_stash_pop_usage, 0);
|
2019-02-26 00:16:18 +01:00
|
|
|
|
|
|
|
if (get_stash_info(&info, argc, argv))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
assert_stash_ref(&info);
|
|
|
|
if ((ret = do_apply_stash(prefix, &info, index, quiet)))
|
|
|
|
printf_ln(_("The stash entry is kept in case "
|
|
|
|
"you need it again."));
|
|
|
|
else
|
2019-03-09 19:30:21 +01:00
|
|
|
ret = do_drop_stash(&info, quiet);
|
2019-02-26 00:16:18 +01:00
|
|
|
|
|
|
|
free_stash_info(&info);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:17 +01:00
|
|
|
static int branch_stash(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
const char *branch = NULL;
|
|
|
|
struct stash_info info;
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
struct option options[] = {
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
argc = parse_options(argc, argv, prefix, options,
|
2019-02-26 00:16:28 +01:00
|
|
|
git_stash_branch_usage, 0);
|
2019-02-26 00:16:17 +01:00
|
|
|
|
|
|
|
if (!argc) {
|
|
|
|
fprintf_ln(stderr, _("No branch name specified"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
branch = argv[0];
|
|
|
|
|
|
|
|
if (get_stash_info(&info, argc - 1, argv + 1))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
cp.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp.args, "checkout", "-b", NULL);
|
|
|
|
strvec_push(&cp.args, branch);
|
|
|
|
strvec_push(&cp.args, oid_to_hex(&info.b_commit));
|
2019-02-26 00:16:17 +01:00
|
|
|
ret = run_command(&cp);
|
|
|
|
if (!ret)
|
|
|
|
ret = do_apply_stash(prefix, &info, 1, 0);
|
|
|
|
if (!ret && info.is_stash_ref)
|
2019-03-09 19:30:21 +01:00
|
|
|
ret = do_drop_stash(&info, 0);
|
2019-02-26 00:16:17 +01:00
|
|
|
|
|
|
|
free_stash_info(&info);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:19 +01:00
|
|
|
static int list_stash(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
struct option options[] = {
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
argc = parse_options(argc, argv, prefix, options,
|
2019-02-26 00:16:28 +01:00
|
|
|
git_stash_list_usage,
|
2019-02-26 00:16:19 +01:00
|
|
|
PARSE_OPT_KEEP_UNKNOWN);
|
|
|
|
|
|
|
|
if (!ref_exists(ref_stash))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cp.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp.args, "log", "--format=%gd: %gs", "-g",
|
2021-05-20 23:47:01 +02:00
|
|
|
"--first-parent", NULL);
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushv(&cp.args, argv);
|
|
|
|
strvec_push(&cp.args, ref_stash);
|
|
|
|
strvec_push(&cp.args, "--");
|
2019-02-26 00:16:19 +01:00
|
|
|
return run_command(&cp);
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:20 +01:00
|
|
|
static int show_stat = 1;
|
|
|
|
static int show_patch;
|
2021-03-03 12:16:43 +01:00
|
|
|
static int show_include_untracked;
|
2019-02-26 00:16:20 +01:00
|
|
|
|
|
|
|
static int git_stash_config(const char *var, const char *value, void *cb)
|
|
|
|
{
|
|
|
|
if (!strcmp(var, "stash.showstat")) {
|
|
|
|
show_stat = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcmp(var, "stash.showpatch")) {
|
|
|
|
show_patch = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
2021-03-03 12:16:43 +01:00
|
|
|
if (!strcmp(var, "stash.showincludeuntracked")) {
|
|
|
|
show_include_untracked = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
2020-03-03 18:46:12 +01:00
|
|
|
return git_diff_basic_config(var, value, cb);
|
2019-02-26 00:16:20 +01:00
|
|
|
}
|
|
|
|
|
2021-03-03 12:16:42 +01:00
|
|
|
static void diff_include_untracked(const struct stash_info *info, struct diff_options *diff_opt)
|
|
|
|
{
|
|
|
|
const struct object_id *oid[] = { &info->w_commit, &info->u_tree };
|
|
|
|
struct tree *tree[ARRAY_SIZE(oid)];
|
|
|
|
struct tree_desc tree_desc[ARRAY_SIZE(oid)];
|
|
|
|
struct unpack_trees_options unpack_tree_opt = { 0 };
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(oid); i++) {
|
|
|
|
tree[i] = parse_tree_indirect(oid[i]);
|
|
|
|
if (parse_tree(tree[i]) < 0)
|
|
|
|
die(_("failed to parse tree"));
|
|
|
|
init_tree_desc(&tree_desc[i], tree[i]->buffer, tree[i]->size);
|
|
|
|
}
|
|
|
|
|
|
|
|
unpack_tree_opt.head_idx = -1;
|
|
|
|
unpack_tree_opt.src_index = &the_index;
|
|
|
|
unpack_tree_opt.dst_index = &the_index;
|
|
|
|
unpack_tree_opt.merge = 1;
|
|
|
|
unpack_tree_opt.fn = stash_worktree_untracked_merge;
|
|
|
|
|
|
|
|
if (unpack_trees(ARRAY_SIZE(tree_desc), tree_desc, &unpack_tree_opt))
|
|
|
|
die(_("failed to unpack trees"));
|
|
|
|
|
|
|
|
do_diff_cache(&info->b_commit, diff_opt);
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:20 +01:00
|
|
|
static int show_stash(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int ret = 0;
|
|
|
|
struct stash_info info;
|
|
|
|
struct rev_info rev;
|
2020-07-28 22:24:27 +02:00
|
|
|
struct strvec stash_args = STRVEC_INIT;
|
|
|
|
struct strvec revision_args = STRVEC_INIT;
|
2021-03-03 12:16:42 +01:00
|
|
|
enum {
|
|
|
|
UNTRACKED_NONE,
|
|
|
|
UNTRACKED_INCLUDE,
|
|
|
|
UNTRACKED_ONLY
|
2021-05-21 12:37:47 +02:00
|
|
|
} show_untracked = show_include_untracked ? UNTRACKED_INCLUDE : UNTRACKED_NONE;
|
2019-02-26 00:16:20 +01:00
|
|
|
struct option options[] = {
|
2021-03-03 12:16:42 +01:00
|
|
|
OPT_SET_INT('u', "include-untracked", &show_untracked,
|
|
|
|
N_("include untracked files in the stash"),
|
|
|
|
UNTRACKED_INCLUDE),
|
|
|
|
OPT_SET_INT_F(0, "only-untracked", &show_untracked,
|
|
|
|
N_("only show untracked files in the stash"),
|
|
|
|
UNTRACKED_ONLY, PARSE_OPT_NONEG),
|
2019-02-26 00:16:20 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
init_diff_ui_defaults();
|
|
|
|
git_config(git_diff_ui_config, NULL);
|
|
|
|
init_revisions(&rev, prefix);
|
|
|
|
|
2021-03-03 12:16:42 +01:00
|
|
|
argc = parse_options(argc, argv, prefix, options, git_stash_show_usage,
|
|
|
|
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
|
|
|
|
PARSE_OPT_KEEP_DASHDASH);
|
|
|
|
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(&revision_args, argv[0]);
|
2019-02-26 00:16:20 +01:00
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
if (argv[i][0] != '-')
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(&stash_args, argv[i]);
|
2019-02-26 00:16:20 +01:00
|
|
|
else
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(&revision_args, argv[i]);
|
2019-02-26 00:16:20 +01:00
|
|
|
}
|
|
|
|
|
2020-07-29 02:37:20 +02:00
|
|
|
ret = get_stash_info(&info, stash_args.nr, stash_args.v);
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_clear(&stash_args);
|
2019-02-26 00:16:20 +01:00
|
|
|
if (ret)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The config settings are applied only if there are not passed
|
|
|
|
* any options.
|
|
|
|
*/
|
2020-07-29 02:37:20 +02:00
|
|
|
if (revision_args.nr == 1) {
|
2019-02-26 00:16:20 +01:00
|
|
|
if (show_stat)
|
|
|
|
rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;
|
|
|
|
|
|
|
|
if (show_patch)
|
|
|
|
rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
|
|
|
|
|
|
|
|
if (!show_stat && !show_patch) {
|
|
|
|
free_stash_info(&info);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-29 02:37:20 +02:00
|
|
|
argc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL);
|
2019-02-26 00:16:20 +01:00
|
|
|
if (argc > 1) {
|
|
|
|
free_stash_info(&info);
|
2019-02-26 00:16:28 +01:00
|
|
|
usage_with_options(git_stash_show_usage, options);
|
2019-02-26 00:16:20 +01:00
|
|
|
}
|
stash: setup default diff output format if necessary
In the scripted 'git stash show' when no arguments are passed, we just
pass '--stat' to 'git diff'. When any argument is passed to 'stash
show', we no longer pass '--stat' to 'git diff', and pass whatever
flags are passed directly through to 'git diff'.
By default 'git diff' shows the patch output. So when a user uses
'git stash show --patience', they would be shown the diff as expected,
using the patience algorithm. '--patience' in this case only changes
the diff algorithm, but does not cause 'git diff' to show the diff by
itself. The diff is shown because that's the default behaviour of
'git diff'.
In the C version of 'git stash show', we try to emulate that behaviour
using the internal diff API. However we forgot to set up the default
output format, in case it wasn't set by any of the flags that were
passed through. So 'git stash show --patience' in the builtin version
of stash would be completely silent, while it would show the diff in
the scripted version.
The same thing would happen for other flags that only affect the way a
patch is displayed, rather than switching to a different output format
than the default one.
Fix this by setting up the default output format for 'git diff'.
Reported-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-03-20 23:49:55 +01:00
|
|
|
if (!rev.diffopt.output_format) {
|
|
|
|
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
|
|
|
|
diff_setup_done(&rev.diffopt);
|
|
|
|
}
|
2019-02-26 00:16:20 +01:00
|
|
|
|
|
|
|
rev.diffopt.flags.recursive = 1;
|
|
|
|
setup_diff_pager(&rev.diffopt);
|
2021-03-03 12:16:42 +01:00
|
|
|
switch (show_untracked) {
|
|
|
|
case UNTRACKED_NONE:
|
|
|
|
diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt);
|
|
|
|
break;
|
|
|
|
case UNTRACKED_ONLY:
|
2021-05-12 22:16:13 +02:00
|
|
|
if (info.has_u)
|
|
|
|
diff_root_tree_oid(&info.u_tree, "", &rev.diffopt);
|
2021-03-03 12:16:42 +01:00
|
|
|
break;
|
|
|
|
case UNTRACKED_INCLUDE:
|
2021-05-12 22:16:13 +02:00
|
|
|
if (info.has_u)
|
|
|
|
diff_include_untracked(&info, &rev.diffopt);
|
|
|
|
else
|
|
|
|
diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt);
|
2021-03-03 12:16:42 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-02-26 00:16:20 +01:00
|
|
|
log_tree_diff_flush(&rev);
|
|
|
|
|
|
|
|
free_stash_info(&info);
|
|
|
|
return diff_result_code(&rev.diffopt, 0);
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:21 +01:00
|
|
|
static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
|
|
|
|
int quiet)
|
|
|
|
{
|
|
|
|
if (!stash_msg)
|
|
|
|
stash_msg = "Created via \"git stash store\".";
|
|
|
|
|
|
|
|
if (update_ref(stash_msg, ref_stash, w_commit, NULL,
|
|
|
|
REF_FORCE_CREATE_REFLOG,
|
|
|
|
quiet ? UPDATE_REFS_QUIET_ON_ERR :
|
|
|
|
UPDATE_REFS_MSG_ON_ERR)) {
|
|
|
|
if (!quiet) {
|
|
|
|
fprintf_ln(stderr, _("Cannot update %s with %s"),
|
|
|
|
ref_stash, oid_to_hex(w_commit));
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int store_stash(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
|
|
|
int quiet = 0;
|
|
|
|
const char *stash_msg = NULL;
|
|
|
|
struct object_id obj;
|
|
|
|
struct object_context dummy;
|
|
|
|
struct option options[] = {
|
|
|
|
OPT__QUIET(&quiet, N_("be quiet")),
|
|
|
|
OPT_STRING('m', "message", &stash_msg, "message",
|
|
|
|
N_("stash message")),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
argc = parse_options(argc, argv, prefix, options,
|
2019-02-26 00:16:28 +01:00
|
|
|
git_stash_store_usage,
|
2019-02-26 00:16:21 +01:00
|
|
|
PARSE_OPT_KEEP_UNKNOWN);
|
|
|
|
|
|
|
|
if (argc != 1) {
|
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("\"git stash store\" requires one "
|
|
|
|
"<commit> argument"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-04-22 04:14:43 +02:00
|
|
|
if (get_oid_with_context(the_repository,
|
|
|
|
argv[0], quiet ? GET_OID_QUIETLY : 0, &obj,
|
2019-02-26 00:16:21 +01:00
|
|
|
&dummy)) {
|
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("Cannot update %s with %s"),
|
|
|
|
ref_stash, argv[0]);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return do_store_stash(&obj, stash_msg, quiet);
|
|
|
|
}
|
|
|
|
|
2020-07-28 22:24:27 +02:00
|
|
|
static void add_pathspecs(struct strvec *args,
|
2019-03-11 23:16:32 +01:00
|
|
|
const struct pathspec *ps) {
|
2019-02-26 00:16:22 +01:00
|
|
|
int i;
|
|
|
|
|
2019-03-11 23:16:32 +01:00
|
|
|
for (i = 0; i < ps->nr; i++)
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(args, ps->items[i].original);
|
2019-02-26 00:16:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* `untracked_files` will be filled with the names of untracked files.
|
|
|
|
* The return value is:
|
|
|
|
*
|
|
|
|
* = 0 if there are not any untracked files
|
|
|
|
* > 0 if there are untracked files
|
|
|
|
*/
|
2019-03-11 23:16:32 +01:00
|
|
|
static int get_untracked_files(const struct pathspec *ps, int include_untracked,
|
2019-02-26 00:16:22 +01:00
|
|
|
struct strbuf *untracked_files)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int found = 0;
|
2021-07-01 12:51:27 +02:00
|
|
|
struct dir_struct dir = DIR_INIT;
|
2019-02-26 00:16:22 +01:00
|
|
|
|
|
|
|
if (include_untracked != INCLUDE_ALL_FILES)
|
|
|
|
setup_standard_excludes(&dir);
|
|
|
|
|
Fix error-prone fill_directory() API; make it only return matches
Traditionally, the expected calling convention for the dir.c API was:
fill_directory(&dir, ..., pathspec)
foreach entry in dir->entries:
if (dir_path_match(entry, pathspec))
process_or_display(entry)
This may have made sense once upon a time, because the fill_directory() call
could use cheap checks to avoid doing full pathspec matching, and an external
caller may have wanted to do other post-processing of the results anyway.
However:
* this structure makes it easy for users of the API to get it wrong
* this structure actually makes it harder to understand
fill_directory() and the functions it uses internally. It has
tripped me up several times while trying to fix bugs and
restructure things.
* relying on post-filtering was already found to produce wrong
results; pathspec matching had to be added internally for multiple
cases in order to get the right results (see commits 404ebceda01c
(dir: also check directories for matching pathspecs, 2019-09-17)
and 89a1f4aaf765 (dir: if our pathspec might match files under a
dir, recurse into it, 2019-09-17))
* it's bad for performance: fill_directory() already has to do lots
of checks and knows the subset of cases where it still needs to do
more checks. Forcing external callers to do full pathspec
matching means they must re-check _every_ path.
So, add the pathspec matching within the fill_directory() internals, and
remove it from external callers.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-01 06:17:45 +02:00
|
|
|
fill_directory(&dir, the_repository->index, ps);
|
2019-02-26 00:16:22 +01:00
|
|
|
for (i = 0; i < dir.nr; i++) {
|
|
|
|
struct dir_entry *ent = dir.entries[i];
|
Fix error-prone fill_directory() API; make it only return matches
Traditionally, the expected calling convention for the dir.c API was:
fill_directory(&dir, ..., pathspec)
foreach entry in dir->entries:
if (dir_path_match(entry, pathspec))
process_or_display(entry)
This may have made sense once upon a time, because the fill_directory() call
could use cheap checks to avoid doing full pathspec matching, and an external
caller may have wanted to do other post-processing of the results anyway.
However:
* this structure makes it easy for users of the API to get it wrong
* this structure actually makes it harder to understand
fill_directory() and the functions it uses internally. It has
tripped me up several times while trying to fix bugs and
restructure things.
* relying on post-filtering was already found to produce wrong
results; pathspec matching had to be added internally for multiple
cases in order to get the right results (see commits 404ebceda01c
(dir: also check directories for matching pathspecs, 2019-09-17)
and 89a1f4aaf765 (dir: if our pathspec might match files under a
dir, recurse into it, 2019-09-17))
* it's bad for performance: fill_directory() already has to do lots
of checks and knows the subset of cases where it still needs to do
more checks. Forcing external callers to do full pathspec
matching means they must re-check _every_ path.
So, add the pathspec matching within the fill_directory() internals, and
remove it from external callers.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-01 06:17:45 +02:00
|
|
|
found++;
|
|
|
|
strbuf_addstr(untracked_files, ent->name);
|
|
|
|
/* NUL-terminate: will be fed to update-index -z */
|
|
|
|
strbuf_addch(untracked_files, '\0');
|
2019-02-26 00:16:22 +01:00
|
|
|
}
|
|
|
|
|
dir: fix problematic API to avoid memory leaks
The dir structure seemed to have a number of leaks and problems around
it. First I noticed that parent_hashmap and recursive_hashmap were
being leaked (though Peff noticed and submitted fixes before me). Then
I noticed in the previous commit that clear_directory() was only taking
responsibility for a subset of fields within dir_struct, despite the
fact that entries[] and ignored[] we allocated internally to dir.c.
That, of course, resulted in many callers either leaking or haphazardly
trying to free these arrays and their contents.
Digging further, I found that despite the pretty clear documentation
near the top of dir.h that folks were supposed to call clear_directory()
when the user no longer needed the dir_struct, there were four callers
that didn't bother doing that at all. However, two of them clearly
thought about leaks since they had an UNLEAK(dir) directive, which to me
suggests that the method to free the data was too unclear. I suspect
the non-obviousness of the API and its holes led folks to avoid it,
which then snowballed into further problems with the entries[],
ignored[], parent_hashmap, and recursive_hashmap problems.
Rename clear_directory() to dir_clear() to be more in line with other
data structures in git, and introduce a dir_init() to handle the
suggested memsetting of dir_struct to all zeroes. I hope that a name
like "dir_clear()" is more clear, and that the presence of dir_init()
will provide a hint to those looking at the code that they need to look
for either a dir_clear() or a dir_free() and lead them to find
dir_clear().
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-08-19 00:58:26 +02:00
|
|
|
dir_clear(&dir);
|
2019-02-26 00:16:22 +01:00
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2019-02-26 00:16:26 +01:00
|
|
|
* The return value of `check_changes_tracked_files()` can be:
|
2019-02-26 00:16:22 +01:00
|
|
|
*
|
|
|
|
* < 0 if there was an error
|
|
|
|
* = 0 if there are no changes.
|
|
|
|
* > 0 if there are changes.
|
|
|
|
*/
|
2019-03-11 23:16:32 +01:00
|
|
|
static int check_changes_tracked_files(const struct pathspec *ps)
|
2019-02-26 00:16:22 +01:00
|
|
|
{
|
|
|
|
int result;
|
|
|
|
struct rev_info rev;
|
|
|
|
struct object_id dummy;
|
2019-03-11 23:16:32 +01:00
|
|
|
int ret = 0;
|
2019-02-26 00:16:22 +01:00
|
|
|
|
|
|
|
/* No initial commit. */
|
|
|
|
if (get_oid("HEAD", &dummy))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (read_cache() < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
init_revisions(&rev, NULL);
|
2019-03-11 23:16:32 +01:00
|
|
|
copy_pathspec(&rev.prune_data, ps);
|
2019-02-26 00:16:22 +01:00
|
|
|
|
|
|
|
rev.diffopt.flags.quick = 1;
|
|
|
|
rev.diffopt.flags.ignore_submodules = 1;
|
|
|
|
rev.abbrev = 0;
|
|
|
|
|
|
|
|
add_head_to_pending(&rev);
|
|
|
|
diff_setup_done(&rev.diffopt);
|
|
|
|
|
|
|
|
result = run_diff_index(&rev, 1);
|
2019-03-11 23:16:32 +01:00
|
|
|
if (diff_result_code(&rev.diffopt, result)) {
|
|
|
|
ret = 1;
|
|
|
|
goto done;
|
|
|
|
}
|
2019-02-26 00:16:22 +01:00
|
|
|
|
|
|
|
object_array_clear(&rev.pending);
|
|
|
|
result = run_diff_files(&rev, 0);
|
2019-03-11 23:16:32 +01:00
|
|
|
if (diff_result_code(&rev.diffopt, result)) {
|
|
|
|
ret = 1;
|
|
|
|
goto done;
|
|
|
|
}
|
2019-02-26 00:16:22 +01:00
|
|
|
|
2019-03-11 23:16:32 +01:00
|
|
|
done:
|
|
|
|
clear_pathspec(&rev.prune_data);
|
|
|
|
return ret;
|
2019-02-26 00:16:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The function will fill `untracked_files` with the names of untracked files
|
|
|
|
* It will return 1 if there were any changes and 0 if there were not.
|
|
|
|
*/
|
2019-03-11 23:16:32 +01:00
|
|
|
static int check_changes(const struct pathspec *ps, int include_untracked,
|
2019-02-26 00:16:26 +01:00
|
|
|
struct strbuf *untracked_files)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
if (check_changes_tracked_files(ps))
|
|
|
|
ret = 1;
|
|
|
|
|
2019-02-26 00:16:22 +01:00
|
|
|
if (include_untracked && get_untracked_files(ps, include_untracked,
|
2019-02-26 00:16:26 +01:00
|
|
|
untracked_files))
|
|
|
|
ret = 1;
|
2019-02-26 00:16:22 +01:00
|
|
|
|
2019-02-26 00:16:26 +01:00
|
|
|
return ret;
|
2019-02-26 00:16:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
|
|
|
|
struct strbuf files)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct strbuf untracked_msg = STRBUF_INIT;
|
|
|
|
struct child_process cp_upd_index = CHILD_PROCESS_INIT;
|
2019-02-26 00:16:27 +01:00
|
|
|
struct index_state istate = { NULL };
|
2019-02-26 00:16:22 +01:00
|
|
|
|
|
|
|
cp_upd_index.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
"--remove", "--stdin", NULL);
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
|
2019-02-26 00:16:22 +01:00
|
|
|
stash_index_path.buf);
|
|
|
|
|
|
|
|
strbuf_addf(&untracked_msg, "untracked files on %s\n", msg->buf);
|
|
|
|
if (pipe_command(&cp_upd_index, files.buf, files.len, NULL, 0,
|
|
|
|
NULL, 0)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:27 +01:00
|
|
|
if (write_index_as_tree(&info->u_tree, &istate, stash_index_path.buf, 0,
|
|
|
|
NULL)) {
|
2019-02-26 00:16:22 +01:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (commit_tree(untracked_msg.buf, untracked_msg.len,
|
|
|
|
&info->u_tree, NULL, &info->u_commit, NULL, NULL)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2019-02-26 00:16:27 +01:00
|
|
|
discard_index(&istate);
|
2019-02-26 00:16:22 +01:00
|
|
|
strbuf_release(&untracked_msg);
|
|
|
|
remove_path(stash_index_path.buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-10-28 10:29:34 +02:00
|
|
|
static int stash_staged(struct stash_info *info, struct strbuf *out_patch,
|
|
|
|
int quiet)
|
2021-10-18 18:09:06 +02:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
|
|
|
|
struct index_state istate = { NULL };
|
|
|
|
|
|
|
|
if (write_index_as_tree(&info->w_tree, &istate, the_repository->index_file,
|
|
|
|
0, NULL)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
cp_diff_tree.git_cmd = 1;
|
|
|
|
strvec_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;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!out_patch->len) {
|
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("No staged changes"));
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
discard_index(&istate);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-03-11 23:16:32 +01:00
|
|
|
static int stash_patch(struct stash_info *info, const struct pathspec *ps,
|
2019-02-26 00:16:24 +01:00
|
|
|
struct strbuf *out_patch, int quiet)
|
2019-02-26 00:16:22 +01:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct child_process cp_read_tree = CHILD_PROCESS_INIT;
|
|
|
|
struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
|
2019-02-26 00:16:27 +01:00
|
|
|
struct index_state istate = { NULL };
|
2019-12-21 22:57:13 +01:00
|
|
|
char *old_index_env = NULL, *old_repo_index_file;
|
2019-02-26 00:16:22 +01:00
|
|
|
|
|
|
|
remove_path(stash_index_path.buf);
|
|
|
|
|
|
|
|
cp_read_tree.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp_read_tree.args, "read-tree", "HEAD", NULL);
|
|
|
|
strvec_pushf(&cp_read_tree.env_array, "GIT_INDEX_FILE=%s",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
stash_index_path.buf);
|
2019-02-26 00:16:22 +01:00
|
|
|
if (run_command(&cp_read_tree)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find out what the user wants. */
|
2019-12-21 22:57:13 +01:00
|
|
|
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);
|
2019-02-26 00:16:22 +01:00
|
|
|
|
|
|
|
/* State of the working tree. */
|
2019-02-26 00:16:27 +01:00
|
|
|
if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
|
|
|
|
NULL)) {
|
2019-02-26 00:16:22 +01:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
cp_diff_tree.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
oid_to_hex(&info->w_tree), "--", NULL);
|
2019-02-26 00:16:22 +01:00
|
|
|
if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!out_patch->len) {
|
2019-02-26 00:16:24 +01:00
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("No changes selected"));
|
2019-02-26 00:16:22 +01:00
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2019-02-26 00:16:27 +01:00
|
|
|
discard_index(&istate);
|
2019-02-26 00:16:22 +01:00
|
|
|
remove_path(stash_index_path.buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-03-11 23:16:32 +01:00
|
|
|
static int stash_working_tree(struct stash_info *info, const struct pathspec *ps)
|
2019-02-26 00:16:22 +01:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct rev_info rev;
|
|
|
|
struct child_process cp_upd_index = CHILD_PROCESS_INIT;
|
|
|
|
struct strbuf diff_output = STRBUF_INIT;
|
2019-02-26 00:16:27 +01:00
|
|
|
struct index_state istate = { NULL };
|
2019-02-26 00:16:22 +01:00
|
|
|
|
|
|
|
init_revisions(&rev, NULL);
|
2019-03-11 23:16:32 +01:00
|
|
|
copy_pathspec(&rev.prune_data, ps);
|
2019-02-26 00:16:22 +01:00
|
|
|
|
|
|
|
set_alternate_index_output(stash_index_path.buf);
|
|
|
|
if (reset_tree(&info->i_tree, 0, 0)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
set_alternate_index_output(NULL);
|
|
|
|
|
|
|
|
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
|
|
|
|
rev.diffopt.format_callback = add_diff_to_buf;
|
|
|
|
rev.diffopt.format_callback_data = &diff_output;
|
|
|
|
|
|
|
|
if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_pending_object(&rev, parse_object(the_repository, &info->b_commit),
|
|
|
|
"");
|
|
|
|
if (run_diff_index(&rev, 0)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
cp_upd_index.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp_upd_index.args, "update-index",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
"--ignore-skip-worktree-entries",
|
|
|
|
"-z", "--add", "--remove", "--stdin", NULL);
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
stash_index_path.buf);
|
2019-02-26 00:16:22 +01:00
|
|
|
|
|
|
|
if (pipe_command(&cp_upd_index, diff_output.buf, diff_output.len,
|
|
|
|
NULL, 0, NULL, 0)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:27 +01:00
|
|
|
if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
|
|
|
|
NULL)) {
|
2019-02-26 00:16:22 +01:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2019-02-26 00:16:27 +01:00
|
|
|
discard_index(&istate);
|
2019-02-26 00:16:22 +01:00
|
|
|
UNLEAK(rev);
|
|
|
|
object_array_clear(&rev.pending);
|
2019-03-11 23:16:32 +01:00
|
|
|
clear_pathspec(&rev.prune_data);
|
2019-02-26 00:16:22 +01:00
|
|
|
strbuf_release(&diff_output);
|
|
|
|
remove_path(stash_index_path.buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-03-11 23:16:32 +01:00
|
|
|
static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,
|
2021-10-18 18:09:06 +02:00
|
|
|
int include_untracked, int patch_mode, int only_staged,
|
2019-02-26 00:16:24 +01:00
|
|
|
struct stash_info *info, struct strbuf *patch,
|
|
|
|
int quiet)
|
2019-02-26 00:16:22 +01:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
int flags = 0;
|
|
|
|
int untracked_commit_option = 0;
|
|
|
|
const char *head_short_sha1 = NULL;
|
|
|
|
const char *branch_ref = NULL;
|
|
|
|
const char *branch_name = "(no branch)";
|
|
|
|
struct commit *head_commit = NULL;
|
|
|
|
struct commit_list *parents = NULL;
|
|
|
|
struct strbuf msg = STRBUF_INIT;
|
|
|
|
struct strbuf commit_tree_label = STRBUF_INIT;
|
|
|
|
struct strbuf untracked_files = STRBUF_INIT;
|
|
|
|
|
|
|
|
prepare_fallback_ident("git stash", "git@stash");
|
|
|
|
|
|
|
|
read_cache_preload(NULL);
|
2019-09-11 20:20:27 +02:00
|
|
|
if (refresh_and_write_cache(REFRESH_QUIET, 0, 0) < 0) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
2019-02-26 00:16:22 +01:00
|
|
|
|
|
|
|
if (get_oid("HEAD", &info->b_commit)) {
|
2019-02-26 00:16:24 +01:00
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("You do not have "
|
|
|
|
"the initial commit yet"));
|
2019-02-26 00:16:22 +01:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
} else {
|
|
|
|
head_commit = lookup_commit(the_repository, &info->b_commit);
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:26 +01:00
|
|
|
if (!check_changes(ps, include_untracked, &untracked_files)) {
|
2019-02-26 00:16:22 +01:00
|
|
|
ret = 1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
|
|
|
|
if (flags & REF_ISSYMREF)
|
|
|
|
branch_name = strrchr(branch_ref, '/') + 1;
|
|
|
|
head_short_sha1 = find_unique_abbrev(&head_commit->object.oid,
|
|
|
|
DEFAULT_ABBREV);
|
|
|
|
strbuf_addf(&msg, "%s: %s ", branch_name, head_short_sha1);
|
|
|
|
pp_commit_easy(CMIT_FMT_ONELINE, head_commit, &msg);
|
|
|
|
|
|
|
|
strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
|
|
|
|
commit_list_insert(head_commit, &parents);
|
|
|
|
if (write_cache_as_tree(&info->i_tree, 0, NULL) ||
|
|
|
|
commit_tree(commit_tree_label.buf, commit_tree_label.len,
|
|
|
|
&info->i_tree, parents, &info->i_commit, NULL, NULL)) {
|
2019-02-26 00:16:24 +01:00
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("Cannot save the current "
|
|
|
|
"index state"));
|
2019-02-26 00:16:22 +01:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:26 +01:00
|
|
|
if (include_untracked) {
|
2019-02-26 00:16:22 +01:00
|
|
|
if (save_untracked_files(info, &msg, untracked_files)) {
|
2019-02-26 00:16:24 +01:00
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("Cannot save "
|
|
|
|
"the untracked files"));
|
2019-02-26 00:16:22 +01:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
untracked_commit_option = 1;
|
|
|
|
}
|
|
|
|
if (patch_mode) {
|
2019-02-26 00:16:24 +01:00
|
|
|
ret = stash_patch(info, ps, patch, quiet);
|
2019-02-26 00:16:22 +01:00
|
|
|
if (ret < 0) {
|
2019-02-26 00:16:24 +01:00
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("Cannot save the current "
|
|
|
|
"worktree state"));
|
2019-02-26 00:16:22 +01:00
|
|
|
goto done;
|
|
|
|
} else if (ret > 0) {
|
|
|
|
goto done;
|
|
|
|
}
|
2021-10-18 18:09:06 +02:00
|
|
|
} else if (only_staged) {
|
2021-10-28 10:29:34 +02:00
|
|
|
ret = stash_staged(info, patch, quiet);
|
2021-10-18 18:09:06 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("Cannot save the current "
|
|
|
|
"staged state"));
|
|
|
|
goto done;
|
|
|
|
} else if (ret > 0) {
|
|
|
|
goto done;
|
|
|
|
}
|
2019-02-26 00:16:22 +01:00
|
|
|
} else {
|
|
|
|
if (stash_working_tree(info, ps)) {
|
2019-02-26 00:16:24 +01:00
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("Cannot save the current "
|
|
|
|
"worktree state"));
|
2019-02-26 00:16:22 +01:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!stash_msg_buf->len)
|
|
|
|
strbuf_addf(stash_msg_buf, "WIP on %s", msg.buf);
|
|
|
|
else
|
|
|
|
strbuf_insertf(stash_msg_buf, 0, "On %s: ", branch_name);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* `parents` will be empty after calling `commit_tree()`, so there is
|
|
|
|
* no need to call `free_commit_list()`
|
|
|
|
*/
|
|
|
|
parents = NULL;
|
|
|
|
if (untracked_commit_option)
|
|
|
|
commit_list_insert(lookup_commit(the_repository,
|
|
|
|
&info->u_commit),
|
|
|
|
&parents);
|
|
|
|
commit_list_insert(lookup_commit(the_repository, &info->i_commit),
|
|
|
|
&parents);
|
|
|
|
commit_list_insert(head_commit, &parents);
|
|
|
|
|
|
|
|
if (commit_tree(stash_msg_buf->buf, stash_msg_buf->len, &info->w_tree,
|
|
|
|
parents, &info->w_commit, NULL, NULL)) {
|
2019-02-26 00:16:24 +01:00
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("Cannot record "
|
|
|
|
"working tree state"));
|
2019-02-26 00:16:22 +01:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
strbuf_release(&commit_tree_label);
|
|
|
|
strbuf_release(&msg);
|
|
|
|
strbuf_release(&untracked_files);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int create_stash(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct strbuf stash_msg_buf = STRBUF_INIT;
|
|
|
|
struct stash_info info;
|
|
|
|
struct pathspec ps;
|
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
/* Starting with argv[1], since argv[0] is "create" */
|
|
|
|
strbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');
|
2019-02-26 00:16:22 +01:00
|
|
|
|
|
|
|
memset(&ps, 0, sizeof(ps));
|
2019-03-11 23:16:32 +01:00
|
|
|
if (!check_changes_tracked_files(&ps))
|
2019-02-26 00:16:26 +01:00
|
|
|
return 0;
|
|
|
|
|
2021-10-18 18:09:06 +02:00
|
|
|
ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info,
|
2019-02-26 00:16:24 +01:00
|
|
|
NULL, 0);
|
2019-02-26 00:16:22 +01:00
|
|
|
if (!ret)
|
|
|
|
printf_ln("%s", oid_to_hex(&info.w_commit));
|
|
|
|
|
|
|
|
strbuf_release(&stash_msg_buf);
|
2019-02-26 00:16:26 +01:00
|
|
|
return ret;
|
2019-02-26 00:16:22 +01:00
|
|
|
}
|
|
|
|
|
2019-03-11 23:16:32 +01:00
|
|
|
static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet,
|
2021-10-18 18:09:06 +02:00
|
|
|
int keep_index, int patch_mode, int include_untracked, int only_staged)
|
2019-02-26 00:16:23 +01:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct stash_info info;
|
|
|
|
struct strbuf patch = STRBUF_INIT;
|
|
|
|
struct strbuf stash_msg_buf = STRBUF_INIT;
|
2019-02-26 00:16:26 +01:00
|
|
|
struct strbuf untracked_files = STRBUF_INIT;
|
2019-02-26 00:16:23 +01:00
|
|
|
|
|
|
|
if (patch_mode && keep_index == -1)
|
|
|
|
keep_index = 1;
|
|
|
|
|
|
|
|
if (patch_mode && include_untracked) {
|
|
|
|
fprintf_ln(stderr, _("Can't use --patch and --include-untracked"
|
|
|
|
" or --all at the same time"));
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2021-10-18 18:09:06 +02:00
|
|
|
/* --patch overrides --staged */
|
|
|
|
if (patch_mode)
|
|
|
|
only_staged = 0;
|
|
|
|
|
|
|
|
if (only_staged && include_untracked) {
|
|
|
|
fprintf_ln(stderr, _("Can't use --staged and --include-untracked"
|
|
|
|
" or --all at the same time"));
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:23 +01:00
|
|
|
read_cache_preload(NULL);
|
2019-03-11 23:16:32 +01:00
|
|
|
if (!include_untracked && ps->nr) {
|
2019-02-26 00:16:23 +01:00
|
|
|
int i;
|
2019-03-11 23:16:32 +01:00
|
|
|
char *ps_matched = xcalloc(ps->nr, 1);
|
2019-02-26 00:16:23 +01:00
|
|
|
|
2021-04-01 03:49:52 +02:00
|
|
|
/* TODO: audit for interaction with sparse-index. */
|
|
|
|
ensure_full_index(&the_index);
|
2019-02-26 00:16:23 +01:00
|
|
|
for (i = 0; i < active_nr; i++)
|
2019-03-11 23:16:32 +01:00
|
|
|
ce_path_match(&the_index, active_cache[i], ps,
|
2019-02-26 00:16:23 +01:00
|
|
|
ps_matched);
|
|
|
|
|
2019-04-25 09:41:12 +02:00
|
|
|
if (report_path_error(ps_matched, ps)) {
|
2019-02-26 00:16:23 +01:00
|
|
|
fprintf_ln(stderr, _("Did you forget to 'git add'?"));
|
|
|
|
ret = -1;
|
|
|
|
free(ps_matched);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
free(ps_matched);
|
|
|
|
}
|
|
|
|
|
2019-09-11 20:20:27 +02:00
|
|
|
if (refresh_and_write_cache(REFRESH_QUIET, 0, 0)) {
|
2019-02-26 00:16:23 +01:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:26 +01:00
|
|
|
if (!check_changes(ps, include_untracked, &untracked_files)) {
|
2019-02-26 00:16:23 +01:00
|
|
|
if (!quiet)
|
|
|
|
printf_ln(_("No local changes to save"));
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!reflog_exists(ref_stash) && do_clear_stash()) {
|
|
|
|
ret = -1;
|
2019-02-26 00:16:24 +01:00
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("Cannot initialize stash"));
|
2019-02-26 00:16:23 +01:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stash_msg)
|
|
|
|
strbuf_addstr(&stash_msg_buf, stash_msg);
|
2021-10-18 18:09:06 +02:00
|
|
|
if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged,
|
2019-02-26 00:16:24 +01:00
|
|
|
&info, &patch, quiet)) {
|
2019-02-26 00:16:23 +01:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (do_store_stash(&info.w_commit, stash_msg_buf.buf, 1)) {
|
|
|
|
ret = -1;
|
2019-02-26 00:16:24 +01:00
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("Cannot save the current status"));
|
2019-02-26 00:16:23 +01:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:24 +01:00
|
|
|
if (!quiet)
|
|
|
|
printf_ln(_("Saved working directory and index state %s"),
|
|
|
|
stash_msg_buf.buf);
|
2019-02-26 00:16:23 +01:00
|
|
|
|
2021-10-18 18:09:06 +02:00
|
|
|
if (!(patch_mode || only_staged)) {
|
2019-03-11 23:16:32 +01:00
|
|
|
if (include_untracked && !ps->nr) {
|
2019-02-26 00:16:23 +01:00
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
cp.git_cmd = 1;
|
2022-01-26 02:43:45 +01:00
|
|
|
if (startup_info->original_cwd) {
|
2021-12-09 06:08:32 +01:00
|
|
|
cp.dir = startup_info->original_cwd;
|
2022-01-26 02:43:45 +01:00
|
|
|
strvec_pushf(&cp.env_array, "%s=%s",
|
|
|
|
GIT_WORK_TREE_ENVIRONMENT,
|
|
|
|
the_repository->worktree);
|
|
|
|
}
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp.args, "clean", "--force",
|
2021-12-09 06:08:32 +01:00
|
|
|
"--quiet", "-d", ":/", NULL);
|
2019-02-26 00:16:23 +01:00
|
|
|
if (include_untracked == INCLUDE_ALL_FILES)
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(&cp.args, "-x");
|
2019-02-26 00:16:23 +01:00
|
|
|
if (run_command(&cp)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
discard_cache();
|
2019-03-11 23:16:32 +01:00
|
|
|
if (ps->nr) {
|
2019-02-26 00:16:23 +01:00
|
|
|
struct child_process cp_add = CHILD_PROCESS_INIT;
|
|
|
|
struct child_process cp_diff = CHILD_PROCESS_INIT;
|
|
|
|
struct child_process cp_apply = CHILD_PROCESS_INIT;
|
|
|
|
struct strbuf out = STRBUF_INIT;
|
|
|
|
|
|
|
|
cp_add.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(&cp_add.args, "add");
|
2019-02-26 00:16:23 +01:00
|
|
|
if (!include_untracked)
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(&cp_add.args, "-u");
|
2019-02-26 00:16:23 +01:00
|
|
|
if (include_untracked == INCLUDE_ALL_FILES)
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(&cp_add.args, "--force");
|
|
|
|
strvec_push(&cp_add.args, "--");
|
2019-02-26 00:16:23 +01:00
|
|
|
add_pathspecs(&cp_add.args, ps);
|
|
|
|
if (run_command(&cp_add)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
cp_diff.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp_diff.args, "diff-index", "-p",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
"--cached", "--binary", "HEAD", "--",
|
|
|
|
NULL);
|
2019-02-26 00:16:23 +01:00
|
|
|
add_pathspecs(&cp_diff.args, ps);
|
|
|
|
if (pipe_command(&cp_diff, NULL, 0, &out, 0, NULL, 0)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
cp_apply.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp_apply.args, "apply", "--index",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
"-R", NULL);
|
2019-02-26 00:16:23 +01:00
|
|
|
if (pipe_command(&cp_apply, out.buf, out.len, NULL, 0,
|
|
|
|
NULL, 0)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
cp.git_cmd = 1;
|
Comment important codepaths regarding nuking untracked files/dirs
In the last few commits we focused on code in unpack-trees.c that
mistakenly removed untracked files or directories. There may be more of
those, but in this commit we change our focus: callers of toplevel
commands that are expected to remove untracked files or directories.
As noted previously, we have toplevel commands that are expected to
delete untracked files such as 'read-tree --reset', 'reset --hard', and
'checkout --force'. However, that does not mean that other highlevel
commands that happen to call these other commands thought about or
conveyed to users the possibility that untracked files could be removed.
Audit the code for such callsites, and add comments near existing
callsites to mention whether these are safe or not.
My auditing is somewhat incomplete, though; it skipped several cases:
* git-rebase--preserve-merges.sh: is in the process of being
deprecated/removed, so I won't leave a note that there are
likely more bugs in that script.
* contrib/git-new-workdir: why is the -f flag being used in a new
empty directory?? It shouldn't hurt, but it seems useless.
* git-p4.py: Don't see why -f is needed for a new dir (maybe it's
not and is just superfluous), but I'm not at all familiar with
the p4 stuff
* git-archimport.perl: Don't care; arch is long since dead
* git-cvs*.perl: Don't care; cvs is long since dead
Also, the reset --hard in builtin/worktree.c looks safe, due to only
running in an empty directory.
Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-09-27 18:33:47 +02:00
|
|
|
/* BUG: this nukes untracked files in the way */
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp.args, "reset", "--hard", "-q",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
"--no-recurse-submodules", NULL);
|
2019-02-26 00:16:23 +01:00
|
|
|
if (run_command(&cp)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keep_index == 1 && !is_null_oid(&info.i_tree)) {
|
2019-07-16 16:23:22 +02:00
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
2019-02-26 00:16:23 +01:00
|
|
|
|
2019-07-16 16:23:22 +02:00
|
|
|
cp.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp.args, "checkout", "--no-overlay",
|
strvec: fix indentation in renamed calls
Code which split an argv_array call across multiple lines, like:
argv_array_pushl(&args, "one argument",
"another argument", "and more",
NULL);
was recently mechanically renamed to use strvec, which results in
mis-matched indentation like:
strvec_pushl(&args, "one argument",
"another argument", "and more",
NULL);
Let's fix these up to align the arguments with the opening paren. I did
this manually by sifting through the results of:
git jump grep 'strvec_.*,$'
and liberally applying my editor's auto-format. Most of the changes are
of the form shown above, though I also normalized a few that had
originally used a single-tab indentation (rather than our usual style of
aligning with the open paren). I also rewrapped a couple of obvious
cases (e.g., where previously too-long lines became short enough to fit
on one), but I wasn't aggressive about it. In cases broken to three or
more lines, the grouping of arguments is sometimes meaningful, and it
wasn't worth my time or reviewer time to ponder each case individually.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-07-28 22:26:31 +02:00
|
|
|
oid_to_hex(&info.i_tree), "--", NULL);
|
2019-07-16 16:23:22 +02:00
|
|
|
if (!ps->nr)
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(&cp.args, ":/");
|
2019-07-16 16:23:22 +02:00
|
|
|
else
|
|
|
|
add_pathspecs(&cp.args, ps);
|
|
|
|
if (run_command(&cp)) {
|
2019-02-26 00:16:23 +01:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
} else {
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
cp.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp.args, "apply", "-R", NULL);
|
2019-02-26 00:16:23 +01:00
|
|
|
|
|
|
|
if (pipe_command(&cp, patch.buf, patch.len, NULL, 0, NULL, 0)) {
|
2019-02-26 00:16:24 +01:00
|
|
|
if (!quiet)
|
|
|
|
fprintf_ln(stderr, _("Cannot remove "
|
|
|
|
"worktree changes"));
|
2019-02-26 00:16:23 +01:00
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keep_index < 1) {
|
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
|
|
|
|
cp.git_cmd = 1;
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_pushl(&cp.args, "reset", "-q", "--", NULL);
|
2019-02-26 00:16:23 +01:00
|
|
|
add_pathspecs(&cp.args, ps);
|
|
|
|
if (run_command(&cp)) {
|
|
|
|
ret = -1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
strbuf_release(&stash_msg_buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:25:21 +01:00
|
|
|
static int push_stash(int argc, const char **argv, const char *prefix,
|
|
|
|
int push_assumed)
|
2019-02-26 00:16:23 +01:00
|
|
|
{
|
2020-02-17 18:25:21 +01:00
|
|
|
int force_assume = 0;
|
2019-02-26 00:16:23 +01:00
|
|
|
int keep_index = -1;
|
2021-10-18 18:09:06 +02:00
|
|
|
int only_staged = 0;
|
2019-02-26 00:16:23 +01:00
|
|
|
int patch_mode = 0;
|
|
|
|
int include_untracked = 0;
|
|
|
|
int quiet = 0;
|
2020-02-17 18:25:22 +01:00
|
|
|
int pathspec_file_nul = 0;
|
2019-02-26 00:16:23 +01:00
|
|
|
const char *stash_msg = NULL;
|
2020-02-17 18:25:22 +01:00
|
|
|
const char *pathspec_from_file = NULL;
|
2019-02-26 00:16:23 +01:00
|
|
|
struct pathspec ps;
|
|
|
|
struct option options[] = {
|
|
|
|
OPT_BOOL('k', "keep-index", &keep_index,
|
|
|
|
N_("keep index")),
|
2021-10-18 18:09:06 +02:00
|
|
|
OPT_BOOL('S', "staged", &only_staged,
|
|
|
|
N_("stash staged changes only")),
|
2019-02-26 00:16:23 +01:00
|
|
|
OPT_BOOL('p', "patch", &patch_mode,
|
|
|
|
N_("stash in patch mode")),
|
|
|
|
OPT__QUIET(&quiet, N_("quiet mode")),
|
|
|
|
OPT_BOOL('u', "include-untracked", &include_untracked,
|
|
|
|
N_("include untracked files in stash")),
|
|
|
|
OPT_SET_INT('a', "all", &include_untracked,
|
|
|
|
N_("include ignore files"), 2),
|
|
|
|
OPT_STRING('m', "message", &stash_msg, N_("message"),
|
|
|
|
N_("stash message")),
|
2020-02-17 18:25:22 +01:00
|
|
|
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
|
|
|
|
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
|
2019-02-26 00:16:23 +01:00
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2020-02-17 18:25:21 +01:00
|
|
|
if (argc) {
|
|
|
|
force_assume = !strcmp(argv[0], "-p");
|
2019-02-26 00:16:28 +01:00
|
|
|
argc = parse_options(argc, argv, prefix, options,
|
stash: don't show "git stash push" usage on bad "git stash" usage
Change the usage message emitted by "git stash --invalid-option" to
emit usage information for "git stash" in general, and not just for
the "push" command. I.e. before:
$ git stash --invalid-option
error: unknown option `invalid-option'
usage: git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
[-u|--include-untracked] [-a|--all] [-m|--message <message>]
[--] [<pathspec>...]]
[...]
After:
$ git stash --invalid-option
error: unknown option `invalid-option'
usage: git stash list [<options>]
or: git stash show [<options>] [<stash>]
or: git stash drop [-q|--quiet] [<stash>]
or: git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]
or: git stash branch <branchname> [<stash>]
or: git stash clear
or: git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]
[-u|--include-untracked] [-a|--all] [-m|--message <message>]
[--pathspec-from-file=<file> [--pathspec-file-nul]]
[--] [<pathspec>...]]
or: git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]
[-u|--include-untracked] [-a|--all] [<message>]
[...]
That we emitted the usage for just "push" in the case of the
subcommand not being explicitly specified was an unintentional
side-effect of how it was implemented. When it was converted to C in
d553f538b8a (stash: convert push to builtin, 2019-02-25) the pattern
of having per-subcommand usage information was rightly continued. The
"git-stash.sh" shellscript did not have that, and always printed the
equivalent of "git_stash_usage".
But in doing so the case of push being implicit and explicit was
conflated. A variable was added to track this in 8c3713cede7 (stash:
eliminate crude option parsing, 2020-02-17), but it did not update the
usage output accordingly.
This still leaves e.g. "git stash push -h" emitting the
"git_stash_usage" output, instead of "git_stash_push_usage". That
should be fixed, but is a much deeper misbehavior in parse_options()
not being aware of subcommands at all. I.e. in how
PARSE_OPT_KEEP_UNKNOWN and PARSE_OPT_NO_INTERNAL_HELP combine in
commands such as "git stash".
Perhaps PARSE_OPT_KEEP_UNKNOWN should imply
PARSE_OPT_NO_INTERNAL_HELP, or better yet parse_options() should be
extended to fully handle these subcommand cases that we handle
manually in "git stash", "git commit-graph", "git multi-pack-index"
etc. All of those musings would be a much bigger change than this
isolated fix though, so let's leave that for some other time.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-12-16 13:54:21 +01:00
|
|
|
push_assumed ? git_stash_usage :
|
2019-02-26 00:16:28 +01:00
|
|
|
git_stash_push_usage,
|
2020-02-17 18:25:21 +01:00
|
|
|
PARSE_OPT_KEEP_DASHDASH);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc) {
|
|
|
|
if (!strcmp(argv[0], "--")) {
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
} else if (push_assumed && !force_assume) {
|
|
|
|
die("subcommand wasn't specified; 'push' can't be assumed due to unexpected token '%s'",
|
|
|
|
argv[0]);
|
|
|
|
}
|
|
|
|
}
|
2019-02-26 00:16:23 +01:00
|
|
|
|
2019-03-07 16:29:33 +01:00
|
|
|
parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
|
|
|
|
prefix, argv);
|
2020-02-17 18:25:22 +01:00
|
|
|
|
|
|
|
if (pathspec_from_file) {
|
|
|
|
if (patch_mode)
|
2022-01-05 21:02:16 +01:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--patch");
|
2020-02-17 18:25:22 +01:00
|
|
|
|
2021-10-18 18:09:06 +02:00
|
|
|
if (only_staged)
|
2022-01-05 21:02:16 +01:00
|
|
|
die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--staged");
|
2021-10-18 18:09:06 +02:00
|
|
|
|
2020-02-17 18:25:22 +01:00
|
|
|
if (ps.nr)
|
2022-01-05 21:02:24 +01:00
|
|
|
die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
|
2020-02-17 18:25:22 +01:00
|
|
|
|
|
|
|
parse_pathspec_file(&ps, 0,
|
|
|
|
PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
|
|
|
|
prefix, pathspec_from_file, pathspec_file_nul);
|
|
|
|
} else if (pathspec_file_nul) {
|
2022-01-05 21:02:19 +01:00
|
|
|
die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
|
2020-02-17 18:25:22 +01:00
|
|
|
}
|
|
|
|
|
2019-03-11 23:16:32 +01:00
|
|
|
return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
|
2021-10-18 18:09:06 +02:00
|
|
|
include_untracked, only_staged);
|
2019-02-26 00:16:23 +01:00
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:25 +01:00
|
|
|
static int save_stash(int argc, const char **argv, const char *prefix)
|
|
|
|
{
|
|
|
|
int keep_index = -1;
|
2021-10-18 18:09:06 +02:00
|
|
|
int only_staged = 0;
|
2019-02-26 00:16:25 +01:00
|
|
|
int patch_mode = 0;
|
|
|
|
int include_untracked = 0;
|
|
|
|
int quiet = 0;
|
|
|
|
int ret = 0;
|
|
|
|
const char *stash_msg = NULL;
|
|
|
|
struct pathspec ps;
|
|
|
|
struct strbuf stash_msg_buf = STRBUF_INIT;
|
|
|
|
struct option options[] = {
|
|
|
|
OPT_BOOL('k', "keep-index", &keep_index,
|
|
|
|
N_("keep index")),
|
2021-10-18 18:09:06 +02:00
|
|
|
OPT_BOOL('S', "staged", &only_staged,
|
|
|
|
N_("stash staged changes only")),
|
2019-02-26 00:16:25 +01:00
|
|
|
OPT_BOOL('p', "patch", &patch_mode,
|
|
|
|
N_("stash in patch mode")),
|
|
|
|
OPT__QUIET(&quiet, N_("quiet mode")),
|
|
|
|
OPT_BOOL('u', "include-untracked", &include_untracked,
|
|
|
|
N_("include untracked files in stash")),
|
|
|
|
OPT_SET_INT('a', "all", &include_untracked,
|
|
|
|
N_("include ignore files"), 2),
|
|
|
|
OPT_STRING('m', "message", &stash_msg, "message",
|
|
|
|
N_("stash message")),
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
|
|
|
argc = parse_options(argc, argv, prefix, options,
|
2019-02-26 00:16:28 +01:00
|
|
|
git_stash_save_usage,
|
2019-02-26 00:16:25 +01:00
|
|
|
PARSE_OPT_KEEP_DASHDASH);
|
|
|
|
|
|
|
|
if (argc)
|
|
|
|
stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');
|
|
|
|
|
|
|
|
memset(&ps, 0, sizeof(ps));
|
2019-03-11 23:16:32 +01:00
|
|
|
ret = do_push_stash(&ps, stash_msg, quiet, keep_index,
|
2021-10-18 18:09:06 +02:00
|
|
|
patch_mode, include_untracked, only_staged);
|
2019-02-26 00:16:25 +01:00
|
|
|
|
|
|
|
strbuf_release(&stash_msg_buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
int cmd_stash(int argc, const char **argv, const char *prefix)
|
2019-02-26 00:16:15 +01:00
|
|
|
{
|
|
|
|
pid_t pid = getpid();
|
|
|
|
const char *index_file;
|
2020-07-28 22:24:27 +02:00
|
|
|
struct strvec args = STRVEC_INIT;
|
2019-02-26 00:16:15 +01:00
|
|
|
|
|
|
|
struct option options[] = {
|
|
|
|
OPT_END()
|
|
|
|
};
|
|
|
|
|
2020-03-03 18:46:12 +01:00
|
|
|
git_config(git_stash_config, NULL);
|
2019-02-26 00:16:30 +01:00
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
argc = parse_options(argc, argv, prefix, options, git_stash_usage,
|
2019-02-26 00:16:15 +01:00
|
|
|
PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
|
|
|
|
|
|
|
|
index_file = get_index_file();
|
|
|
|
strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file,
|
|
|
|
(uintmax_t)pid);
|
|
|
|
|
2019-02-26 00:16:28 +01:00
|
|
|
if (!argc)
|
2020-02-17 18:25:21 +01:00
|
|
|
return !!push_stash(0, NULL, prefix, 0);
|
2019-02-26 00:16:28 +01:00
|
|
|
else if (!strcmp(argv[0], "apply"))
|
2019-02-26 00:16:15 +01:00
|
|
|
return !!apply_stash(argc, argv, prefix);
|
2019-02-26 00:16:16 +01:00
|
|
|
else if (!strcmp(argv[0], "clear"))
|
|
|
|
return !!clear_stash(argc, argv, prefix);
|
|
|
|
else if (!strcmp(argv[0], "drop"))
|
|
|
|
return !!drop_stash(argc, argv, prefix);
|
2019-02-26 00:16:18 +01:00
|
|
|
else if (!strcmp(argv[0], "pop"))
|
|
|
|
return !!pop_stash(argc, argv, prefix);
|
2019-02-26 00:16:17 +01:00
|
|
|
else if (!strcmp(argv[0], "branch"))
|
|
|
|
return !!branch_stash(argc, argv, prefix);
|
2019-02-26 00:16:19 +01:00
|
|
|
else if (!strcmp(argv[0], "list"))
|
|
|
|
return !!list_stash(argc, argv, prefix);
|
2019-02-26 00:16:20 +01:00
|
|
|
else if (!strcmp(argv[0], "show"))
|
|
|
|
return !!show_stash(argc, argv, prefix);
|
2019-02-26 00:16:21 +01:00
|
|
|
else if (!strcmp(argv[0], "store"))
|
|
|
|
return !!store_stash(argc, argv, prefix);
|
2019-02-26 00:16:22 +01:00
|
|
|
else if (!strcmp(argv[0], "create"))
|
|
|
|
return !!create_stash(argc, argv, prefix);
|
2019-02-26 00:16:23 +01:00
|
|
|
else if (!strcmp(argv[0], "push"))
|
2020-02-17 18:25:21 +01:00
|
|
|
return !!push_stash(argc, argv, prefix, 0);
|
2019-02-26 00:16:25 +01:00
|
|
|
else if (!strcmp(argv[0], "save"))
|
|
|
|
return !!save_stash(argc, argv, prefix);
|
2019-02-26 00:16:28 +01:00
|
|
|
else if (*argv[0] != '-')
|
2021-12-28 14:28:43 +01:00
|
|
|
usage_msg_optf(_("unknown subcommand: %s"),
|
|
|
|
git_stash_usage, options, argv[0]);
|
2019-02-26 00:16:28 +01:00
|
|
|
|
2020-02-17 18:25:21 +01:00
|
|
|
/* Assume 'stash push' */
|
2020-07-28 22:24:27 +02:00
|
|
|
strvec_push(&args, "push");
|
|
|
|
strvec_pushv(&args, argv);
|
2020-07-29 02:37:20 +02:00
|
|
|
return !!push_stash(args.nr, args.v, prefix, 1);
|
2019-02-26 00:16:15 +01:00
|
|
|
}
|