4028b643141e1f808f92dd5740e486e05bb7c401
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#include "revision.h"
14#include "log-tree.h"
15
16static const char * const git_stash_helper_usage[] = {
17 N_("git stash--helper list [<options>]"),
18 N_("git stash--helper show [<options>] [<stash>]"),
19 N_("git stash--helper drop [-q|--quiet] [<stash>]"),
20 N_("git stash--helper ( pop | apply ) [--index] [-q|--quiet] [<stash>]"),
21 N_("git stash--helper branch <branchname> [<stash>]"),
22 N_("git stash--helper clear"),
23 NULL
24};
25
26static const char * const git_stash_helper_list_usage[] = {
27 N_("git stash--helper list [<options>]"),
28 NULL
29};
30
31static const char * const git_stash_helper_show_usage[] = {
32 N_("git stash--helper show [<options>] [<stash>]"),
33 NULL
34};
35
36static const char * const git_stash_helper_drop_usage[] = {
37 N_("git stash--helper drop [-q|--quiet] [<stash>]"),
38 NULL
39};
40
41static const char * const git_stash_helper_pop_usage[] = {
42 N_("git stash--helper pop [--index] [-q|--quiet] [<stash>]"),
43 NULL
44};
45
46static const char * const git_stash_helper_apply_usage[] = {
47 N_("git stash--helper apply [--index] [-q|--quiet] [<stash>]"),
48 NULL
49};
50
51static const char * const git_stash_helper_branch_usage[] = {
52 N_("git stash--helper branch <branchname> [<stash>]"),
53 NULL
54};
55
56static const char * const git_stash_helper_clear_usage[] = {
57 N_("git stash--helper clear"),
58 NULL
59};
60
61static const char *ref_stash = "refs/stash";
62static struct strbuf stash_index_path = STRBUF_INIT;
63
64/*
65 * w_commit is set to the commit containing the working tree
66 * b_commit is set to the base commit
67 * i_commit is set to the commit containing the index tree
68 * u_commit is set to the commit containing the untracked files tree
69 * w_tree is set to the working tree
70 * b_tree is set to the base tree
71 * i_tree is set to the index tree
72 * u_tree is set to the untracked files tree
73 */
74struct stash_info {
75 struct object_id w_commit;
76 struct object_id b_commit;
77 struct object_id i_commit;
78 struct object_id u_commit;
79 struct object_id w_tree;
80 struct object_id b_tree;
81 struct object_id i_tree;
82 struct object_id u_tree;
83 struct strbuf revision;
84 int is_stash_ref;
85 int has_u;
86};
87
88static void free_stash_info(struct stash_info *info)
89{
90 strbuf_release(&info->revision);
91}
92
93static void assert_stash_like(struct stash_info *info, const char *revision)
94{
95 if (get_oidf(&info->b_commit, "%s^1", revision) ||
96 get_oidf(&info->w_tree, "%s:", revision) ||
97 get_oidf(&info->b_tree, "%s^1:", revision) ||
98 get_oidf(&info->i_tree, "%s^2:", revision))
99 die(_("'%s' is not a stash-like commit"), revision);
100}
101
102static int get_stash_info(struct stash_info *info, int argc, const char **argv)
103{
104 int ret;
105 char *end_of_rev;
106 char *expanded_ref;
107 const char *revision;
108 const char *commit = NULL;
109 struct object_id dummy;
110 struct strbuf symbolic = STRBUF_INIT;
111
112 if (argc > 1) {
113 int i;
114 struct strbuf refs_msg = STRBUF_INIT;
115
116 for (i = 0; i < argc; i++)
117 strbuf_addf(&refs_msg, " '%s'", argv[i]);
118
119 fprintf_ln(stderr, _("Too many revisions specified:%s"),
120 refs_msg.buf);
121 strbuf_release(&refs_msg);
122
123 return -1;
124 }
125
126 if (argc == 1)
127 commit = argv[0];
128
129 strbuf_init(&info->revision, 0);
130 if (!commit) {
131 if (!ref_exists(ref_stash)) {
132 free_stash_info(info);
133 fprintf_ln(stderr, _("No stash entries found."));
134 return -1;
135 }
136
137 strbuf_addf(&info->revision, "%s@{0}", ref_stash);
138 } else if (strspn(commit, "0123456789") == strlen(commit)) {
139 strbuf_addf(&info->revision, "%s@{%s}", ref_stash, commit);
140 } else {
141 strbuf_addstr(&info->revision, commit);
142 }
143
144 revision = info->revision.buf;
145
146 if (get_oid(revision, &info->w_commit)) {
147 error(_("%s is not a valid reference"), revision);
148 free_stash_info(info);
149 return -1;
150 }
151
152 assert_stash_like(info, revision);
153
154 info->has_u = !get_oidf(&info->u_tree, "%s^3:", revision);
155
156 end_of_rev = strchrnul(revision, '@');
157 strbuf_add(&symbolic, revision, end_of_rev - revision);
158
159 ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref);
160 strbuf_release(&symbolic);
161 switch (ret) {
162 case 0: /* Not found, but valid ref */
163 info->is_stash_ref = 0;
164 break;
165 case 1:
166 info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
167 break;
168 default: /* Invalid or ambiguous */
169 free_stash_info(info);
170 }
171
172 free(expanded_ref);
173 return !(ret == 0 || ret == 1);
174}
175
176static int do_clear_stash(void)
177{
178 struct object_id obj;
179 if (get_oid(ref_stash, &obj))
180 return 0;
181
182 return delete_ref(NULL, ref_stash, &obj, 0);
183}
184
185static int clear_stash(int argc, const char **argv, const char *prefix)
186{
187 struct option options[] = {
188 OPT_END()
189 };
190
191 argc = parse_options(argc, argv, prefix, options,
192 git_stash_helper_clear_usage,
193 PARSE_OPT_STOP_AT_NON_OPTION);
194
195 if (argc)
196 return error(_("git stash clear with parameters is "
197 "unimplemented"));
198
199 return do_clear_stash();
200}
201
202static int reset_tree(struct object_id *i_tree, int update, int reset)
203{
204 int nr_trees = 1;
205 struct unpack_trees_options opts;
206 struct tree_desc t[MAX_UNPACK_TREES];
207 struct tree *tree;
208 struct lock_file lock_file = LOCK_INIT;
209
210 read_cache_preload(NULL);
211 if (refresh_cache(REFRESH_QUIET))
212 return -1;
213
214 hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
215
216 memset(&opts, 0, sizeof(opts));
217
218 tree = parse_tree_indirect(i_tree);
219 if (parse_tree(tree))
220 return -1;
221
222 init_tree_desc(t, tree->buffer, tree->size);
223
224 opts.head_idx = 1;
225 opts.src_index = &the_index;
226 opts.dst_index = &the_index;
227 opts.merge = 1;
228 opts.reset = reset;
229 opts.update = update;
230 opts.fn = oneway_merge;
231
232 if (unpack_trees(nr_trees, t, &opts))
233 return -1;
234
235 if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
236 return error(_("unable to write new index file"));
237
238 return 0;
239}
240
241static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit)
242{
243 struct child_process cp = CHILD_PROCESS_INIT;
244 const char *w_commit_hex = oid_to_hex(w_commit);
245
246 /*
247 * Diff-tree would not be very hard to replace with a native function,
248 * however it should be done together with apply_cached.
249 */
250 cp.git_cmd = 1;
251 argv_array_pushl(&cp.args, "diff-tree", "--binary", NULL);
252 argv_array_pushf(&cp.args, "%s^2^..%s^2", w_commit_hex, w_commit_hex);
253
254 return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
255}
256
257static int apply_cached(struct strbuf *out)
258{
259 struct child_process cp = CHILD_PROCESS_INIT;
260
261 /*
262 * Apply currently only reads either from stdin or a file, thus
263 * apply_all_patches would have to be updated to optionally take a
264 * buffer.
265 */
266 cp.git_cmd = 1;
267 argv_array_pushl(&cp.args, "apply", "--cached", NULL);
268 return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
269}
270
271static int reset_head(void)
272{
273 struct child_process cp = CHILD_PROCESS_INIT;
274
275 /*
276 * Reset is overall quite simple, however there is no current public
277 * API for resetting.
278 */
279 cp.git_cmd = 1;
280 argv_array_push(&cp.args, "reset");
281
282 return run_command(&cp);
283}
284
285static int get_newly_staged(struct strbuf *out, struct object_id *c_tree)
286{
287 struct child_process cp = CHILD_PROCESS_INIT;
288 const char *c_tree_hex = oid_to_hex(c_tree);
289
290 /*
291 * diff-index is very similar to diff-tree above, and should be
292 * converted together with update_index.
293 */
294 cp.git_cmd = 1;
295 argv_array_pushl(&cp.args, "diff-index", "--cached", "--name-only",
296 "--diff-filter=A", NULL);
297 argv_array_push(&cp.args, c_tree_hex);
298 return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
299}
300
301static int update_index(struct strbuf *out)
302{
303 struct child_process cp = CHILD_PROCESS_INIT;
304
305 /*
306 * Update-index is very complicated and may need to have a public
307 * function exposed in order to remove this forking.
308 */
309 cp.git_cmd = 1;
310 argv_array_pushl(&cp.args, "update-index", "--add", "--stdin", NULL);
311 return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
312}
313
314static int restore_untracked(struct object_id *u_tree)
315{
316 int res;
317 struct child_process cp = CHILD_PROCESS_INIT;
318
319 /*
320 * We need to run restore files from a given index, but without
321 * affecting the current index, so we use GIT_INDEX_FILE with
322 * run_command to fork processes that will not interfere.
323 */
324 cp.git_cmd = 1;
325 argv_array_push(&cp.args, "read-tree");
326 argv_array_push(&cp.args, oid_to_hex(u_tree));
327 argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
328 stash_index_path.buf);
329 if (run_command(&cp)) {
330 remove_path(stash_index_path.buf);
331 return -1;
332 }
333
334 child_process_init(&cp);
335 cp.git_cmd = 1;
336 argv_array_pushl(&cp.args, "checkout-index", "--all", NULL);
337 argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
338 stash_index_path.buf);
339
340 res = run_command(&cp);
341 remove_path(stash_index_path.buf);
342 return res;
343}
344
345static int do_apply_stash(const char *prefix, struct stash_info *info,
346 int index, int quiet)
347{
348 int ret;
349 int has_index = index;
350 struct merge_options o;
351 struct object_id c_tree;
352 struct object_id index_tree;
353 struct commit *result;
354 const struct object_id *bases[1];
355
356 read_cache_preload(NULL);
357 if (refresh_cache(REFRESH_QUIET))
358 return -1;
359
360 if (write_cache_as_tree(&c_tree, 0, NULL))
361 return error(_("cannot apply a stash in the middle of a merge"));
362
363 if (index) {
364 if (oideq(&info->b_tree, &info->i_tree) ||
365 oideq(&c_tree, &info->i_tree)) {
366 has_index = 0;
367 } else {
368 struct strbuf out = STRBUF_INIT;
369
370 if (diff_tree_binary(&out, &info->w_commit)) {
371 strbuf_release(&out);
372 return error(_("could not generate diff %s^!."),
373 oid_to_hex(&info->w_commit));
374 }
375
376 ret = apply_cached(&out);
377 strbuf_release(&out);
378 if (ret)
379 return error(_("conflicts in index."
380 "Try without --index."));
381
382 discard_cache();
383 read_cache();
384 if (write_cache_as_tree(&index_tree, 0, NULL))
385 return error(_("could not save index tree"));
386
387 reset_head();
388 }
389 }
390
391 if (info->has_u && restore_untracked(&info->u_tree))
392 return error(_("could not restore untracked files from stash"));
393
394 init_merge_options(&o);
395
396 o.branch1 = "Updated upstream";
397 o.branch2 = "Stashed changes";
398
399 if (oideq(&info->b_tree, &c_tree))
400 o.branch1 = "Version stash was based on";
401
402 if (quiet)
403 o.verbosity = 0;
404
405 if (o.verbosity >= 3)
406 printf_ln(_("Merging %s with %s"), o.branch1, o.branch2);
407
408 bases[0] = &info->b_tree;
409
410 ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,
411 &result);
412 if (ret) {
413 rerere(0);
414
415 if (index)
416 fprintf_ln(stderr, _("Index was not unstashed."));
417
418 return ret;
419 }
420
421 if (has_index) {
422 if (reset_tree(&index_tree, 0, 0))
423 return -1;
424 } else {
425 struct strbuf out = STRBUF_INIT;
426
427 if (get_newly_staged(&out, &c_tree)) {
428 strbuf_release(&out);
429 return -1;
430 }
431
432 if (reset_tree(&c_tree, 0, 1)) {
433 strbuf_release(&out);
434 return -1;
435 }
436
437 ret = update_index(&out);
438 strbuf_release(&out);
439 if (ret)
440 return -1;
441
442 discard_cache();
443 }
444
445 if (quiet) {
446 if (refresh_cache(REFRESH_QUIET))
447 warning("could not refresh index");
448 } else {
449 struct child_process cp = CHILD_PROCESS_INIT;
450
451 /*
452 * Status is quite simple and could be replaced with calls to
453 * wt_status in the future, but it adds complexities which may
454 * require more tests.
455 */
456 cp.git_cmd = 1;
457 cp.dir = prefix;
458 argv_array_push(&cp.args, "status");
459 run_command(&cp);
460 }
461
462 return 0;
463}
464
465static int apply_stash(int argc, const char **argv, const char *prefix)
466{
467 int ret;
468 int quiet = 0;
469 int index = 0;
470 struct stash_info info;
471 struct option options[] = {
472 OPT__QUIET(&quiet, N_("be quiet, only report errors")),
473 OPT_BOOL(0, "index", &index,
474 N_("attempt to recreate the index")),
475 OPT_END()
476 };
477
478 argc = parse_options(argc, argv, prefix, options,
479 git_stash_helper_apply_usage, 0);
480
481 if (get_stash_info(&info, argc, argv))
482 return -1;
483
484 ret = do_apply_stash(prefix, &info, index, quiet);
485 free_stash_info(&info);
486 return ret;
487}
488
489static int do_drop_stash(const char *prefix, struct stash_info *info, int quiet)
490{
491 int ret;
492 struct child_process cp_reflog = CHILD_PROCESS_INIT;
493 struct child_process cp = CHILD_PROCESS_INIT;
494
495 /*
496 * reflog does not provide a simple function for deleting refs. One will
497 * need to be added to avoid implementing too much reflog code here
498 */
499
500 cp_reflog.git_cmd = 1;
501 argv_array_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
502 "--rewrite", NULL);
503 argv_array_push(&cp_reflog.args, info->revision.buf);
504 ret = run_command(&cp_reflog);
505 if (!ret) {
506 if (!quiet)
507 printf_ln(_("Dropped %s (%s)"), info->revision.buf,
508 oid_to_hex(&info->w_commit));
509 } else {
510 return error(_("%s: Could not drop stash entry"),
511 info->revision.buf);
512 }
513
514 /*
515 * This could easily be replaced by get_oid, but currently it will throw
516 * a fatal error when a reflog is empty, which we can not recover from.
517 */
518 cp.git_cmd = 1;
519 /* Even though --quiet is specified, rev-parse still outputs the hash */
520 cp.no_stdout = 1;
521 argv_array_pushl(&cp.args, "rev-parse", "--verify", "--quiet", NULL);
522 argv_array_pushf(&cp.args, "%s@{0}", ref_stash);
523 ret = run_command(&cp);
524
525 /* do_clear_stash if we just dropped the last stash entry */
526 if (ret)
527 do_clear_stash();
528
529 return 0;
530}
531
532static void assert_stash_ref(struct stash_info *info)
533{
534 if (!info->is_stash_ref) {
535 error(_("'%s' is not a stash reference"), info->revision.buf);
536 free_stash_info(info);
537 exit(1);
538 }
539}
540
541static int drop_stash(int argc, const char **argv, const char *prefix)
542{
543 int ret;
544 int quiet = 0;
545 struct stash_info info;
546 struct option options[] = {
547 OPT__QUIET(&quiet, N_("be quiet, only report errors")),
548 OPT_END()
549 };
550
551 argc = parse_options(argc, argv, prefix, options,
552 git_stash_helper_drop_usage, 0);
553
554 if (get_stash_info(&info, argc, argv))
555 return -1;
556
557 assert_stash_ref(&info);
558
559 ret = do_drop_stash(prefix, &info, quiet);
560 free_stash_info(&info);
561 return ret;
562}
563
564static int pop_stash(int argc, const char **argv, const char *prefix)
565{
566 int ret;
567 int index = 0;
568 int quiet = 0;
569 struct stash_info info;
570 struct option options[] = {
571 OPT__QUIET(&quiet, N_("be quiet, only report errors")),
572 OPT_BOOL(0, "index", &index,
573 N_("attempt to recreate the index")),
574 OPT_END()
575 };
576
577 argc = parse_options(argc, argv, prefix, options,
578 git_stash_helper_pop_usage, 0);
579
580 if (get_stash_info(&info, argc, argv))
581 return -1;
582
583 assert_stash_ref(&info);
584 if ((ret = do_apply_stash(prefix, &info, index, quiet)))
585 printf_ln(_("The stash entry is kept in case "
586 "you need it again."));
587 else
588 ret = do_drop_stash(prefix, &info, quiet);
589
590 free_stash_info(&info);
591 return ret;
592}
593
594static int branch_stash(int argc, const char **argv, const char *prefix)
595{
596 int ret;
597 const char *branch = NULL;
598 struct stash_info info;
599 struct child_process cp = CHILD_PROCESS_INIT;
600 struct option options[] = {
601 OPT_END()
602 };
603
604 argc = parse_options(argc, argv, prefix, options,
605 git_stash_helper_branch_usage, 0);
606
607 if (!argc) {
608 fprintf_ln(stderr, _("No branch name specified"));
609 return -1;
610 }
611
612 branch = argv[0];
613
614 if (get_stash_info(&info, argc - 1, argv + 1))
615 return -1;
616
617 cp.git_cmd = 1;
618 argv_array_pushl(&cp.args, "checkout", "-b", NULL);
619 argv_array_push(&cp.args, branch);
620 argv_array_push(&cp.args, oid_to_hex(&info.b_commit));
621 ret = run_command(&cp);
622 if (!ret)
623 ret = do_apply_stash(prefix, &info, 1, 0);
624 if (!ret && info.is_stash_ref)
625 ret = do_drop_stash(prefix, &info, 0);
626
627 free_stash_info(&info);
628
629 return ret;
630}
631
632static int list_stash(int argc, const char **argv, const char *prefix)
633{
634 struct child_process cp = CHILD_PROCESS_INIT;
635 struct option options[] = {
636 OPT_END()
637 };
638
639 argc = parse_options(argc, argv, prefix, options,
640 git_stash_helper_list_usage,
641 PARSE_OPT_KEEP_UNKNOWN);
642
643 if (!ref_exists(ref_stash))
644 return 0;
645
646 cp.git_cmd = 1;
647 argv_array_pushl(&cp.args, "log", "--format=%gd: %gs", "-g",
648 "--first-parent", "-m", NULL);
649 argv_array_pushv(&cp.args, argv);
650 argv_array_push(&cp.args, ref_stash);
651 argv_array_push(&cp.args, "--");
652 return run_command(&cp);
653}
654
655static int show_stat = 1;
656static int show_patch;
657
658static int git_stash_config(const char *var, const char *value, void *cb)
659{
660 if (!strcmp(var, "stash.showstat")) {
661 show_stat = git_config_bool(var, value);
662 return 0;
663 }
664 if (!strcmp(var, "stash.showpatch")) {
665 show_patch = git_config_bool(var, value);
666 return 0;
667 }
668 return git_default_config(var, value, cb);
669}
670
671static int show_stash(int argc, const char **argv, const char *prefix)
672{
673 int i;
674 int opts = 0;
675 int ret = 0;
676 struct stash_info info;
677 struct rev_info rev;
678 struct argv_array stash_args = ARGV_ARRAY_INIT;
679 struct option options[] = {
680 OPT_END()
681 };
682
683 init_diff_ui_defaults();
684 git_config(git_diff_ui_config, NULL);
685 init_revisions(&rev, prefix);
686
687 for (i = 1; i < argc; i++) {
688 if (argv[i][0] != '-')
689 argv_array_push(&stash_args, argv[i]);
690 else
691 opts++;
692 }
693
694 ret = get_stash_info(&info, stash_args.argc, stash_args.argv);
695 argv_array_clear(&stash_args);
696 if (ret)
697 return -1;
698
699 /*
700 * The config settings are applied only if there are not passed
701 * any options.
702 */
703 if (!opts) {
704 git_config(git_stash_config, NULL);
705 if (show_stat)
706 rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;
707
708 if (show_patch)
709 rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
710
711 if (!show_stat && !show_patch) {
712 free_stash_info(&info);
713 return 0;
714 }
715 }
716
717 argc = setup_revisions(argc, argv, &rev, NULL);
718 if (argc > 1) {
719 free_stash_info(&info);
720 usage_with_options(git_stash_helper_show_usage, options);
721 }
722
723 rev.diffopt.flags.recursive = 1;
724 setup_diff_pager(&rev.diffopt);
725 diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt);
726 log_tree_diff_flush(&rev);
727
728 free_stash_info(&info);
729 return diff_result_code(&rev.diffopt, 0);
730}
731
732int cmd_stash__helper(int argc, const char **argv, const char *prefix)
733{
734 pid_t pid = getpid();
735 const char *index_file;
736
737 struct option options[] = {
738 OPT_END()
739 };
740
741 git_config(git_default_config, NULL);
742
743 argc = parse_options(argc, argv, prefix, options, git_stash_helper_usage,
744 PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
745
746 index_file = get_index_file();
747 strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file,
748 (uintmax_t)pid);
749
750 if (argc < 1)
751 usage_with_options(git_stash_helper_usage, options);
752 if (!strcmp(argv[0], "apply"))
753 return !!apply_stash(argc, argv, prefix);
754 else if (!strcmp(argv[0], "clear"))
755 return !!clear_stash(argc, argv, prefix);
756 else if (!strcmp(argv[0], "drop"))
757 return !!drop_stash(argc, argv, prefix);
758 else if (!strcmp(argv[0], "pop"))
759 return !!pop_stash(argc, argv, prefix);
760 else if (!strcmp(argv[0], "branch"))
761 return !!branch_stash(argc, argv, prefix);
762 else if (!strcmp(argv[0], "list"))
763 return !!list_stash(argc, argv, prefix);
764 else if (!strcmp(argv[0], "show"))
765 return !!show_stash(argc, argv, prefix);
766
767 usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]),
768 git_stash_helper_usage, options);
769}