Git fork

Merge branch 'bc/stash-export-import'

An interchange format for stash entries is defined, and subcommand
of "git stash" to import/export has been added.

* bc/stash-export-import:
builtin/stash: provide a way to import stashes from a ref
builtin/stash: provide a way to export stashes to a ref
builtin/stash: factor out revision parsing into a function
object-name: make get_oid quietly return an error

+584 -13
+28 -1
Documentation/git-stash.adoc
··· 23 23 'git stash' clear 24 24 'git stash' create [<message>] 25 25 'git stash' store [(-m | --message) <message>] [-q | --quiet] <commit> 26 + 'git stash' export (--print | --to-ref <ref>) [<stash>...] 27 + 'git stash' import <commit> 26 28 27 29 DESCRIPTION 28 30 ----------- ··· 154 156 reflog. This is intended to be useful for scripts. It is 155 157 probably not the command you want to use; see "push" above. 156 158 159 + export ( --print | --to-ref <ref> ) [<stash>...]:: 160 + 161 + Export the specified stashes, or all of them if none are specified, to 162 + a chain of commits which can be transferred using the normal fetch and 163 + push mechanisms, then imported using the `import` subcommand. 164 + 165 + import <commit>:: 166 + 167 + Import the specified stashes from the specified commit, which must have been 168 + created by `export`, and add them to the list of stashes. To replace the 169 + existing stashes, use `clear` first. 170 + 157 171 OPTIONS 158 172 ------- 159 173 -a:: ··· 242 256 + 243 257 Quiet, suppress feedback messages. 244 258 259 + --print:: 260 + This option is only valid for the `export` command. 261 + + 262 + Create the chain of commits representing the exported stashes without 263 + storing it anywhere in the ref namespace and print the object ID to 264 + standard output. This is designed for scripts. 265 + 266 + --to-ref:: 267 + This option is only valid for the `export` command. 268 + + 269 + Create the chain of commits representing the exported stashes and store 270 + it to the specified ref. 271 + 245 272 \--:: 246 273 This option is only valid for `push` command. 247 274 + ··· 259 286 260 287 <stash>:: 261 288 This option is only valid for `apply`, `branch`, `drop`, `pop`, 262 - `show` commands. 289 + `show`, and `export` commands. 263 290 + 264 291 A reference of the form `stash@{<revision>}`. When no `<stash>` is 265 292 given, the latest stash is assumed (that is, `stash@{0}`).
+449 -11
builtin/stash.c
··· 28 28 #include "log-tree.h" 29 29 #include "diffcore.h" 30 30 #include "reflog.h" 31 + #include "reflog-walk.h" 31 32 #include "add-interactive.h" 33 + #include "oid-array.h" 34 + #include "commit.h" 32 35 33 36 #define INCLUDE_ALL_FILES 2 34 37 ··· 56 59 " [-u | --include-untracked] [-a | --all] [<message>]") 57 60 #define BUILTIN_STASH_CREATE_USAGE \ 58 61 N_("git stash create [<message>]") 62 + #define BUILTIN_STASH_EXPORT_USAGE \ 63 + N_("git stash export (--print | --to-ref <ref>) [<stash>...]") 64 + #define BUILTIN_STASH_IMPORT_USAGE \ 65 + N_("git stash import <commit>") 59 66 #define BUILTIN_STASH_CLEAR_USAGE \ 60 67 "git stash clear" 61 68 ··· 71 78 BUILTIN_STASH_CLEAR_USAGE, 72 79 BUILTIN_STASH_CREATE_USAGE, 73 80 BUILTIN_STASH_STORE_USAGE, 81 + BUILTIN_STASH_EXPORT_USAGE, 82 + BUILTIN_STASH_IMPORT_USAGE, 74 83 NULL 75 84 }; 76 85 ··· 124 133 NULL 125 134 }; 126 135 136 + static const char * const git_stash_export_usage[] = { 137 + BUILTIN_STASH_EXPORT_USAGE, 138 + NULL 139 + }; 140 + 141 + static const char * const git_stash_import_usage[] = { 142 + BUILTIN_STASH_IMPORT_USAGE, 143 + NULL 144 + }; 145 + 127 146 static const char ref_stash[] = "refs/stash"; 128 147 static struct strbuf stash_index_path = STRBUF_INIT; 129 148 ··· 132 151 * b_commit is set to the base commit 133 152 * i_commit is set to the commit containing the index tree 134 153 * u_commit is set to the commit containing the untracked files tree 154 + * c_commit is set to the first parent (chain commit) when importing and is otherwise unset 135 155 * w_tree is set to the working tree 136 156 * b_tree is set to the base tree 137 157 * i_tree is set to the index tree ··· 142 162 struct object_id b_commit; 143 163 struct object_id i_commit; 144 164 struct object_id u_commit; 165 + struct object_id c_commit; 145 166 struct object_id w_tree; 146 167 struct object_id b_tree; 147 168 struct object_id i_tree; ··· 160 181 strbuf_release(&info->revision); 161 182 } 162 183 184 + static int check_stash_topology(struct repository *r, struct commit *stash) 185 + { 186 + struct commit *p1, *p2, *p3 = NULL; 187 + 188 + /* stash must have two or three parents */ 189 + if (!stash->parents || !stash->parents->next || 190 + (stash->parents->next->next && stash->parents->next->next->next)) 191 + return -1; 192 + p1 = stash->parents->item; 193 + p2 = stash->parents->next->item; 194 + if (stash->parents->next->next) 195 + p3 = stash->parents->next->next->item; 196 + if (repo_parse_commit(r, p1) || repo_parse_commit(r, p2) || 197 + (p3 && repo_parse_commit(r, p3))) 198 + return -1; 199 + /* p2 must have a single parent, p3 must have no parents */ 200 + if (!p2->parents || p2->parents->next || (p3 && p3->parents)) 201 + return -1; 202 + if (repo_parse_commit(r, p2->parents->item)) 203 + return -1; 204 + /* p2^1 must equal p1 */ 205 + if (!oideq(&p1->object.oid, &p2->parents->item->object.oid)) 206 + return -1; 207 + 208 + return 0; 209 + } 210 + 163 211 static void assert_stash_like(struct stash_info *info, const char *revision) 164 212 { 165 213 if (get_oidf(&info->b_commit, "%s^1", revision) || ··· 169 217 die(_("'%s' is not a stash-like commit"), revision); 170 218 } 171 219 220 + static int parse_stash_revision(struct strbuf *revision, const char *commit, int quiet) 221 + { 222 + strbuf_reset(revision); 223 + if (!commit) { 224 + if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash)) { 225 + if (!quiet) 226 + fprintf_ln(stderr, _("No stash entries found.")); 227 + return -1; 228 + } 229 + 230 + strbuf_addf(revision, "%s@{0}", ref_stash); 231 + } else if (strspn(commit, "0123456789") == strlen(commit)) { 232 + strbuf_addf(revision, "%s@{%s}", ref_stash, commit); 233 + } else { 234 + strbuf_addstr(revision, commit); 235 + } 236 + return 0; 237 + } 238 + 172 239 static int get_stash_info(struct stash_info *info, int argc, const char **argv) 173 240 { 174 241 int ret; ··· 196 263 if (argc == 1) 197 264 commit = argv[0]; 198 265 199 - if (!commit) { 200 - if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash)) { 201 - fprintf_ln(stderr, _("No stash entries found.")); 202 - return -1; 203 - } 204 - 205 - strbuf_addf(&info->revision, "%s@{0}", ref_stash); 206 - } else if (strspn(commit, "0123456789") == strlen(commit)) { 207 - strbuf_addf(&info->revision, "%s@{%s}", ref_stash, commit); 208 - } else { 209 - strbuf_addstr(&info->revision, commit); 266 + strbuf_init(&info->revision, 0); 267 + if (parse_stash_revision(&info->revision, commit, 0)) { 268 + return -1; 210 269 } 211 270 212 271 revision = info->revision.buf; ··· 1894 1953 return ret; 1895 1954 } 1896 1955 1956 + static int write_commit_with_parents(struct repository *r, 1957 + struct object_id *out, 1958 + const struct object_id *oid, 1959 + struct commit_list *parents) 1960 + { 1961 + size_t author_len, committer_len; 1962 + struct commit *this; 1963 + const char *orig_author, *orig_committer; 1964 + char *author = NULL, *committer = NULL; 1965 + const char *buffer; 1966 + unsigned long bufsize; 1967 + const char *p; 1968 + struct strbuf msg = STRBUF_INIT; 1969 + int ret = 0; 1970 + struct ident_split id; 1971 + 1972 + this = lookup_commit_reference(r, oid); 1973 + buffer = repo_get_commit_buffer(r, this, &bufsize); 1974 + orig_author = find_commit_header(buffer, "author", &author_len); 1975 + orig_committer = find_commit_header(buffer, "committer", &committer_len); 1976 + 1977 + if (!orig_author || !orig_committer) { 1978 + ret = error(_("cannot parse commit %s"), oid_to_hex(oid)); 1979 + goto out; 1980 + } 1981 + 1982 + if (split_ident_line(&id, orig_author, author_len) < 0 || 1983 + split_ident_line(&id, orig_committer, committer_len) < 0) { 1984 + ret = error(_("invalid author or committer for %s"), oid_to_hex(oid)); 1985 + goto out; 1986 + } 1987 + 1988 + p = strstr(buffer, "\n\n"); 1989 + strbuf_addstr(&msg, "git stash: "); 1990 + 1991 + if (p) 1992 + strbuf_add(&msg, p + 2, bufsize - (p + 2 - buffer)); 1993 + strbuf_complete_line(&msg); 1994 + 1995 + author = xmemdupz(orig_author, author_len); 1996 + committer = xmemdupz(orig_committer, committer_len); 1997 + 1998 + if (commit_tree_extended(msg.buf, msg.len, 1999 + r->hash_algo->empty_tree, parents, 2000 + out, author, committer, 2001 + NULL, NULL)) { 2002 + ret = error(_("could not write commit")); 2003 + goto out; 2004 + } 2005 + out: 2006 + strbuf_release(&msg); 2007 + repo_unuse_commit_buffer(r, this, buffer); 2008 + free(author); 2009 + free(committer); 2010 + return ret; 2011 + } 2012 + 2013 + static int do_import_stash(struct repository *r, const char *rev) 2014 + { 2015 + struct object_id chain; 2016 + int res = 0; 2017 + const char *buffer = NULL; 2018 + unsigned long bufsize; 2019 + struct commit *this = NULL; 2020 + struct commit_list *items = NULL, *cur; 2021 + char *msg = NULL; 2022 + 2023 + if (repo_get_oid(r, rev, &chain)) 2024 + return error(_("not a valid revision: %s"), rev); 2025 + 2026 + this = lookup_commit_reference(r, &chain); 2027 + if (!this) 2028 + return error(_("not a commit: %s"), rev); 2029 + 2030 + /* 2031 + * Walk the commit history, finding each stash entry, and load data into 2032 + * the array. 2033 + */ 2034 + for (;;) { 2035 + const char *author, *committer; 2036 + size_t author_len, committer_len; 2037 + const char *p; 2038 + const char *expected = "git stash <git@stash> 1000684800 +0000"; 2039 + const char *prefix = "git stash: "; 2040 + struct commit *stash; 2041 + struct tree *tree = repo_get_commit_tree(r, this); 2042 + 2043 + if (!tree || 2044 + !oideq(&tree->object.oid, r->hash_algo->empty_tree) || 2045 + (this->parents && 2046 + (!this->parents->next || this->parents->next->next))) { 2047 + res = error(_("%s is not a valid exported stash commit"), 2048 + oid_to_hex(&this->object.oid)); 2049 + goto out; 2050 + } 2051 + 2052 + buffer = repo_get_commit_buffer(r, this, &bufsize); 2053 + 2054 + if (!this->parents) { 2055 + /* 2056 + * We don't have any parents. Make sure this is our 2057 + * root commit. 2058 + */ 2059 + author = find_commit_header(buffer, "author", &author_len); 2060 + committer = find_commit_header(buffer, "committer", &committer_len); 2061 + 2062 + if (!author || !committer) { 2063 + error(_("cannot parse commit %s"), oid_to_hex(&this->object.oid)); 2064 + goto out; 2065 + } 2066 + 2067 + if (author_len != strlen(expected) || 2068 + committer_len != strlen(expected) || 2069 + memcmp(author, expected, author_len) || 2070 + memcmp(committer, expected, committer_len)) { 2071 + res = error(_("found root commit %s with invalid data"), oid_to_hex(&this->object.oid)); 2072 + goto out; 2073 + } 2074 + break; 2075 + } 2076 + 2077 + p = strstr(buffer, "\n\n"); 2078 + if (!p) { 2079 + res = error(_("cannot parse commit %s"), oid_to_hex(&this->object.oid)); 2080 + goto out; 2081 + } 2082 + 2083 + p += 2; 2084 + if (((size_t)(bufsize - (p - buffer)) < strlen(prefix)) || 2085 + memcmp(prefix, p, strlen(prefix))) { 2086 + res = error(_("found stash commit %s without expected prefix"), oid_to_hex(&this->object.oid)); 2087 + goto out; 2088 + } 2089 + 2090 + stash = this->parents->next->item; 2091 + 2092 + if (repo_parse_commit(r, this->parents->item) || 2093 + repo_parse_commit(r, stash)) { 2094 + res = error(_("cannot parse parents of commit: %s"), 2095 + oid_to_hex(&this->object.oid)); 2096 + goto out; 2097 + } 2098 + 2099 + if (check_stash_topology(r, stash)) { 2100 + res = error(_("%s does not look like a stash commit"), 2101 + oid_to_hex(&stash->object.oid)); 2102 + goto out; 2103 + } 2104 + 2105 + repo_unuse_commit_buffer(r, this, buffer); 2106 + buffer = NULL; 2107 + items = commit_list_insert(stash, &items); 2108 + this = this->parents->item; 2109 + } 2110 + 2111 + /* 2112 + * Now, walk each entry, adding it to the stash as a normal stash 2113 + * commit. 2114 + */ 2115 + for (cur = items; cur; cur = cur->next) { 2116 + const char *p; 2117 + struct object_id *oid; 2118 + 2119 + this = cur->item; 2120 + oid = &this->object.oid; 2121 + buffer = repo_get_commit_buffer(r, this, &bufsize); 2122 + if (!buffer) { 2123 + res = error(_("cannot read commit buffer for %s"), oid_to_hex(oid)); 2124 + goto out; 2125 + } 2126 + 2127 + p = strstr(buffer, "\n\n"); 2128 + if (!p) { 2129 + res = error(_("cannot parse commit %s"), oid_to_hex(oid)); 2130 + goto out; 2131 + } 2132 + 2133 + p += 2; 2134 + msg = xmemdupz(p, bufsize - (p - buffer)); 2135 + repo_unuse_commit_buffer(r, this, buffer); 2136 + buffer = NULL; 2137 + 2138 + if (do_store_stash(oid, msg, 1)) { 2139 + res = error(_("cannot save the stash for %s"), oid_to_hex(oid)); 2140 + goto out; 2141 + } 2142 + FREE_AND_NULL(msg); 2143 + } 2144 + out: 2145 + if (this && buffer) 2146 + repo_unuse_commit_buffer(r, this, buffer); 2147 + free_commit_list(items); 2148 + free(msg); 2149 + 2150 + return res; 2151 + } 2152 + 2153 + static int import_stash(int argc, const char **argv, const char *prefix, 2154 + struct repository *repo) 2155 + { 2156 + struct option options[] = { 2157 + OPT_END() 2158 + }; 2159 + 2160 + argc = parse_options(argc, argv, prefix, options, 2161 + git_stash_import_usage, 2162 + PARSE_OPT_KEEP_DASHDASH); 2163 + 2164 + if (argc != 1) 2165 + usage_msg_opt("a revision is required", git_stash_import_usage, options); 2166 + 2167 + return do_import_stash(repo, argv[0]); 2168 + } 2169 + 2170 + struct stash_entry_data { 2171 + struct repository *r; 2172 + struct commit_list **items; 2173 + size_t count; 2174 + }; 2175 + 2176 + static int collect_stash_entries(struct object_id *old_oid UNUSED, 2177 + struct object_id *new_oid, 2178 + const char *committer UNUSED, 2179 + timestamp_t timestamp UNUSED, 2180 + int tz UNUSED, const char *msg UNUSED, 2181 + void *cb_data) 2182 + { 2183 + struct stash_entry_data *data = cb_data; 2184 + struct commit *stash; 2185 + 2186 + data->count++; 2187 + stash = lookup_commit_reference(data->r, new_oid); 2188 + if (!stash || check_stash_topology(data->r, stash)) { 2189 + return error(_("%s does not look like a stash commit"), 2190 + oid_to_hex(new_oid)); 2191 + } 2192 + data->items = commit_list_append(stash, data->items); 2193 + return 0; 2194 + } 2195 + 2196 + static int do_export_stash(struct repository *r, 2197 + const char *ref, 2198 + int argc, 2199 + const char **argv) 2200 + { 2201 + struct object_id base; 2202 + struct object_context unused; 2203 + struct commit *prev; 2204 + struct commit_list *items = NULL, **iter = &items, *cur; 2205 + int res = 0; 2206 + int i; 2207 + struct strbuf revision = STRBUF_INIT; 2208 + const char *author, *committer; 2209 + 2210 + /* 2211 + * This is an arbitrary, fixed date, specifically the one used by git 2212 + * format-patch. The goal is merely to produce reproducible output. 2213 + */ 2214 + prepare_fallback_ident("git stash", "git@stash"); 2215 + author = fmt_ident("git stash", "git@stash", WANT_BLANK_IDENT, 2216 + "2001-09-17T00:00:00Z", 0); 2217 + committer = fmt_ident("git stash", "git@stash", WANT_BLANK_IDENT, 2218 + "2001-09-17T00:00:00Z", 0); 2219 + 2220 + /* First, we create a single empty commit. */ 2221 + if (commit_tree_extended("", 0, r->hash_algo->empty_tree, NULL, 2222 + &base, author, committer, NULL, NULL)) 2223 + return error(_("unable to write base commit")); 2224 + 2225 + prev = lookup_commit_reference(r, &base); 2226 + 2227 + if (argc) { 2228 + /* 2229 + * Find each specified stash, and load data into the array. 2230 + */ 2231 + for (i = 0; i < argc; i++) { 2232 + struct object_id oid; 2233 + struct commit *stash; 2234 + 2235 + if (parse_stash_revision(&revision, argv[i], 1) || 2236 + get_oid_with_context(r, revision.buf, 2237 + GET_OID_QUIETLY | GET_OID_GENTLY, 2238 + &oid, &unused)) { 2239 + res = error(_("unable to find stash entry %s"), argv[i]); 2240 + goto out; 2241 + } 2242 + 2243 + stash = lookup_commit_reference(r, &oid); 2244 + if (!stash || check_stash_topology(r, stash)) { 2245 + res = error(_("%s does not look like a stash commit"), 2246 + revision.buf); 2247 + goto out; 2248 + } 2249 + iter = commit_list_append(stash, iter); 2250 + } 2251 + } else { 2252 + /* 2253 + * Walk the reflog, finding each stash entry, and load data into the 2254 + * array. 2255 + */ 2256 + struct stash_entry_data cb_data = { 2257 + .r = r, .items = iter, 2258 + }; 2259 + if (refs_for_each_reflog_ent_reverse(get_main_ref_store(r), 2260 + "refs/stash", 2261 + collect_stash_entries, 2262 + &cb_data) && cb_data.count) 2263 + goto out; 2264 + } 2265 + 2266 + /* 2267 + * Now, create a set of commits identical to the regular stash commits, 2268 + * but where their first parents form a chain to our original empty 2269 + * base commit. 2270 + */ 2271 + items = reverse_commit_list(items); 2272 + for (cur = items; cur; cur = cur->next) { 2273 + struct commit_list *parents = NULL; 2274 + struct commit_list **next = &parents; 2275 + struct object_id out; 2276 + struct commit *stash = cur->item; 2277 + 2278 + next = commit_list_append(prev, next); 2279 + next = commit_list_append(stash, next); 2280 + res = write_commit_with_parents(r, &out, &stash->object.oid, parents); 2281 + free_commit_list(parents); 2282 + if (res) 2283 + goto out; 2284 + prev = lookup_commit_reference(r, &out); 2285 + } 2286 + if (ref) 2287 + refs_update_ref(get_main_ref_store(r), NULL, ref, 2288 + &prev->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); 2289 + else 2290 + puts(oid_to_hex(&prev->object.oid)); 2291 + out: 2292 + strbuf_release(&revision); 2293 + free_commit_list(items); 2294 + 2295 + return res; 2296 + } 2297 + 2298 + enum export_action { 2299 + ACTION_NONE, 2300 + ACTION_PRINT, 2301 + ACTION_TO_REF, 2302 + }; 2303 + 2304 + static int export_stash(int argc, 2305 + const char **argv, 2306 + const char *prefix, 2307 + struct repository *repo) 2308 + { 2309 + const char *ref = NULL; 2310 + enum export_action action = ACTION_NONE; 2311 + struct option options[] = { 2312 + OPT_CMDMODE(0, "print", &action, 2313 + N_("print the object ID instead of writing it to a ref"), 2314 + ACTION_PRINT), 2315 + OPT_STRING(0, "to-ref", &ref, "ref", 2316 + N_("save the data to the given ref")), 2317 + OPT_END() 2318 + }; 2319 + 2320 + argc = parse_options(argc, argv, prefix, options, 2321 + git_stash_export_usage, 2322 + PARSE_OPT_KEEP_DASHDASH); 2323 + 2324 + if (ref && action == ACTION_NONE) 2325 + action = ACTION_TO_REF; 2326 + 2327 + if (action == ACTION_NONE || (ref && action == ACTION_PRINT)) 2328 + return error(_("exactly one of --print and --to-ref is required")); 2329 + 2330 + return do_export_stash(repo, ref, argc, argv); 2331 + } 2332 + 1897 2333 int cmd_stash(int argc, 1898 2334 const char **argv, 1899 2335 const char *prefix, ··· 1914 2350 OPT_SUBCOMMAND("store", &fn, store_stash), 1915 2351 OPT_SUBCOMMAND("create", &fn, create_stash), 1916 2352 OPT_SUBCOMMAND("push", &fn, push_stash_unassumed), 2353 + OPT_SUBCOMMAND("export", &fn, export_stash), 2354 + OPT_SUBCOMMAND("import", &fn, import_stash), 1917 2355 OPT_SUBCOMMAND_F("save", &fn, save_stash, PARSE_OPT_NOCOMPLETE), 1918 2356 OPT_END() 1919 2357 };
+1
hash.h
··· 216 216 #define GET_OID_REQUIRE_PATH 010000 217 217 #define GET_OID_HASH_ANY 020000 218 218 #define GET_OID_SKIP_AMBIGUITY_CHECK 040000 219 + #define GET_OID_GENTLY 0100000 219 220 220 221 #define GET_OID_DISAMBIGUATORS \ 221 222 (GET_OID_COMMIT | GET_OID_COMMITTISH | \
+5 -1
object-name.c
··· 1081 1081 * still fill in the oid with the "old" value, 1082 1082 * which we can use. 1083 1083 */ 1084 - } else { 1084 + } else if (!(flags & GET_OID_GENTLY)) { 1085 1085 if (flags & GET_OID_QUIETLY) { 1086 1086 exit(128); 1087 1087 } 1088 1088 die(_("log for '%.*s' only has %d entries"), 1089 1089 len, str, co_cnt); 1090 + } 1091 + if (flags & GET_OID_GENTLY) { 1092 + free(real_ref); 1093 + return -1; 1090 1094 } 1091 1095 } 1092 1096 }
+101
t/t3903-stash.sh
··· 11 11 . ./test-lib.sh 12 12 . "$TEST_DIRECTORY"/lib-unique-files.sh 13 13 14 + test_expect_success 'setup' ' 15 + test_oid_cache <<-EOF 16 + export_base sha1:73c9bab443d1f88ac61aa533d2eeaaa15451239c 17 + export_base sha256:f210fa6346e3e2ce047bdb570426b17075980c1ac01fec8fc4b75bd3ab4bcfe4 18 + EOF 19 + ' 20 + 14 21 test_expect_success 'usage on cmd and subcommand invalid option' ' 15 22 test_expect_code 129 git stash --invalid-option 2>usage && 16 23 grep "or: git stash" usage && ··· 1432 1439 echo content >expect && 1433 1440 test_cmp expect file 1434 1441 ) 1442 + ' 1443 + 1444 + test_expect_success 'stash export and import round-trip stashes' ' 1445 + git reset && 1446 + >untracked && 1447 + >tracked1 && 1448 + >tracked2 && 1449 + git add tracked* && 1450 + git stash -- && 1451 + >subdir/untracked && 1452 + >subdir/tracked1 && 1453 + >subdir/tracked2 && 1454 + git add subdir/tracked* && 1455 + git stash --include-untracked -- subdir/ && 1456 + git tag t-stash0 stash@{0} && 1457 + git tag t-stash1 stash@{1} && 1458 + simple=$(git stash export --print) && 1459 + git stash clear && 1460 + git stash import "$simple" && 1461 + test_cmp_rev stash@{0} t-stash0 && 1462 + test_cmp_rev stash@{1} t-stash1 && 1463 + git stash export --to-ref refs/heads/foo && 1464 + test_cmp_rev "$(test_oid empty_tree)" foo: && 1465 + test_cmp_rev "$(test_oid empty_tree)" foo^: && 1466 + test_cmp_rev t-stash0 foo^2 && 1467 + test_cmp_rev t-stash1 foo^^2 && 1468 + git log --first-parent --format="%s" refs/heads/foo >log && 1469 + grep "^git stash: " log >log2 && 1470 + test_line_count = 13 log2 && 1471 + git stash clear && 1472 + git stash import foo && 1473 + test_cmp_rev stash@{0} t-stash0 && 1474 + test_cmp_rev stash@{1} t-stash1 1475 + ' 1476 + 1477 + test_expect_success 'stash import appends commits' ' 1478 + git log --format=oneline -g refs/stash >out && 1479 + cat out out >out2 && 1480 + git stash import refs/heads/foo && 1481 + git log --format=oneline -g refs/stash >actual && 1482 + test_line_count = $(wc -l <out2) actual 1483 + ' 1484 + 1485 + test_expect_success 'stash export can accept specified stashes' ' 1486 + git stash clear && 1487 + git stash import foo && 1488 + git stash export --to-ref refs/heads/bar stash@{1} stash@{0} && 1489 + git stash clear && 1490 + git stash import refs/heads/bar && 1491 + test_cmp_rev stash@{1} t-stash0 && 1492 + test_cmp_rev stash@{0} t-stash1 && 1493 + git log --format=oneline -g refs/stash >actual && 1494 + test_line_count = 2 actual 1495 + ' 1496 + 1497 + test_expect_success 'stash export rejects invalid arguments' ' 1498 + test_must_fail git stash export --print --to-ref refs/heads/invalid 2>err && 1499 + grep "exactly one of --print and --to-ref is required" err && 1500 + test_must_fail git stash export 2>err2 && 1501 + grep "exactly one of --print and --to-ref is required" err2 1502 + ' 1503 + 1504 + test_expect_success 'stash can import and export zero stashes' ' 1505 + git stash clear && 1506 + git stash export --to-ref refs/heads/baz && 1507 + test_cmp_rev "$(test_oid empty_tree)" baz: && 1508 + test_cmp_rev "$(test_oid export_base)" baz && 1509 + test_must_fail git rev-parse baz^1 && 1510 + git stash import baz && 1511 + test_must_fail git rev-parse refs/stash 1512 + ' 1513 + 1514 + test_expect_success 'stash rejects invalid attempts to import commits' ' 1515 + git stash import foo && 1516 + test_must_fail git stash import HEAD 2>output && 1517 + oid=$(git rev-parse HEAD) && 1518 + grep "$oid is not a valid exported stash commit" output && 1519 + test_cmp_rev stash@{0} t-stash0 && 1520 + 1521 + git checkout --orphan orphan && 1522 + git commit-tree $(test_oid empty_tree) -p "$oid" -p "$oid^" -m "" >fake-commit && 1523 + git update-ref refs/heads/orphan "$(cat fake-commit)" && 1524 + oid=$(git rev-parse HEAD) && 1525 + test_must_fail git stash import orphan 2>output && 1526 + grep "found stash commit $oid without expected prefix" output && 1527 + test_cmp_rev stash@{0} t-stash0 && 1528 + 1529 + git checkout --orphan orphan2 && 1530 + git commit-tree $(test_oid empty_tree) -m "" >fake-commit && 1531 + git update-ref refs/heads/orphan2 "$(cat fake-commit)" && 1532 + oid=$(git rev-parse HEAD) && 1533 + test_must_fail git stash import orphan2 2>output && 1534 + grep "found root commit $oid with invalid data" output && 1535 + test_cmp_rev stash@{0} t-stash0 1435 1536 ' 1436 1537 1437 1538 test_expect_success 'stash apply should succeed with unmodified file' '