]> git.proxmox.com Git - libgit2.git/blame - src/stash.c
No such thing as an orphan branch
[libgit2.git] / src / stash.c
CommitLineData
590fb68b 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
590fb68b 3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8#include "common.h"
9#include "repository.h"
10#include "commit.h"
11#include "tree.h"
12#include "reflog.h"
13#include "git2/diff.h"
14#include "git2/stash.h"
15#include "git2/status.h"
16#include "git2/checkout.h"
114f5a6c 17#include "git2/index.h"
4ec197f3 18#include "signature.h"
590fb68b 19
20static int create_error(int error, const char *msg)
21{
22 giterr_set(GITERR_STASH, "Cannot stash changes - %s", msg);
23 return error;
24}
25
590fb68b 26static int retrieve_head(git_reference **out, git_repository *repo)
27{
28 int error = git_repository_head(out, repo);
29
605da51a 30 if (error == GIT_EUNBORNBRANCH)
590fb68b 31 return create_error(error, "You do not have the initial commit yet.");
32
33 return error;
34}
35
36static int append_abbreviated_oid(git_buf *out, const git_oid *b_commit)
37{
38 char *formatted_oid;
39
40 formatted_oid = git_oid_allocfmt(b_commit);
41 GITERR_CHECK_ALLOC(formatted_oid);
42
43 git_buf_put(out, formatted_oid, 7);
44 git__free(formatted_oid);
45
46 return git_buf_oom(out) ? -1 : 0;
47}
48
49static int append_commit_description(git_buf *out, git_commit* commit)
50{
51 const char *message;
a8122b5d 52 size_t pos = 0, len;
590fb68b 53
54 if (append_abbreviated_oid(out, git_commit_id(commit)) < 0)
55 return -1;
56
57 message = git_commit_message(commit);
58 len = strlen(message);
59
60 /* TODO: Replace with proper commit short message
61 * when git_commit_message_short() is implemented.
62 */
63 while (pos < len && message[pos] != '\n')
64 pos++;
65
66 git_buf_putc(out, ' ');
67 git_buf_put(out, message, pos);
68 git_buf_putc(out, '\n');
69
70 return git_buf_oom(out) ? -1 : 0;
71}
72
73static int retrieve_base_commit_and_message(
74 git_commit **b_commit,
75 git_buf *stash_message,
76 git_repository *repo)
77{
78 git_reference *head = NULL;
79 int error;
80
81 if ((error = retrieve_head(&head, repo)) < 0)
82 return error;
83
590fb68b 84 if (strcmp("HEAD", git_reference_name(head)) == 0)
a6a82e1a 85 error = git_buf_puts(stash_message, "(no branch): ");
590fb68b 86 else
a6a82e1a 87 error = git_buf_printf(
590fb68b 88 stash_message,
89 "%s: ",
90 git_reference_name(head) + strlen(GIT_REFS_HEADS_DIR));
a6a82e1a 91 if (error < 0)
590fb68b 92 goto cleanup;
93
a6a82e1a
RB
94 if ((error = git_commit_lookup(
95 b_commit, repo, git_reference_target(head))) < 0)
590fb68b 96 goto cleanup;
97
a6a82e1a
RB
98 if ((error = append_commit_description(stash_message, *b_commit)) < 0)
99 goto cleanup;
590fb68b 100
101cleanup:
102 git_reference_free(head);
103 return error;
104}
105
106static int build_tree_from_index(git_tree **out, git_index *index)
107{
a6a82e1a 108 int error;
590fb68b 109 git_oid i_tree_oid;
110
a6a82e1a 111 if ((error = git_index_write_tree(&i_tree_oid, index)) < 0)
590fb68b 112 return -1;
113
114 return git_tree_lookup(out, git_index_owner(index), &i_tree_oid);
115}
116
117static int commit_index(
118 git_commit **i_commit,
119 git_index *index,
2274993b 120 const git_signature *stasher,
590fb68b 121 const char *message,
122 const git_commit *parent)
123{
124 git_tree *i_tree = NULL;
125 git_oid i_commit_oid;
126 git_buf msg = GIT_BUF_INIT;
a6a82e1a 127 int error;
590fb68b 128
a6a82e1a 129 if ((error = build_tree_from_index(&i_tree, index)) < 0)
590fb68b 130 goto cleanup;
131
a6a82e1a 132 if ((error = git_buf_printf(&msg, "index on %s\n", message)) < 0)
590fb68b 133 goto cleanup;
134
a6a82e1a 135 if ((error = git_commit_create(
590fb68b 136 &i_commit_oid,
137 git_index_owner(index),
138 NULL,
139 stasher,
140 stasher,
141 NULL,
142 git_buf_cstr(&msg),
143 i_tree,
144 1,
a6a82e1a
RB
145 &parent)) < 0)
146 goto cleanup;
590fb68b 147
148 error = git_commit_lookup(i_commit, git_index_owner(index), &i_commit_oid);
149
150cleanup:
151 git_tree_free(i_tree);
152 git_buf_free(&msg);
153 return error;
154}
155
156struct cb_data {
157 git_index *index;
158
a6a82e1a
RB
159 int error;
160
590fb68b 161 bool include_changed;
162 bool include_untracked;
163 bool include_ignored;
164};
165
166static int update_index_cb(
590fb68b 167 const git_diff_delta *delta,
793c4385
RB
168 float progress,
169 void *payload)
590fb68b 170{
793c4385 171 struct cb_data *data = (struct cb_data *)payload;
a6a82e1a 172 const char *add_path = NULL;
590fb68b 173
174 GIT_UNUSED(progress);
175
176 switch (delta->status) {
177 case GIT_DELTA_IGNORED:
a6a82e1a
RB
178 if (data->include_ignored)
179 add_path = delta->new_file.path;
180 break;
590fb68b 181
182 case GIT_DELTA_UNTRACKED:
a6a82e1a
RB
183 if (data->include_untracked)
184 add_path = delta->new_file.path;
185 break;
590fb68b 186
187 case GIT_DELTA_ADDED:
590fb68b 188 case GIT_DELTA_MODIFIED:
a6a82e1a
RB
189 if (data->include_changed)
190 add_path = delta->new_file.path;
191 break;
590fb68b 192
193 case GIT_DELTA_DELETED:
194 if (!data->include_changed)
195 break;
11d9f6b3 196 if (git_index_find(NULL, data->index, delta->old_file.path) == 0)
a6a82e1a
RB
197 data->error = git_index_remove(
198 data->index, delta->old_file.path, 0);
199 break;
590fb68b 200
201 default:
202 /* Unimplemented */
203 giterr_set(
204 GITERR_INVALID,
a6a82e1a 205 "Cannot update index. Unimplemented status (%d)",
590fb68b 206 delta->status);
a6a82e1a
RB
207 data->error = -1;
208 break;
590fb68b 209 }
210
a6a82e1a 211 if (add_path != NULL)
25743bd7 212 data->error = git_index_add_bypath(data->index, add_path);
a6a82e1a
RB
213
214 return data->error;
590fb68b 215}
216
217static int build_untracked_tree(
218 git_tree **tree_out,
219 git_index *index,
220 git_commit *i_commit,
221 uint32_t flags)
222{
223 git_tree *i_tree = NULL;
224 git_diff_list *diff = NULL;
2f8d30be 225 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
590fb68b 226 struct cb_data data = {0};
a6a82e1a 227 int error;
590fb68b 228
229 git_index_clear(index);
230
231 data.index = index;
232
233 if (flags & GIT_STASH_INCLUDE_UNTRACKED) {
a6a82e1a
RB
234 opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED |
235 GIT_DIFF_RECURSE_UNTRACKED_DIRS;
590fb68b 236 data.include_untracked = true;
237 }
238
239 if (flags & GIT_STASH_INCLUDE_IGNORED) {
240 opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
241 data.include_ignored = true;
242 }
243
a6a82e1a 244 if ((error = git_commit_tree(&i_tree, i_commit)) < 0)
590fb68b 245 goto cleanup;
246
a6a82e1a
RB
247 if ((error = git_diff_tree_to_workdir(
248 &diff, git_index_owner(index), i_tree, &opts)) < 0)
590fb68b 249 goto cleanup;
250
a6a82e1a
RB
251 if ((error = git_diff_foreach(
252 diff, update_index_cb, NULL, NULL, &data)) < 0)
253 {
254 if (error == GIT_EUSER)
255 error = data.error;
590fb68b 256 goto cleanup;
a6a82e1a 257 }
590fb68b 258
a6a82e1a 259 error = build_tree_from_index(tree_out, index);
590fb68b 260
261cleanup:
262 git_diff_list_free(diff);
263 git_tree_free(i_tree);
264 return error;
265}
266
267static int commit_untracked(
268 git_commit **u_commit,
269 git_index *index,
2274993b 270 const git_signature *stasher,
590fb68b 271 const char *message,
272 git_commit *i_commit,
273 uint32_t flags)
274{
275 git_tree *u_tree = NULL;
276 git_oid u_commit_oid;
277 git_buf msg = GIT_BUF_INIT;
a6a82e1a 278 int error;
590fb68b 279
a6a82e1a 280 if ((error = build_untracked_tree(&u_tree, index, i_commit, flags)) < 0)
590fb68b 281 goto cleanup;
282
a6a82e1a 283 if ((error = git_buf_printf(&msg, "untracked files on %s\n", message)) < 0)
590fb68b 284 goto cleanup;
285
a6a82e1a 286 if ((error = git_commit_create(
590fb68b 287 &u_commit_oid,
288 git_index_owner(index),
289 NULL,
290 stasher,
291 stasher,
292 NULL,
293 git_buf_cstr(&msg),
294 u_tree,
295 0,
a6a82e1a
RB
296 NULL)) < 0)
297 goto cleanup;
590fb68b 298
299 error = git_commit_lookup(u_commit, git_index_owner(index), &u_commit_oid);
300
301cleanup:
302 git_tree_free(u_tree);
303 git_buf_free(&msg);
304 return error;
305}
306
307static int build_workdir_tree(
308 git_tree **tree_out,
309 git_index *index,
310 git_commit *b_commit)
311{
5735bf5e 312 git_repository *repo = git_index_owner(index);
590fb68b 313 git_tree *b_tree = NULL;
314 git_diff_list *diff = NULL, *diff2 = NULL;
2f8d30be 315 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
590fb68b 316 struct cb_data data = {0};
a6a82e1a 317 int error;
590fb68b 318
a6a82e1a 319 if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
590fb68b 320 goto cleanup;
321
a6a82e1a 322 if ((error = git_diff_tree_to_index(&diff, repo, b_tree, NULL, &opts)) < 0)
590fb68b 323 goto cleanup;
324
a6a82e1a 325 if ((error = git_diff_index_to_workdir(&diff2, repo, NULL, &opts)) < 0)
590fb68b 326 goto cleanup;
327
a6a82e1a 328 if ((error = git_diff_merge(diff, diff2)) < 0)
590fb68b 329 goto cleanup;
330
331 data.index = index;
332 data.include_changed = true;
333
a6a82e1a
RB
334 if ((error = git_diff_foreach(
335 diff, update_index_cb, NULL, NULL, &data)) < 0)
336 {
337 if (error == GIT_EUSER)
338 error = data.error;
590fb68b 339 goto cleanup;
a6a82e1a 340 }
590fb68b 341
590fb68b 342
a6a82e1a
RB
343 if ((error = build_tree_from_index(tree_out, index)) < 0)
344 goto cleanup;
590fb68b 345
346cleanup:
347 git_diff_list_free(diff);
348 git_diff_list_free(diff2);
349 git_tree_free(b_tree);
a6a82e1a 350
590fb68b 351 return error;
352}
353
354static int commit_worktree(
355 git_oid *w_commit_oid,
356 git_index *index,
2274993b 357 const git_signature *stasher,
590fb68b 358 const char *message,
359 git_commit *i_commit,
360 git_commit *b_commit,
361 git_commit *u_commit)
362{
a6a82e1a 363 int error = 0;
590fb68b 364 git_tree *w_tree = NULL, *i_tree = NULL;
590fb68b 365 const git_commit *parents[] = { NULL, NULL, NULL };
366
367 parents[0] = b_commit;
368 parents[1] = i_commit;
369 parents[2] = u_commit;
370
a6a82e1a
RB
371 if ((error = git_commit_tree(&i_tree, i_commit)) < 0)
372 goto cleanup;
590fb68b 373
a6a82e1a 374 if ((error = git_index_read_tree(index, i_tree)) < 0)
590fb68b 375 goto cleanup;
376
a6a82e1a 377 if ((error = build_workdir_tree(&w_tree, index, b_commit)) < 0)
590fb68b 378 goto cleanup;
379
a6a82e1a 380 error = git_commit_create(
590fb68b 381 w_commit_oid,
382 git_index_owner(index),
383 NULL,
384 stasher,
385 stasher,
386 NULL,
387 message,
388 w_tree,
a6a82e1a
RB
389 u_commit ? 3 : 2,
390 parents);
590fb68b 391
392cleanup:
393 git_tree_free(i_tree);
394 git_tree_free(w_tree);
395 return error;
396}
397
398static int prepare_worktree_commit_message(
399 git_buf* msg,
400 const char *user_message)
401{
402 git_buf buf = GIT_BUF_INIT;
a6a82e1a
RB
403 int error;
404
6f58332f
RB
405 if ((error = git_buf_set(&buf, git_buf_cstr(msg), git_buf_len(msg))) < 0)
406 return error;
590fb68b 407
590fb68b 408 git_buf_clear(msg);
409
410 if (!user_message)
411 git_buf_printf(msg, "WIP on %s", git_buf_cstr(&buf));
412 else {
413 const char *colon;
414
415 if ((colon = strchr(git_buf_cstr(&buf), ':')) == NULL)
416 goto cleanup;
417
418 git_buf_puts(msg, "On ");
419 git_buf_put(msg, git_buf_cstr(&buf), colon - buf.ptr);
420 git_buf_printf(msg, ": %s\n", user_message);
421 }
422
6f58332f 423 error = (git_buf_oom(msg) || git_buf_oom(&buf)) ? -1 : 0;
590fb68b 424
425cleanup:
426 git_buf_free(&buf);
a6a82e1a 427
590fb68b 428 return error;
429}
430
431static int update_reflog(
432 git_oid *w_commit_oid,
433 git_repository *repo,
2274993b 434 const git_signature *stasher,
590fb68b 435 const char *message)
436{
437 git_reference *stash = NULL;
438 git_reflog *reflog = NULL;
439 int error;
440
2508cc66 441 if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1)) < 0)
590fb68b 442 goto cleanup;
443
444 if ((error = git_reflog_read(&reflog, stash)) < 0)
445 goto cleanup;
446
447 if ((error = git_reflog_append(reflog, w_commit_oid, stasher, message)) < 0)
448 goto cleanup;
449
450 if ((error = git_reflog_write(reflog)) < 0)
451 goto cleanup;
452
590fb68b 453cleanup:
454 git_reference_free(stash);
455 git_reflog_free(reflog);
456 return error;
457}
458
459static int is_dirty_cb(const char *path, unsigned int status, void *payload)
460{
461 GIT_UNUSED(path);
462 GIT_UNUSED(status);
463 GIT_UNUSED(payload);
464
465 return 1;
466}
467
468static int ensure_there_are_changes_to_stash(
469 git_repository *repo,
470 bool include_untracked_files,
471 bool include_ignored_files)
472{
473 int error;
79cfa20d 474 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
590fb68b 475
590fb68b 476 opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
477 if (include_untracked_files)
478 opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
479 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
480
481 if (include_ignored_files)
482 opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED;
483
484 error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
485
486 if (error == GIT_EUSER)
487 return 0;
488
489 if (!error)
490 return create_error(GIT_ENOTFOUND, "There is nothing to stash.");
491
492 return error;
493}
494
495static int reset_index_and_workdir(
496 git_repository *repo,
497 git_commit *commit,
498 bool remove_untracked)
499{
b81aa2f1 500 git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
590fb68b 501
cf208031 502 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
590fb68b 503
504 if (remove_untracked)
505 opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED;
506
507 return git_checkout_tree(repo, (git_object *)commit, &opts);
508}
509
510int git_stash_save(
511 git_oid *out,
512 git_repository *repo,
2274993b 513 const git_signature *stasher,
590fb68b 514 const char *message,
515 uint32_t flags)
516{
517 git_index *index = NULL;
518 git_commit *b_commit = NULL, *i_commit = NULL, *u_commit = NULL;
519 git_buf msg = GIT_BUF_INIT;
520 int error;
521
522 assert(out && repo && stasher);
523
a6a82e1a 524 if ((error = git_repository__ensure_not_bare(repo, "stash save")) < 0)
590fb68b 525 return error;
526
527 if ((error = retrieve_base_commit_and_message(&b_commit, &msg, repo)) < 0)
528 goto cleanup;
529
530 if ((error = ensure_there_are_changes_to_stash(
531 repo,
a6a82e1a
RB
532 (flags & GIT_STASH_INCLUDE_UNTRACKED) != 0,
533 (flags & GIT_STASH_INCLUDE_IGNORED) != 0)) < 0)
590fb68b 534 goto cleanup;
535
a6a82e1a 536 if ((error = git_repository_index(&index, repo)) < 0)
590fb68b 537 goto cleanup;
538
a6a82e1a
RB
539 if ((error = commit_index(
540 &i_commit, index, stasher, git_buf_cstr(&msg), b_commit)) < 0)
590fb68b 541 goto cleanup;
542
a6a82e1a
RB
543 if ((flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)) &&
544 (error = commit_untracked(
545 &u_commit, index, stasher, git_buf_cstr(&msg),
546 i_commit, flags)) < 0)
590fb68b 547 goto cleanup;
548
a6a82e1a 549 if ((error = prepare_worktree_commit_message(&msg, message)) < 0)
590fb68b 550 goto cleanup;
551
a6a82e1a
RB
552 if ((error = commit_worktree(
553 out, index, stasher, git_buf_cstr(&msg),
554 i_commit, b_commit, u_commit)) < 0)
590fb68b 555 goto cleanup;
556
557 git_buf_rtrim(&msg);
a6a82e1a
RB
558
559 if ((error = update_reflog(out, repo, stasher, git_buf_cstr(&msg))) < 0)
590fb68b 560 goto cleanup;
561
a6a82e1a 562 if ((error = reset_index_and_workdir(
590fb68b 563 repo,
a6a82e1a
RB
564 ((flags & GIT_STASH_KEEP_INDEX) != 0) ? i_commit : b_commit,
565 (flags & GIT_STASH_INCLUDE_UNTRACKED) != 0)) < 0)
590fb68b 566 goto cleanup;
567
590fb68b 568cleanup:
a6a82e1a 569
590fb68b 570 git_buf_free(&msg);
571 git_commit_free(i_commit);
572 git_commit_free(b_commit);
573 git_commit_free(u_commit);
574 git_index_free(index);
a6a82e1a 575
590fb68b 576 return error;
577}
23388413 578
579int git_stash_foreach(
580 git_repository *repo,
1d8ec670 581 git_stash_cb callback,
23388413 582 void *payload)
583{
584 git_reference *stash;
585 git_reflog *reflog = NULL;
586 int error;
587 size_t i, max;
588 const git_reflog_entry *entry;
589
590 error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE);
52c52737
RB
591 if (error == GIT_ENOTFOUND) {
592 giterr_clear();
23388413 593 return 0;
52c52737 594 }
23388413 595 if (error < 0)
596 goto cleanup;
597
598 if ((error = git_reflog_read(&reflog, stash)) < 0)
599 goto cleanup;
600
601 max = git_reflog_entrycount(reflog);
602 for (i = 0; i < max; i++) {
b15df1d9 603 entry = git_reflog_entry_byindex(reflog, i);
a6a82e1a 604
23388413 605 if (callback(i,
2508cc66
BS
606 git_reflog_entry_message(entry),
607 git_reflog_entry_id_new(entry),
23388413 608 payload)) {
609 error = GIT_EUSER;
a6a82e1a 610 break;
23388413 611 }
612 }
613
23388413 614cleanup:
615 git_reference_free(stash);
616 git_reflog_free(reflog);
617 return error;
618}
e4c64cf2 619
620int git_stash_drop(
621 git_repository *repo,
622 size_t index)
623{
624 git_reference *stash;
625 git_reflog *reflog = NULL;
626 size_t max;
627 int error;
628
629 if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
630 return error;
631
632 if ((error = git_reflog_read(&reflog, stash)) < 0)
633 goto cleanup;
634
635 max = git_reflog_entrycount(reflog);
636
637 if (index > max - 1) {
638 error = GIT_ENOTFOUND;
639 giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index);
640 goto cleanup;
641 }
642
b15df1d9 643 if ((error = git_reflog_drop(reflog, index, true)) < 0)
e4c64cf2 644 goto cleanup;
645
646 if ((error = git_reflog_write(reflog)) < 0)
647 goto cleanup;
648
649 if (max == 1) {
650 error = git_reference_delete(stash);
d00d5464 651 git_reference_free(stash);
e4c64cf2 652 stash = NULL;
9ccab8df 653 } else if (index == 0) {
654 const git_reflog_entry *entry;
655
656 entry = git_reflog_entry_byindex(reflog, 0);
52c52737 657
d00d5464
ET
658 git_reference_free(stash);
659 error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1);
e4c64cf2 660 }
661
662cleanup:
663 git_reference_free(stash);
664 git_reflog_free(reflog);
665 return error;
666}