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