diff options
Diffstat (limited to 'builtin/stash.c')
| -rw-r--r-- | builtin/stash.c | 541 |
1 files changed, 511 insertions, 30 deletions
diff --git a/builtin/stash.c b/builtin/stash.c index dbaa999cf1..1977e50df2 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -13,7 +13,6 @@ #include "lockfile.h" #include "cache-tree.h" #include "unpack-trees.h" -#include "merge-recursive.h" #include "merge-ort-wrappers.h" #include "strvec.h" #include "run-command.h" @@ -29,7 +28,10 @@ #include "log-tree.h" #include "diffcore.h" #include "reflog.h" +#include "reflog-walk.h" #include "add-interactive.h" +#include "oid-array.h" +#include "commit.h" #define INCLUDE_ALL_FILES 2 @@ -57,6 +59,10 @@ " [-u | --include-untracked] [-a | --all] [<message>]") #define BUILTIN_STASH_CREATE_USAGE \ N_("git stash create [<message>]") +#define BUILTIN_STASH_EXPORT_USAGE \ + N_("git stash export (--print | --to-ref <ref>) [<stash>...]") +#define BUILTIN_STASH_IMPORT_USAGE \ + N_("git stash import <commit>") #define BUILTIN_STASH_CLEAR_USAGE \ "git stash clear" @@ -72,6 +78,8 @@ static const char * const git_stash_usage[] = { BUILTIN_STASH_CLEAR_USAGE, BUILTIN_STASH_CREATE_USAGE, BUILTIN_STASH_STORE_USAGE, + BUILTIN_STASH_EXPORT_USAGE, + BUILTIN_STASH_IMPORT_USAGE, NULL }; @@ -125,6 +133,16 @@ static const char * const git_stash_save_usage[] = { NULL }; +static const char * const git_stash_export_usage[] = { + BUILTIN_STASH_EXPORT_USAGE, + NULL +}; + +static const char * const git_stash_import_usage[] = { + BUILTIN_STASH_IMPORT_USAGE, + NULL +}; + static const char ref_stash[] = "refs/stash"; static struct strbuf stash_index_path = STRBUF_INIT; @@ -133,6 +151,7 @@ static struct strbuf stash_index_path = STRBUF_INIT; * 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 + * c_commit is set to the first parent (chain commit) when importing and is otherwise unset * w_tree is set to the working tree * b_tree is set to the base tree * i_tree is set to the index tree @@ -143,6 +162,7 @@ struct stash_info { struct object_id b_commit; struct object_id i_commit; struct object_id u_commit; + struct object_id c_commit; struct object_id w_tree; struct object_id b_tree; struct object_id i_tree; @@ -161,6 +181,33 @@ static void free_stash_info(struct stash_info *info) strbuf_release(&info->revision); } +static int check_stash_topology(struct repository *r, struct commit *stash) +{ + struct commit *p1, *p2, *p3 = NULL; + + /* stash must have two or three parents */ + if (!stash->parents || !stash->parents->next || + (stash->parents->next->next && stash->parents->next->next->next)) + return -1; + p1 = stash->parents->item; + p2 = stash->parents->next->item; + if (stash->parents->next->next) + p3 = stash->parents->next->next->item; + if (repo_parse_commit(r, p1) || repo_parse_commit(r, p2) || + (p3 && repo_parse_commit(r, p3))) + return -1; + /* p2 must have a single parent, p3 must have no parents */ + if (!p2->parents || p2->parents->next || (p3 && p3->parents)) + return -1; + if (repo_parse_commit(r, p2->parents->item)) + return -1; + /* p2^1 must equal p1 */ + if (!oideq(&p1->object.oid, &p2->parents->item->object.oid)) + return -1; + + return 0; +} + static void assert_stash_like(struct stash_info *info, const char *revision) { if (get_oidf(&info->b_commit, "%s^1", revision) || @@ -170,6 +217,25 @@ static void assert_stash_like(struct stash_info *info, const char *revision) die(_("'%s' is not a stash-like commit"), revision); } +static int parse_stash_revision(struct strbuf *revision, const char *commit, int quiet) +{ + strbuf_reset(revision); + if (!commit) { + if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash)) { + if (!quiet) + fprintf_ln(stderr, _("No stash entries found.")); + return -1; + } + + strbuf_addf(revision, "%s@{0}", ref_stash); + } else if (strspn(commit, "0123456789") == strlen(commit)) { + strbuf_addf(revision, "%s@{%s}", ref_stash, commit); + } else { + strbuf_addstr(revision, commit); + } + return 0; +} + static int get_stash_info(struct stash_info *info, int argc, const char **argv) { int ret; @@ -197,17 +263,9 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv) if (argc == 1) commit = argv[0]; - if (!commit) { - if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash)) { - 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); + strbuf_init(&info->revision, 0); + if (parse_stash_revision(&info->revision, commit, 0)) { + return -1; } revision = info->revision.buf; @@ -921,7 +979,7 @@ static int show_stash(int argc, const char **argv, const char *prefix, int do_usage = 0; init_diff_ui_defaults(); - git_config(git_diff_ui_config, NULL); + repo_config(the_repository, git_diff_ui_config, NULL); repo_init_revisions(the_repository, &rev, prefix); argc = parse_options(argc, argv, prefix, options, git_stash_show_usage, @@ -1243,7 +1301,8 @@ done: } static int stash_patch(struct stash_info *info, const struct pathspec *ps, - struct strbuf *out_patch, int quiet) + struct strbuf *out_patch, int quiet, + struct add_p_opt *add_p_opt) { int ret = 0; struct child_process cp_read_tree = CHILD_PROCESS_INIT; @@ -1268,7 +1327,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps); + ret = !!run_add_p(the_repository, ADD_P_STASH, add_p_opt, NULL, ps); the_repository->index_file = old_repo_index_file; if (old_index_env && *old_index_env) @@ -1363,8 +1422,8 @@ done: } static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf, - int include_untracked, int patch_mode, int only_staged, - struct stash_info *info, struct strbuf *patch, + int include_untracked, int patch_mode, struct add_p_opt *add_p_opt, + int only_staged, struct stash_info *info, struct strbuf *patch, int quiet) { int ret = 0; @@ -1373,6 +1432,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b const char *head_short_sha1 = NULL; const char *branch_ref = NULL; const char *branch_name = "(no branch)"; + char *branch_name_buf = NULL; struct commit *head_commit = NULL; struct commit_list *parents = NULL; struct strbuf msg = STRBUF_INIT; @@ -1405,8 +1465,12 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b branch_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, &flags); - if (flags & REF_ISSYMREF) - skip_prefix(branch_ref, "refs/heads/", &branch_name); + + if (flags & REF_ISSYMREF) { + if (skip_prefix(branch_ref, "refs/heads/", &branch_name)) + branch_name = branch_name_buf = xstrdup(branch_name); + } + head_short_sha1 = repo_find_unique_abbrev(the_repository, &head_commit->object.oid, DEFAULT_ABBREV); @@ -1440,7 +1504,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b untracked_commit_option = 1; } if (patch_mode) { - ret = stash_patch(info, ps, patch, quiet); + ret = stash_patch(info, ps, patch, quiet, add_p_opt); if (ret < 0) { if (!quiet) fprintf_ln(stderr, _("Cannot save the current " @@ -1496,6 +1560,7 @@ done: strbuf_release(&msg); strbuf_release(&untracked_files); free_commit_list(parents); + free(branch_name_buf); return ret; } @@ -1514,7 +1579,7 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, if (!check_changes_tracked_files(&ps)) return 0; - ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info, + ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info, NULL, 0); if (!ret) printf_ln("%s", oid_to_hex(&info.w_commit)); @@ -1525,7 +1590,8 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, } static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet, - int keep_index, int patch_mode, int include_untracked, int only_staged) + int keep_index, int patch_mode, struct add_p_opt *add_p_opt, + int include_untracked, int only_staged) { int ret = 0; struct stash_info info = STASH_INFO_INIT; @@ -1595,8 +1661,8 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q if (stash_msg) strbuf_addstr(&stash_msg_buf, stash_msg); - if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged, - &info, &patch, quiet)) { + if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, + add_p_opt, only_staged, &info, &patch, quiet)) { ret = -1; goto done; } @@ -1769,6 +1835,7 @@ static int push_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; char *pathspec_from_file = NULL; struct pathspec ps; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1776,6 +1843,8 @@ static int push_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1790,11 +1859,15 @@ static int push_stash(int argc, const char **argv, const char *prefix, int ret; if (argc) { - force_assume = !strcmp(argv[0], "-p"); + int flags = PARSE_OPT_KEEP_DASHDASH; + + if (push_assumed) + flags |= PARSE_OPT_STOP_AT_NON_OPTION; + argc = parse_options(argc, argv, prefix, options, push_assumed ? git_stash_usage : - git_stash_push_usage, - PARSE_OPT_KEEP_DASHDASH); + git_stash_push_usage, flags); + force_assume |= patch_mode; } if (argc) { @@ -1827,8 +1900,20 @@ static int push_stash(int argc, const char **argv, const char *prefix, die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, - include_untracked, only_staged); + &add_p_opt, include_untracked, only_staged); clear_pathspec(&ps); free(pathspec_from_file); @@ -1853,6 +1938,7 @@ static int save_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; struct pathspec ps; struct strbuf stash_msg_buf = STRBUF_INIT; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1860,6 +1946,8 @@ static int save_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1878,13 +1966,404 @@ static int save_stash(int argc, const char **argv, const char *prefix, stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' '); memset(&ps, 0, sizeof(ps)); + + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, - patch_mode, include_untracked, only_staged); + patch_mode, &add_p_opt, include_untracked, + only_staged); strbuf_release(&stash_msg_buf); return ret; } +static int write_commit_with_parents(struct repository *r, + struct object_id *out, + const struct object_id *oid, + struct commit_list *parents) +{ + size_t author_len, committer_len; + struct commit *this; + const char *orig_author, *orig_committer; + char *author = NULL, *committer = NULL; + const char *buffer; + unsigned long bufsize; + const char *p; + struct strbuf msg = STRBUF_INIT; + int ret = 0; + struct ident_split id; + + this = lookup_commit_reference(r, oid); + buffer = repo_get_commit_buffer(r, this, &bufsize); + orig_author = find_commit_header(buffer, "author", &author_len); + orig_committer = find_commit_header(buffer, "committer", &committer_len); + + if (!orig_author || !orig_committer) { + ret = error(_("cannot parse commit %s"), oid_to_hex(oid)); + goto out; + } + + if (split_ident_line(&id, orig_author, author_len) < 0 || + split_ident_line(&id, orig_committer, committer_len) < 0) { + ret = error(_("invalid author or committer for %s"), oid_to_hex(oid)); + goto out; + } + + p = strstr(buffer, "\n\n"); + strbuf_addstr(&msg, "git stash: "); + + if (p) + strbuf_add(&msg, p + 2, bufsize - (p + 2 - buffer)); + strbuf_complete_line(&msg); + + author = xmemdupz(orig_author, author_len); + committer = xmemdupz(orig_committer, committer_len); + + if (commit_tree_extended(msg.buf, msg.len, + r->hash_algo->empty_tree, parents, + out, author, committer, + NULL, NULL)) { + ret = error(_("could not write commit")); + goto out; + } +out: + strbuf_release(&msg); + repo_unuse_commit_buffer(r, this, buffer); + free(author); + free(committer); + return ret; +} + +static int do_import_stash(struct repository *r, const char *rev) +{ + struct object_id chain; + int res = 0; + const char *buffer = NULL; + unsigned long bufsize; + struct commit *this = NULL; + struct commit_list *items = NULL, *cur; + char *msg = NULL; + + if (repo_get_oid(r, rev, &chain)) + return error(_("not a valid revision: %s"), rev); + + this = lookup_commit_reference(r, &chain); + if (!this) + return error(_("not a commit: %s"), rev); + + /* + * Walk the commit history, finding each stash entry, and load data into + * the array. + */ + for (;;) { + const char *author, *committer; + size_t author_len, committer_len; + const char *p; + const char *expected = "git stash <git@stash> 1000684800 +0000"; + const char *prefix = "git stash: "; + struct commit *stash; + struct tree *tree = repo_get_commit_tree(r, this); + + if (!tree || + !oideq(&tree->object.oid, r->hash_algo->empty_tree) || + (this->parents && + (!this->parents->next || this->parents->next->next))) { + res = error(_("%s is not a valid exported stash commit"), + oid_to_hex(&this->object.oid)); + goto out; + } + + buffer = repo_get_commit_buffer(r, this, &bufsize); + + if (!this->parents) { + /* + * We don't have any parents. Make sure this is our + * root commit. + */ + author = find_commit_header(buffer, "author", &author_len); + committer = find_commit_header(buffer, "committer", &committer_len); + + if (!author || !committer) { + error(_("cannot parse commit %s"), oid_to_hex(&this->object.oid)); + goto out; + } + + if (author_len != strlen(expected) || + committer_len != strlen(expected) || + memcmp(author, expected, author_len) || + memcmp(committer, expected, committer_len)) { + res = error(_("found root commit %s with invalid data"), oid_to_hex(&this->object.oid)); + goto out; + } + break; + } + + p = strstr(buffer, "\n\n"); + if (!p) { + res = error(_("cannot parse commit %s"), oid_to_hex(&this->object.oid)); + goto out; + } + + p += 2; + if (((size_t)(bufsize - (p - buffer)) < strlen(prefix)) || + memcmp(prefix, p, strlen(prefix))) { + res = error(_("found stash commit %s without expected prefix"), oid_to_hex(&this->object.oid)); + goto out; + } + + stash = this->parents->next->item; + + if (repo_parse_commit(r, this->parents->item) || + repo_parse_commit(r, stash)) { + res = error(_("cannot parse parents of commit: %s"), + oid_to_hex(&this->object.oid)); + goto out; + } + + if (check_stash_topology(r, stash)) { + res = error(_("%s does not look like a stash commit"), + oid_to_hex(&stash->object.oid)); + goto out; + } + + repo_unuse_commit_buffer(r, this, buffer); + buffer = NULL; + items = commit_list_insert(stash, &items); + this = this->parents->item; + } + + /* + * Now, walk each entry, adding it to the stash as a normal stash + * commit. + */ + for (cur = items; cur; cur = cur->next) { + const char *p; + struct object_id *oid; + + this = cur->item; + oid = &this->object.oid; + buffer = repo_get_commit_buffer(r, this, &bufsize); + if (!buffer) { + res = error(_("cannot read commit buffer for %s"), oid_to_hex(oid)); + goto out; + } + + p = strstr(buffer, "\n\n"); + if (!p) { + res = error(_("cannot parse commit %s"), oid_to_hex(oid)); + goto out; + } + + p += 2; + msg = xmemdupz(p, bufsize - (p - buffer)); + repo_unuse_commit_buffer(r, this, buffer); + buffer = NULL; + + if (do_store_stash(oid, msg, 1)) { + res = error(_("cannot save the stash for %s"), oid_to_hex(oid)); + goto out; + } + FREE_AND_NULL(msg); + } +out: + if (this && buffer) + repo_unuse_commit_buffer(r, this, buffer); + free_commit_list(items); + free(msg); + + return res; +} + +static int import_stash(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + struct option options[] = { + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, + git_stash_import_usage, + PARSE_OPT_KEEP_DASHDASH); + + if (argc != 1) + usage_msg_opt("a revision is required", git_stash_import_usage, options); + + return do_import_stash(repo, argv[0]); +} + +struct stash_entry_data { + struct repository *r; + struct commit_list **items; + size_t count; +}; + +static int collect_stash_entries(struct object_id *old_oid UNUSED, + struct object_id *new_oid, + const char *committer UNUSED, + timestamp_t timestamp UNUSED, + int tz UNUSED, const char *msg UNUSED, + void *cb_data) +{ + struct stash_entry_data *data = cb_data; + struct commit *stash; + + data->count++; + stash = lookup_commit_reference(data->r, new_oid); + if (!stash || check_stash_topology(data->r, stash)) { + return error(_("%s does not look like a stash commit"), + oid_to_hex(new_oid)); + } + data->items = commit_list_append(stash, data->items); + return 0; +} + +static int do_export_stash(struct repository *r, + const char *ref, + int argc, + const char **argv) +{ + struct object_id base; + struct object_context unused; + struct commit *prev; + struct commit_list *items = NULL, **iter = &items, *cur; + int res = 0; + int i; + struct strbuf revision = STRBUF_INIT; + const char *author, *committer; + + /* + * This is an arbitrary, fixed date, specifically the one used by git + * format-patch. The goal is merely to produce reproducible output. + */ + prepare_fallback_ident("git stash", "git@stash"); + author = fmt_ident("git stash", "git@stash", WANT_BLANK_IDENT, + "2001-09-17T00:00:00Z", 0); + committer = fmt_ident("git stash", "git@stash", WANT_BLANK_IDENT, + "2001-09-17T00:00:00Z", 0); + + /* First, we create a single empty commit. */ + if (commit_tree_extended("", 0, r->hash_algo->empty_tree, NULL, + &base, author, committer, NULL, NULL)) + return error(_("unable to write base commit")); + + prev = lookup_commit_reference(r, &base); + + if (argc) { + /* + * Find each specified stash, and load data into the array. + */ + for (i = 0; i < argc; i++) { + struct object_id oid; + struct commit *stash; + + if (parse_stash_revision(&revision, argv[i], 1) || + get_oid_with_context(r, revision.buf, + GET_OID_QUIETLY | GET_OID_GENTLY, + &oid, &unused)) { + res = error(_("unable to find stash entry %s"), argv[i]); + goto out; + } + + stash = lookup_commit_reference(r, &oid); + if (!stash || check_stash_topology(r, stash)) { + res = error(_("%s does not look like a stash commit"), + revision.buf); + goto out; + } + iter = commit_list_append(stash, iter); + } + } else { + /* + * Walk the reflog, finding each stash entry, and load data into the + * array. + */ + struct stash_entry_data cb_data = { + .r = r, .items = iter, + }; + if (refs_for_each_reflog_ent_reverse(get_main_ref_store(r), + "refs/stash", + collect_stash_entries, + &cb_data) && cb_data.count) + goto out; + } + + /* + * Now, create a set of commits identical to the regular stash commits, + * but where their first parents form a chain to our original empty + * base commit. + */ + items = reverse_commit_list(items); + for (cur = items; cur; cur = cur->next) { + struct commit_list *parents = NULL; + struct commit_list **next = &parents; + struct object_id out; + struct commit *stash = cur->item; + + next = commit_list_append(prev, next); + next = commit_list_append(stash, next); + res = write_commit_with_parents(r, &out, &stash->object.oid, parents); + free_commit_list(parents); + if (res) + goto out; + prev = lookup_commit_reference(r, &out); + } + if (ref) + refs_update_ref(get_main_ref_store(r), NULL, ref, + &prev->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); + else + puts(oid_to_hex(&prev->object.oid)); +out: + strbuf_release(&revision); + free_commit_list(items); + + return res; +} + +enum export_action { + ACTION_NONE, + ACTION_PRINT, + ACTION_TO_REF, +}; + +static int export_stash(int argc, + const char **argv, + const char *prefix, + struct repository *repo) +{ + const char *ref = NULL; + enum export_action action = ACTION_NONE; + struct option options[] = { + OPT_CMDMODE(0, "print", &action, + N_("print the object ID instead of writing it to a ref"), + ACTION_PRINT), + OPT_STRING(0, "to-ref", &ref, "ref", + N_("save the data to the given ref")), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, + git_stash_export_usage, + PARSE_OPT_KEEP_DASHDASH); + + if (ref && action == ACTION_NONE) + action = ACTION_TO_REF; + + if (action == ACTION_NONE || (ref && action == ACTION_PRINT)) + return error(_("exactly one of --print and --to-ref is required")); + + return do_export_stash(repo, ref, argc, argv); +} + int cmd_stash(int argc, const char **argv, const char *prefix, @@ -1905,13 +2384,15 @@ int cmd_stash(int argc, OPT_SUBCOMMAND("store", &fn, store_stash), OPT_SUBCOMMAND("create", &fn, create_stash), OPT_SUBCOMMAND("push", &fn, push_stash_unassumed), + OPT_SUBCOMMAND("export", &fn, export_stash), + OPT_SUBCOMMAND("import", &fn, import_stash), OPT_SUBCOMMAND_F("save", &fn, save_stash, PARSE_OPT_NOCOMPLETE), OPT_END() }; const char **args_copy; int ret; - git_config(git_stash_config, NULL); + repo_config(the_repository, git_stash_config, NULL); argc = parse_options(argc, argv, prefix, options, git_stash_usage, PARSE_OPT_SUBCOMMAND_OPTIONAL | |
