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