]> git.proxmox.com Git - libgit2.git/blame - src/stash.c
After iconv init reset ref normalize error
[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
4fe0b0b3
JSS
319 opts.flags = GIT_DIFF_IGNORE_SUBMODULES;
320
a6a82e1a 321 if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
590fb68b 322 goto cleanup;
323
a6a82e1a 324 if ((error = git_diff_tree_to_index(&diff, repo, b_tree, NULL, &opts)) < 0)
590fb68b 325 goto cleanup;
326
a6a82e1a 327 if ((error = git_diff_index_to_workdir(&diff2, repo, NULL, &opts)) < 0)
590fb68b 328 goto cleanup;
329
a6a82e1a 330 if ((error = git_diff_merge(diff, diff2)) < 0)
590fb68b 331 goto cleanup;
332
333 data.index = index;
334 data.include_changed = true;
335
a6a82e1a
RB
336 if ((error = git_diff_foreach(
337 diff, update_index_cb, NULL, NULL, &data)) < 0)
338 {
339 if (error == GIT_EUSER)
340 error = data.error;
590fb68b 341 goto cleanup;
a6a82e1a 342 }
590fb68b 343
590fb68b 344
a6a82e1a
RB
345 if ((error = build_tree_from_index(tree_out, index)) < 0)
346 goto cleanup;
590fb68b 347
348cleanup:
349 git_diff_list_free(diff);
350 git_diff_list_free(diff2);
351 git_tree_free(b_tree);
a6a82e1a 352
590fb68b 353 return error;
354}
355
356static int commit_worktree(
357 git_oid *w_commit_oid,
358 git_index *index,
2274993b 359 const git_signature *stasher,
590fb68b 360 const char *message,
361 git_commit *i_commit,
362 git_commit *b_commit,
363 git_commit *u_commit)
364{
a6a82e1a 365 int error = 0;
590fb68b 366 git_tree *w_tree = NULL, *i_tree = NULL;
590fb68b 367 const git_commit *parents[] = { NULL, NULL, NULL };
368
369 parents[0] = b_commit;
370 parents[1] = i_commit;
371 parents[2] = u_commit;
372
a6a82e1a
RB
373 if ((error = git_commit_tree(&i_tree, i_commit)) < 0)
374 goto cleanup;
590fb68b 375
a6a82e1a 376 if ((error = git_index_read_tree(index, i_tree)) < 0)
590fb68b 377 goto cleanup;
378
a6a82e1a 379 if ((error = build_workdir_tree(&w_tree, index, b_commit)) < 0)
590fb68b 380 goto cleanup;
381
a6a82e1a 382 error = git_commit_create(
590fb68b 383 w_commit_oid,
384 git_index_owner(index),
385 NULL,
386 stasher,
387 stasher,
388 NULL,
389 message,
390 w_tree,
a6a82e1a
RB
391 u_commit ? 3 : 2,
392 parents);
590fb68b 393
394cleanup:
395 git_tree_free(i_tree);
396 git_tree_free(w_tree);
397 return error;
398}
399
400static int prepare_worktree_commit_message(
401 git_buf* msg,
402 const char *user_message)
403{
404 git_buf buf = GIT_BUF_INIT;
a6a82e1a
RB
405 int error;
406
6f58332f
RB
407 if ((error = git_buf_set(&buf, git_buf_cstr(msg), git_buf_len(msg))) < 0)
408 return error;
590fb68b 409
590fb68b 410 git_buf_clear(msg);
411
412 if (!user_message)
413 git_buf_printf(msg, "WIP on %s", git_buf_cstr(&buf));
414 else {
415 const char *colon;
416
417 if ((colon = strchr(git_buf_cstr(&buf), ':')) == NULL)
418 goto cleanup;
419
420 git_buf_puts(msg, "On ");
421 git_buf_put(msg, git_buf_cstr(&buf), colon - buf.ptr);
422 git_buf_printf(msg, ": %s\n", user_message);
423 }
424
6f58332f 425 error = (git_buf_oom(msg) || git_buf_oom(&buf)) ? -1 : 0;
590fb68b 426
427cleanup:
428 git_buf_free(&buf);
a6a82e1a 429
590fb68b 430 return error;
431}
432
433static int update_reflog(
434 git_oid *w_commit_oid,
435 git_repository *repo,
2274993b 436 const git_signature *stasher,
590fb68b 437 const char *message)
438{
439 git_reference *stash = NULL;
440 git_reflog *reflog = NULL;
441 int error;
442
2508cc66 443 if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1)) < 0)
590fb68b 444 goto cleanup;
445
446 if ((error = git_reflog_read(&reflog, stash)) < 0)
447 goto cleanup;
448
449 if ((error = git_reflog_append(reflog, w_commit_oid, stasher, message)) < 0)
450 goto cleanup;
451
452 if ((error = git_reflog_write(reflog)) < 0)
453 goto cleanup;
454
590fb68b 455cleanup:
456 git_reference_free(stash);
457 git_reflog_free(reflog);
458 return error;
459}
460
461static int is_dirty_cb(const char *path, unsigned int status, void *payload)
462{
463 GIT_UNUSED(path);
464 GIT_UNUSED(status);
465 GIT_UNUSED(payload);
466
467 return 1;
468}
469
470static int ensure_there_are_changes_to_stash(
471 git_repository *repo,
472 bool include_untracked_files,
473 bool include_ignored_files)
474{
475 int error;
79cfa20d 476 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
590fb68b 477
590fb68b 478 opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
4fe0b0b3
JSS
479 opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
480
590fb68b 481 if (include_untracked_files)
4fe0b0b3 482 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
590fb68b 483 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
484
485 if (include_ignored_files)
4fe0b0b3 486 opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
590fb68b 487
488 error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
489
490 if (error == GIT_EUSER)
491 return 0;
492
493 if (!error)
494 return create_error(GIT_ENOTFOUND, "There is nothing to stash.");
495
496 return error;
497}
498
499static int reset_index_and_workdir(
500 git_repository *repo,
501 git_commit *commit,
502 bool remove_untracked)
503{
b81aa2f1 504 git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
590fb68b 505
cf208031 506 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
590fb68b 507
508 if (remove_untracked)
509 opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED;
510
511 return git_checkout_tree(repo, (git_object *)commit, &opts);
512}
513
514int git_stash_save(
515 git_oid *out,
516 git_repository *repo,
2274993b 517 const git_signature *stasher,
590fb68b 518 const char *message,
519 uint32_t flags)
520{
521 git_index *index = NULL;
522 git_commit *b_commit = NULL, *i_commit = NULL, *u_commit = NULL;
523 git_buf msg = GIT_BUF_INIT;
524 int error;
525
526 assert(out && repo && stasher);
527
a6a82e1a 528 if ((error = git_repository__ensure_not_bare(repo, "stash save")) < 0)
590fb68b 529 return error;
530
531 if ((error = retrieve_base_commit_and_message(&b_commit, &msg, repo)) < 0)
532 goto cleanup;
533
534 if ((error = ensure_there_are_changes_to_stash(
535 repo,
a6a82e1a
RB
536 (flags & GIT_STASH_INCLUDE_UNTRACKED) != 0,
537 (flags & GIT_STASH_INCLUDE_IGNORED) != 0)) < 0)
590fb68b 538 goto cleanup;
539
a6a82e1a 540 if ((error = git_repository_index(&index, repo)) < 0)
590fb68b 541 goto cleanup;
542
a6a82e1a
RB
543 if ((error = commit_index(
544 &i_commit, index, stasher, git_buf_cstr(&msg), b_commit)) < 0)
590fb68b 545 goto cleanup;
546
a6a82e1a
RB
547 if ((flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)) &&
548 (error = commit_untracked(
549 &u_commit, index, stasher, git_buf_cstr(&msg),
550 i_commit, flags)) < 0)
590fb68b 551 goto cleanup;
552
a6a82e1a 553 if ((error = prepare_worktree_commit_message(&msg, message)) < 0)
590fb68b 554 goto cleanup;
555
a6a82e1a
RB
556 if ((error = commit_worktree(
557 out, index, stasher, git_buf_cstr(&msg),
558 i_commit, b_commit, u_commit)) < 0)
590fb68b 559 goto cleanup;
560
561 git_buf_rtrim(&msg);
a6a82e1a
RB
562
563 if ((error = update_reflog(out, repo, stasher, git_buf_cstr(&msg))) < 0)
590fb68b 564 goto cleanup;
565
a6a82e1a 566 if ((error = reset_index_and_workdir(
590fb68b 567 repo,
a6a82e1a
RB
568 ((flags & GIT_STASH_KEEP_INDEX) != 0) ? i_commit : b_commit,
569 (flags & GIT_STASH_INCLUDE_UNTRACKED) != 0)) < 0)
590fb68b 570 goto cleanup;
571
590fb68b 572cleanup:
a6a82e1a 573
590fb68b 574 git_buf_free(&msg);
575 git_commit_free(i_commit);
576 git_commit_free(b_commit);
577 git_commit_free(u_commit);
578 git_index_free(index);
a6a82e1a 579
590fb68b 580 return error;
581}
23388413 582
583int git_stash_foreach(
584 git_repository *repo,
1d8ec670 585 git_stash_cb callback,
23388413 586 void *payload)
587{
588 git_reference *stash;
589 git_reflog *reflog = NULL;
590 int error;
591 size_t i, max;
592 const git_reflog_entry *entry;
593
594 error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE);
52c52737
RB
595 if (error == GIT_ENOTFOUND) {
596 giterr_clear();
23388413 597 return 0;
52c52737 598 }
23388413 599 if (error < 0)
600 goto cleanup;
601
602 if ((error = git_reflog_read(&reflog, stash)) < 0)
603 goto cleanup;
604
605 max = git_reflog_entrycount(reflog);
606 for (i = 0; i < max; i++) {
b15df1d9 607 entry = git_reflog_entry_byindex(reflog, i);
a6a82e1a 608
23388413 609 if (callback(i,
2508cc66
BS
610 git_reflog_entry_message(entry),
611 git_reflog_entry_id_new(entry),
23388413 612 payload)) {
613 error = GIT_EUSER;
a6a82e1a 614 break;
23388413 615 }
616 }
617
23388413 618cleanup:
619 git_reference_free(stash);
620 git_reflog_free(reflog);
621 return error;
622}
e4c64cf2 623
624int git_stash_drop(
625 git_repository *repo,
626 size_t index)
627{
628 git_reference *stash;
629 git_reflog *reflog = NULL;
630 size_t max;
631 int error;
632
633 if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
634 return error;
635
636 if ((error = git_reflog_read(&reflog, stash)) < 0)
637 goto cleanup;
638
639 max = git_reflog_entrycount(reflog);
640
641 if (index > max - 1) {
642 error = GIT_ENOTFOUND;
643 giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index);
644 goto cleanup;
645 }
646
b15df1d9 647 if ((error = git_reflog_drop(reflog, index, true)) < 0)
e4c64cf2 648 goto cleanup;
649
650 if ((error = git_reflog_write(reflog)) < 0)
651 goto cleanup;
652
653 if (max == 1) {
654 error = git_reference_delete(stash);
d00d5464 655 git_reference_free(stash);
e4c64cf2 656 stash = NULL;
9ccab8df 657 } else if (index == 0) {
658 const git_reflog_entry *entry;
659
660 entry = git_reflog_entry_byindex(reflog, 0);
52c52737 661
d00d5464
ET
662 git_reference_free(stash);
663 error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1);
e4c64cf2 664 }
665
666cleanup:
667 git_reference_free(stash);
668 git_reflog_free(reflog);
669 return error;
670}