]> git.ipfire.org Git - thirdparty/git.git/blame - builtin/stash--helper.c
stash: convert drop and clear to builtin
[thirdparty/git.git] / builtin / stash--helper.c
CommitLineData
8a0fc8d1
JT
1#include "builtin.h"
2#include "config.h"
3#include "parse-options.h"
4#include "refs.h"
5#include "lockfile.h"
6#include "cache-tree.h"
7#include "unpack-trees.h"
8#include "merge-recursive.h"
9#include "argv-array.h"
10#include "run-command.h"
11#include "dir.h"
12#include "rerere.h"
13
14static const char * const git_stash_helper_usage[] = {
4e2dd393 15 N_("git stash--helper drop [-q|--quiet] [<stash>]"),
8a0fc8d1 16 N_("git stash--helper apply [--index] [-q|--quiet] [<stash>]"),
4e2dd393
JT
17 N_("git stash--helper clear"),
18 NULL
19};
20
21static const char * const git_stash_helper_drop_usage[] = {
22 N_("git stash--helper drop [-q|--quiet] [<stash>]"),
8a0fc8d1
JT
23 NULL
24};
25
26static const char * const git_stash_helper_apply_usage[] = {
27 N_("git stash--helper apply [--index] [-q|--quiet] [<stash>]"),
28 NULL
29};
30
4e2dd393
JT
31static const char * const git_stash_helper_clear_usage[] = {
32 N_("git stash--helper clear"),
33 NULL
34};
35
8a0fc8d1
JT
36static const char *ref_stash = "refs/stash";
37static struct strbuf stash_index_path = STRBUF_INIT;
38
39/*
40 * w_commit is set to the commit containing the working tree
41 * b_commit is set to the base commit
42 * i_commit is set to the commit containing the index tree
43 * u_commit is set to the commit containing the untracked files tree
44 * w_tree is set to the working tree
45 * b_tree is set to the base tree
46 * i_tree is set to the index tree
47 * u_tree is set to the untracked files tree
48 */
49struct stash_info {
50 struct object_id w_commit;
51 struct object_id b_commit;
52 struct object_id i_commit;
53 struct object_id u_commit;
54 struct object_id w_tree;
55 struct object_id b_tree;
56 struct object_id i_tree;
57 struct object_id u_tree;
58 struct strbuf revision;
59 int is_stash_ref;
60 int has_u;
61};
62
63static void free_stash_info(struct stash_info *info)
64{
65 strbuf_release(&info->revision);
66}
67
68static void assert_stash_like(struct stash_info *info, const char *revision)
69{
70 if (get_oidf(&info->b_commit, "%s^1", revision) ||
71 get_oidf(&info->w_tree, "%s:", revision) ||
72 get_oidf(&info->b_tree, "%s^1:", revision) ||
73 get_oidf(&info->i_tree, "%s^2:", revision))
74 die(_("'%s' is not a stash-like commit"), revision);
75}
76
77static int get_stash_info(struct stash_info *info, int argc, const char **argv)
78{
79 int ret;
80 char *end_of_rev;
81 char *expanded_ref;
82 const char *revision;
83 const char *commit = NULL;
84 struct object_id dummy;
85 struct strbuf symbolic = STRBUF_INIT;
86
87 if (argc > 1) {
88 int i;
89 struct strbuf refs_msg = STRBUF_INIT;
90
91 for (i = 0; i < argc; i++)
92 strbuf_addf(&refs_msg, " '%s'", argv[i]);
93
94 fprintf_ln(stderr, _("Too many revisions specified:%s"),
95 refs_msg.buf);
96 strbuf_release(&refs_msg);
97
98 return -1;
99 }
100
101 if (argc == 1)
102 commit = argv[0];
103
104 strbuf_init(&info->revision, 0);
105 if (!commit) {
106 if (!ref_exists(ref_stash)) {
107 free_stash_info(info);
108 fprintf_ln(stderr, _("No stash entries found."));
109 return -1;
110 }
111
112 strbuf_addf(&info->revision, "%s@{0}", ref_stash);
113 } else if (strspn(commit, "0123456789") == strlen(commit)) {
114 strbuf_addf(&info->revision, "%s@{%s}", ref_stash, commit);
115 } else {
116 strbuf_addstr(&info->revision, commit);
117 }
118
119 revision = info->revision.buf;
120
121 if (get_oid(revision, &info->w_commit)) {
122 error(_("%s is not a valid reference"), revision);
123 free_stash_info(info);
124 return -1;
125 }
126
127 assert_stash_like(info, revision);
128
129 info->has_u = !get_oidf(&info->u_tree, "%s^3:", revision);
130
131 end_of_rev = strchrnul(revision, '@');
132 strbuf_add(&symbolic, revision, end_of_rev - revision);
133
134 ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref);
135 strbuf_release(&symbolic);
136 switch (ret) {
137 case 0: /* Not found, but valid ref */
138 info->is_stash_ref = 0;
139 break;
140 case 1:
141 info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
142 break;
143 default: /* Invalid or ambiguous */
144 free_stash_info(info);
145 }
146
147 free(expanded_ref);
148 return !(ret == 0 || ret == 1);
149}
150
4e2dd393
JT
151static int do_clear_stash(void)
152{
153 struct object_id obj;
154 if (get_oid(ref_stash, &obj))
155 return 0;
156
157 return delete_ref(NULL, ref_stash, &obj, 0);
158}
159
160static int clear_stash(int argc, const char **argv, const char *prefix)
161{
162 struct option options[] = {
163 OPT_END()
164 };
165
166 argc = parse_options(argc, argv, prefix, options,
167 git_stash_helper_clear_usage,
168 PARSE_OPT_STOP_AT_NON_OPTION);
169
170 if (argc)
171 return error(_("git stash clear with parameters is "
172 "unimplemented"));
173
174 return do_clear_stash();
175}
176
8a0fc8d1
JT
177static int reset_tree(struct object_id *i_tree, int update, int reset)
178{
179 int nr_trees = 1;
180 struct unpack_trees_options opts;
181 struct tree_desc t[MAX_UNPACK_TREES];
182 struct tree *tree;
183 struct lock_file lock_file = LOCK_INIT;
184
185 read_cache_preload(NULL);
186 if (refresh_cache(REFRESH_QUIET))
187 return -1;
188
189 hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
190
191 memset(&opts, 0, sizeof(opts));
192
193 tree = parse_tree_indirect(i_tree);
194 if (parse_tree(tree))
195 return -1;
196
197 init_tree_desc(t, tree->buffer, tree->size);
198
199 opts.head_idx = 1;
200 opts.src_index = &the_index;
201 opts.dst_index = &the_index;
202 opts.merge = 1;
203 opts.reset = reset;
204 opts.update = update;
205 opts.fn = oneway_merge;
206
207 if (unpack_trees(nr_trees, t, &opts))
208 return -1;
209
210 if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
211 return error(_("unable to write new index file"));
212
213 return 0;
214}
215
216static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit)
217{
218 struct child_process cp = CHILD_PROCESS_INIT;
219 const char *w_commit_hex = oid_to_hex(w_commit);
220
221 /*
222 * Diff-tree would not be very hard to replace with a native function,
223 * however it should be done together with apply_cached.
224 */
225 cp.git_cmd = 1;
226 argv_array_pushl(&cp.args, "diff-tree", "--binary", NULL);
227 argv_array_pushf(&cp.args, "%s^2^..%s^2", w_commit_hex, w_commit_hex);
228
229 return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
230}
231
232static int apply_cached(struct strbuf *out)
233{
234 struct child_process cp = CHILD_PROCESS_INIT;
235
236 /*
237 * Apply currently only reads either from stdin or a file, thus
238 * apply_all_patches would have to be updated to optionally take a
239 * buffer.
240 */
241 cp.git_cmd = 1;
242 argv_array_pushl(&cp.args, "apply", "--cached", NULL);
243 return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
244}
245
246static int reset_head(void)
247{
248 struct child_process cp = CHILD_PROCESS_INIT;
249
250 /*
251 * Reset is overall quite simple, however there is no current public
252 * API for resetting.
253 */
254 cp.git_cmd = 1;
255 argv_array_push(&cp.args, "reset");
256
257 return run_command(&cp);
258}
259
260static int get_newly_staged(struct strbuf *out, struct object_id *c_tree)
261{
262 struct child_process cp = CHILD_PROCESS_INIT;
263 const char *c_tree_hex = oid_to_hex(c_tree);
264
265 /*
266 * diff-index is very similar to diff-tree above, and should be
267 * converted together with update_index.
268 */
269 cp.git_cmd = 1;
270 argv_array_pushl(&cp.args, "diff-index", "--cached", "--name-only",
271 "--diff-filter=A", NULL);
272 argv_array_push(&cp.args, c_tree_hex);
273 return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
274}
275
276static int update_index(struct strbuf *out)
277{
278 struct child_process cp = CHILD_PROCESS_INIT;
279
280 /*
281 * Update-index is very complicated and may need to have a public
282 * function exposed in order to remove this forking.
283 */
284 cp.git_cmd = 1;
285 argv_array_pushl(&cp.args, "update-index", "--add", "--stdin", NULL);
286 return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
287}
288
289static int restore_untracked(struct object_id *u_tree)
290{
291 int res;
292 struct child_process cp = CHILD_PROCESS_INIT;
293
294 /*
295 * We need to run restore files from a given index, but without
296 * affecting the current index, so we use GIT_INDEX_FILE with
297 * run_command to fork processes that will not interfere.
298 */
299 cp.git_cmd = 1;
300 argv_array_push(&cp.args, "read-tree");
301 argv_array_push(&cp.args, oid_to_hex(u_tree));
302 argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
303 stash_index_path.buf);
304 if (run_command(&cp)) {
305 remove_path(stash_index_path.buf);
306 return -1;
307 }
308
309 child_process_init(&cp);
310 cp.git_cmd = 1;
311 argv_array_pushl(&cp.args, "checkout-index", "--all", NULL);
312 argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
313 stash_index_path.buf);
314
315 res = run_command(&cp);
316 remove_path(stash_index_path.buf);
317 return res;
318}
319
320static int do_apply_stash(const char *prefix, struct stash_info *info,
321 int index, int quiet)
322{
323 int ret;
324 int has_index = index;
325 struct merge_options o;
326 struct object_id c_tree;
327 struct object_id index_tree;
328 struct commit *result;
329 const struct object_id *bases[1];
330
331 read_cache_preload(NULL);
332 if (refresh_cache(REFRESH_QUIET))
333 return -1;
334
335 if (write_cache_as_tree(&c_tree, 0, NULL))
336 return error(_("cannot apply a stash in the middle of a merge"));
337
338 if (index) {
339 if (oideq(&info->b_tree, &info->i_tree) ||
340 oideq(&c_tree, &info->i_tree)) {
341 has_index = 0;
342 } else {
343 struct strbuf out = STRBUF_INIT;
344
345 if (diff_tree_binary(&out, &info->w_commit)) {
346 strbuf_release(&out);
347 return error(_("could not generate diff %s^!."),
348 oid_to_hex(&info->w_commit));
349 }
350
351 ret = apply_cached(&out);
352 strbuf_release(&out);
353 if (ret)
354 return error(_("conflicts in index."
355 "Try without --index."));
356
357 discard_cache();
358 read_cache();
359 if (write_cache_as_tree(&index_tree, 0, NULL))
360 return error(_("could not save index tree"));
361
362 reset_head();
363 }
364 }
365
366 if (info->has_u && restore_untracked(&info->u_tree))
367 return error(_("could not restore untracked files from stash"));
368
369 init_merge_options(&o);
370
371 o.branch1 = "Updated upstream";
372 o.branch2 = "Stashed changes";
373
374 if (oideq(&info->b_tree, &c_tree))
375 o.branch1 = "Version stash was based on";
376
377 if (quiet)
378 o.verbosity = 0;
379
380 if (o.verbosity >= 3)
381 printf_ln(_("Merging %s with %s"), o.branch1, o.branch2);
382
383 bases[0] = &info->b_tree;
384
385 ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,
386 &result);
387 if (ret) {
388 rerere(0);
389
390 if (index)
391 fprintf_ln(stderr, _("Index was not unstashed."));
392
393 return ret;
394 }
395
396 if (has_index) {
397 if (reset_tree(&index_tree, 0, 0))
398 return -1;
399 } else {
400 struct strbuf out = STRBUF_INIT;
401
402 if (get_newly_staged(&out, &c_tree)) {
403 strbuf_release(&out);
404 return -1;
405 }
406
407 if (reset_tree(&c_tree, 0, 1)) {
408 strbuf_release(&out);
409 return -1;
410 }
411
412 ret = update_index(&out);
413 strbuf_release(&out);
414 if (ret)
415 return -1;
416
417 discard_cache();
418 }
419
420 if (quiet) {
421 if (refresh_cache(REFRESH_QUIET))
422 warning("could not refresh index");
423 } else {
424 struct child_process cp = CHILD_PROCESS_INIT;
425
426 /*
427 * Status is quite simple and could be replaced with calls to
428 * wt_status in the future, but it adds complexities which may
429 * require more tests.
430 */
431 cp.git_cmd = 1;
432 cp.dir = prefix;
433 argv_array_push(&cp.args, "status");
434 run_command(&cp);
435 }
436
437 return 0;
438}
439
440static int apply_stash(int argc, const char **argv, const char *prefix)
441{
442 int ret;
443 int quiet = 0;
444 int index = 0;
445 struct stash_info info;
446 struct option options[] = {
447 OPT__QUIET(&quiet, N_("be quiet, only report errors")),
448 OPT_BOOL(0, "index", &index,
449 N_("attempt to recreate the index")),
450 OPT_END()
451 };
452
453 argc = parse_options(argc, argv, prefix, options,
454 git_stash_helper_apply_usage, 0);
455
456 if (get_stash_info(&info, argc, argv))
457 return -1;
458
459 ret = do_apply_stash(prefix, &info, index, quiet);
460 free_stash_info(&info);
461 return ret;
462}
463
4e2dd393
JT
464static int do_drop_stash(const char *prefix, struct stash_info *info, int quiet)
465{
466 int ret;
467 struct child_process cp_reflog = CHILD_PROCESS_INIT;
468 struct child_process cp = CHILD_PROCESS_INIT;
469
470 /*
471 * reflog does not provide a simple function for deleting refs. One will
472 * need to be added to avoid implementing too much reflog code here
473 */
474
475 cp_reflog.git_cmd = 1;
476 argv_array_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
477 "--rewrite", NULL);
478 argv_array_push(&cp_reflog.args, info->revision.buf);
479 ret = run_command(&cp_reflog);
480 if (!ret) {
481 if (!quiet)
482 printf_ln(_("Dropped %s (%s)"), info->revision.buf,
483 oid_to_hex(&info->w_commit));
484 } else {
485 return error(_("%s: Could not drop stash entry"),
486 info->revision.buf);
487 }
488
489 /*
490 * This could easily be replaced by get_oid, but currently it will throw
491 * a fatal error when a reflog is empty, which we can not recover from.
492 */
493 cp.git_cmd = 1;
494 /* Even though --quiet is specified, rev-parse still outputs the hash */
495 cp.no_stdout = 1;
496 argv_array_pushl(&cp.args, "rev-parse", "--verify", "--quiet", NULL);
497 argv_array_pushf(&cp.args, "%s@{0}", ref_stash);
498 ret = run_command(&cp);
499
500 /* do_clear_stash if we just dropped the last stash entry */
501 if (ret)
502 do_clear_stash();
503
504 return 0;
505}
506
507static void assert_stash_ref(struct stash_info *info)
508{
509 if (!info->is_stash_ref) {
510 error(_("'%s' is not a stash reference"), info->revision.buf);
511 free_stash_info(info);
512 exit(1);
513 }
514}
515
516static int drop_stash(int argc, const char **argv, const char *prefix)
517{
518 int ret;
519 int quiet = 0;
520 struct stash_info info;
521 struct option options[] = {
522 OPT__QUIET(&quiet, N_("be quiet, only report errors")),
523 OPT_END()
524 };
525
526 argc = parse_options(argc, argv, prefix, options,
527 git_stash_helper_drop_usage, 0);
528
529 if (get_stash_info(&info, argc, argv))
530 return -1;
531
532 assert_stash_ref(&info);
533
534 ret = do_drop_stash(prefix, &info, quiet);
535 free_stash_info(&info);
536 return ret;
537}
538
8a0fc8d1
JT
539int cmd_stash__helper(int argc, const char **argv, const char *prefix)
540{
541 pid_t pid = getpid();
542 const char *index_file;
543
544 struct option options[] = {
545 OPT_END()
546 };
547
548 git_config(git_default_config, NULL);
549
550 argc = parse_options(argc, argv, prefix, options, git_stash_helper_usage,
551 PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
552
553 index_file = get_index_file();
554 strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file,
555 (uintmax_t)pid);
556
557 if (argc < 1)
558 usage_with_options(git_stash_helper_usage, options);
559 if (!strcmp(argv[0], "apply"))
560 return !!apply_stash(argc, argv, prefix);
4e2dd393
JT
561 else if (!strcmp(argv[0], "clear"))
562 return !!clear_stash(argc, argv, prefix);
563 else if (!strcmp(argv[0], "drop"))
564 return !!drop_stash(argc, argv, prefix);
8a0fc8d1
JT
565
566 usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]),
567 git_stash_helper_usage, options);
568}