]> git.proxmox.com Git - libgit2.git/blame - src/stash.c
Fix C99 __func__ for MSVC
[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
10672e3e 156struct stash_update_rules {
590fb68b 157 bool include_changed;
158 bool include_untracked;
159 bool include_ignored;
160};
161
10672e3e
RB
162static int stash_update_index_from_diff(
163 git_index *index,
164 const git_diff *diff,
165 struct stash_update_rules *data)
590fb68b 166{
10672e3e
RB
167 int error = 0;
168 size_t d, max_d = git_diff_num_deltas(diff);
169
170 for (d = 0; !error && d < max_d; ++d) {
171 const char *add_path = NULL;
172 const git_diff_delta *delta = git_diff_get_delta(diff, d);
173
174 switch (delta->status) {
175 case GIT_DELTA_IGNORED:
176 if (data->include_ignored)
177 add_path = delta->new_file.path;
178 break;
179
180 case GIT_DELTA_UNTRACKED:
181 if (data->include_untracked)
182 add_path = delta->new_file.path;
590fb68b 183 break;
590fb68b 184
10672e3e
RB
185 case GIT_DELTA_ADDED:
186 case GIT_DELTA_MODIFIED:
187 if (data->include_changed)
188 add_path = delta->new_file.path;
189 break;
a6a82e1a 190
10672e3e
RB
191 case GIT_DELTA_DELETED:
192 if (data->include_changed &&
193 !git_index_find(NULL, index, delta->old_file.path))
194 error = git_index_remove(index, delta->old_file.path, 0);
195 break;
196
197 default:
198 /* Unimplemented */
199 giterr_set(
200 GITERR_INVALID,
201 "Cannot update index. Unimplemented status (%d)",
202 delta->status);
203 return -1;
204 }
205
206 if (add_path != NULL)
207 error = git_index_add_bypath(index, add_path);
208 }
209
210 return error;
590fb68b 211}
212
213static int build_untracked_tree(
214 git_tree **tree_out,
215 git_index *index,
216 git_commit *i_commit,
217 uint32_t flags)
218{
219 git_tree *i_tree = NULL;
3ff1d123 220 git_diff *diff = NULL;
2f8d30be 221 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
10672e3e 222 struct stash_update_rules data = {0};
a6a82e1a 223 int error;
590fb68b 224
225 git_index_clear(index);
226
590fb68b 227 if (flags & GIT_STASH_INCLUDE_UNTRACKED) {
a6a82e1a
RB
228 opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED |
229 GIT_DIFF_RECURSE_UNTRACKED_DIRS;
590fb68b 230 data.include_untracked = true;
231 }
232
233 if (flags & GIT_STASH_INCLUDE_IGNORED) {
234 opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
235 data.include_ignored = true;
236 }
237
a6a82e1a 238 if ((error = git_commit_tree(&i_tree, i_commit)) < 0)
590fb68b 239 goto cleanup;
240
a6a82e1a
RB
241 if ((error = git_diff_tree_to_workdir(
242 &diff, git_index_owner(index), i_tree, &opts)) < 0)
590fb68b 243 goto cleanup;
244
10672e3e 245 if ((error = stash_update_index_from_diff(index, diff, &data)) < 0)
590fb68b 246 goto cleanup;
247
a6a82e1a 248 error = build_tree_from_index(tree_out, index);
590fb68b 249
250cleanup:
3ff1d123 251 git_diff_free(diff);
590fb68b 252 git_tree_free(i_tree);
253 return error;
254}
255
256static int commit_untracked(
257 git_commit **u_commit,
258 git_index *index,
2274993b 259 const git_signature *stasher,
590fb68b 260 const char *message,
261 git_commit *i_commit,
262 uint32_t flags)
263{
264 git_tree *u_tree = NULL;
265 git_oid u_commit_oid;
266 git_buf msg = GIT_BUF_INIT;
a6a82e1a 267 int error;
590fb68b 268
a6a82e1a 269 if ((error = build_untracked_tree(&u_tree, index, i_commit, flags)) < 0)
590fb68b 270 goto cleanup;
271
a6a82e1a 272 if ((error = git_buf_printf(&msg, "untracked files on %s\n", message)) < 0)
590fb68b 273 goto cleanup;
274
a6a82e1a 275 if ((error = git_commit_create(
590fb68b 276 &u_commit_oid,
277 git_index_owner(index),
278 NULL,
279 stasher,
280 stasher,
281 NULL,
282 git_buf_cstr(&msg),
283 u_tree,
284 0,
a6a82e1a
RB
285 NULL)) < 0)
286 goto cleanup;
590fb68b 287
288 error = git_commit_lookup(u_commit, git_index_owner(index), &u_commit_oid);
289
290cleanup:
291 git_tree_free(u_tree);
292 git_buf_free(&msg);
293 return error;
294}
295
296static int build_workdir_tree(
297 git_tree **tree_out,
298 git_index *index,
299 git_commit *b_commit)
300{
5735bf5e 301 git_repository *repo = git_index_owner(index);
590fb68b 302 git_tree *b_tree = NULL;
10672e3e 303 git_diff *diff = NULL;
2f8d30be 304 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
10672e3e 305 struct stash_update_rules data = {0};
a6a82e1a 306 int error;
590fb68b 307
4fe0b0b3
JSS
308 opts.flags = GIT_DIFF_IGNORE_SUBMODULES;
309
a6a82e1a 310 if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
590fb68b 311 goto cleanup;
312
10672e3e
RB
313 if ((error = git_diff_tree_to_workdir_with_index(
314 &diff, repo, b_tree, &opts)) < 0)
590fb68b 315 goto cleanup;
316
590fb68b 317 data.include_changed = true;
318
10672e3e 319 if ((error = stash_update_index_from_diff(index, diff, &data)) < 0)
590fb68b 320 goto cleanup;
321
10672e3e 322 error = build_tree_from_index(tree_out, index);
590fb68b 323
324cleanup:
3ff1d123 325 git_diff_free(diff);
590fb68b 326 git_tree_free(b_tree);
a6a82e1a 327
590fb68b 328 return error;
329}
330
331static int commit_worktree(
332 git_oid *w_commit_oid,
333 git_index *index,
2274993b 334 const git_signature *stasher,
590fb68b 335 const char *message,
336 git_commit *i_commit,
337 git_commit *b_commit,
338 git_commit *u_commit)
339{
a6a82e1a 340 int error = 0;
590fb68b 341 git_tree *w_tree = NULL, *i_tree = NULL;
590fb68b 342 const git_commit *parents[] = { NULL, NULL, NULL };
343
344 parents[0] = b_commit;
345 parents[1] = i_commit;
346 parents[2] = u_commit;
347
a6a82e1a
RB
348 if ((error = git_commit_tree(&i_tree, i_commit)) < 0)
349 goto cleanup;
590fb68b 350
a6a82e1a 351 if ((error = git_index_read_tree(index, i_tree)) < 0)
590fb68b 352 goto cleanup;
353
a6a82e1a 354 if ((error = build_workdir_tree(&w_tree, index, b_commit)) < 0)
590fb68b 355 goto cleanup;
356
a6a82e1a 357 error = git_commit_create(
590fb68b 358 w_commit_oid,
359 git_index_owner(index),
360 NULL,
361 stasher,
362 stasher,
363 NULL,
364 message,
365 w_tree,
a6a82e1a
RB
366 u_commit ? 3 : 2,
367 parents);
590fb68b 368
369cleanup:
370 git_tree_free(i_tree);
371 git_tree_free(w_tree);
372 return error;
373}
374
375static int prepare_worktree_commit_message(
376 git_buf* msg,
377 const char *user_message)
378{
379 git_buf buf = GIT_BUF_INIT;
a6a82e1a
RB
380 int error;
381
6f58332f
RB
382 if ((error = git_buf_set(&buf, git_buf_cstr(msg), git_buf_len(msg))) < 0)
383 return error;
590fb68b 384
590fb68b 385 git_buf_clear(msg);
386
387 if (!user_message)
388 git_buf_printf(msg, "WIP on %s", git_buf_cstr(&buf));
389 else {
390 const char *colon;
391
392 if ((colon = strchr(git_buf_cstr(&buf), ':')) == NULL)
393 goto cleanup;
394
395 git_buf_puts(msg, "On ");
396 git_buf_put(msg, git_buf_cstr(&buf), colon - buf.ptr);
397 git_buf_printf(msg, ": %s\n", user_message);
398 }
399
6f58332f 400 error = (git_buf_oom(msg) || git_buf_oom(&buf)) ? -1 : 0;
590fb68b 401
402cleanup:
403 git_buf_free(&buf);
a6a82e1a 404
590fb68b 405 return error;
406}
407
408static int update_reflog(
409 git_oid *w_commit_oid,
410 git_repository *repo,
2274993b 411 const git_signature *stasher,
590fb68b 412 const char *message)
413{
b976f3c2 414 git_reference *stash;
590fb68b 415 git_reflog *reflog = NULL;
416 int error;
417
2508cc66 418 if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1)) < 0)
590fb68b 419 goto cleanup;
420
b976f3c2
CMN
421 git_reference_free(stash);
422
423 if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE) < 0))
590fb68b 424 goto cleanup;
425
426 if ((error = git_reflog_append(reflog, w_commit_oid, stasher, message)) < 0)
427 goto cleanup;
428
429 if ((error = git_reflog_write(reflog)) < 0)
430 goto cleanup;
431
590fb68b 432cleanup:
590fb68b 433 git_reflog_free(reflog);
434 return error;
435}
436
437static int is_dirty_cb(const char *path, unsigned int status, void *payload)
438{
439 GIT_UNUSED(path);
440 GIT_UNUSED(status);
441 GIT_UNUSED(payload);
442
25e0b157 443 return GIT_PASSTHROUGH;
590fb68b 444}
445
446static int ensure_there_are_changes_to_stash(
447 git_repository *repo,
448 bool include_untracked_files,
449 bool include_ignored_files)
450{
451 int error;
79cfa20d 452 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
590fb68b 453
590fb68b 454 opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
4fe0b0b3
JSS
455 opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
456
590fb68b 457 if (include_untracked_files)
4fe0b0b3 458 opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
590fb68b 459 GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
460
461 if (include_ignored_files)
4fe0b0b3 462 opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
590fb68b 463
464 error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
465
25e0b157 466 if (error == GIT_PASSTHROUGH)
590fb68b 467 return 0;
468
469 if (!error)
470 return create_error(GIT_ENOTFOUND, "There is nothing to stash.");
471
472 return error;
473}
474
475static int reset_index_and_workdir(
476 git_repository *repo,
477 git_commit *commit,
478 bool remove_untracked)
479{
b81aa2f1 480 git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
590fb68b 481
cf208031 482 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
590fb68b 483
484 if (remove_untracked)
485 opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED;
486
487 return git_checkout_tree(repo, (git_object *)commit, &opts);
488}
489
490int git_stash_save(
491 git_oid *out,
492 git_repository *repo,
2274993b 493 const git_signature *stasher,
590fb68b 494 const char *message,
495 uint32_t flags)
496{
497 git_index *index = NULL;
498 git_commit *b_commit = NULL, *i_commit = NULL, *u_commit = NULL;
499 git_buf msg = GIT_BUF_INIT;
500 int error;
501
502 assert(out && repo && stasher);
503
a6a82e1a 504 if ((error = git_repository__ensure_not_bare(repo, "stash save")) < 0)
590fb68b 505 return error;
506
507 if ((error = retrieve_base_commit_and_message(&b_commit, &msg, repo)) < 0)
508 goto cleanup;
509
510 if ((error = ensure_there_are_changes_to_stash(
511 repo,
a6a82e1a
RB
512 (flags & GIT_STASH_INCLUDE_UNTRACKED) != 0,
513 (flags & GIT_STASH_INCLUDE_IGNORED) != 0)) < 0)
590fb68b 514 goto cleanup;
515
a6a82e1a 516 if ((error = git_repository_index(&index, repo)) < 0)
590fb68b 517 goto cleanup;
518
a6a82e1a
RB
519 if ((error = commit_index(
520 &i_commit, index, stasher, git_buf_cstr(&msg), b_commit)) < 0)
590fb68b 521 goto cleanup;
522
a6a82e1a
RB
523 if ((flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)) &&
524 (error = commit_untracked(
525 &u_commit, index, stasher, git_buf_cstr(&msg),
526 i_commit, flags)) < 0)
590fb68b 527 goto cleanup;
528
a6a82e1a 529 if ((error = prepare_worktree_commit_message(&msg, message)) < 0)
590fb68b 530 goto cleanup;
531
a6a82e1a
RB
532 if ((error = commit_worktree(
533 out, index, stasher, git_buf_cstr(&msg),
534 i_commit, b_commit, u_commit)) < 0)
590fb68b 535 goto cleanup;
536
537 git_buf_rtrim(&msg);
a6a82e1a
RB
538
539 if ((error = update_reflog(out, repo, stasher, git_buf_cstr(&msg))) < 0)
590fb68b 540 goto cleanup;
541
a6a82e1a 542 if ((error = reset_index_and_workdir(
590fb68b 543 repo,
a6a82e1a
RB
544 ((flags & GIT_STASH_KEEP_INDEX) != 0) ? i_commit : b_commit,
545 (flags & GIT_STASH_INCLUDE_UNTRACKED) != 0)) < 0)
590fb68b 546 goto cleanup;
547
590fb68b 548cleanup:
a6a82e1a 549
590fb68b 550 git_buf_free(&msg);
551 git_commit_free(i_commit);
552 git_commit_free(b_commit);
553 git_commit_free(u_commit);
554 git_index_free(index);
a6a82e1a 555
590fb68b 556 return error;
557}
23388413 558
559int git_stash_foreach(
560 git_repository *repo,
1d8ec670 561 git_stash_cb callback,
23388413 562 void *payload)
563{
564 git_reference *stash;
565 git_reflog *reflog = NULL;
566 int error;
567 size_t i, max;
568 const git_reflog_entry *entry;
569
570 error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE);
52c52737
RB
571 if (error == GIT_ENOTFOUND) {
572 giterr_clear();
23388413 573 return 0;
52c52737 574 }
23388413 575 if (error < 0)
576 goto cleanup;
577
b976f3c2 578 if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
23388413 579 goto cleanup;
580
581 max = git_reflog_entrycount(reflog);
582 for (i = 0; i < max; i++) {
b15df1d9 583 entry = git_reflog_entry_byindex(reflog, i);
a6a82e1a 584
25e0b157
RB
585 error = GITERR_CALLBACK(
586 callback(i,
587 git_reflog_entry_message(entry),
588 git_reflog_entry_id_new(entry),
589 payload) );
590 if (error)
dab89f9b 591 break;
23388413 592 }
593
23388413 594cleanup:
595 git_reference_free(stash);
596 git_reflog_free(reflog);
597 return error;
598}
e4c64cf2 599
600int git_stash_drop(
601 git_repository *repo,
602 size_t index)
603{
604 git_reference *stash;
605 git_reflog *reflog = NULL;
606 size_t max;
607 int error;
608
609 if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
610 return error;
611
b976f3c2 612 if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
e4c64cf2 613 goto cleanup;
614
615 max = git_reflog_entrycount(reflog);
616
617 if (index > max - 1) {
618 error = GIT_ENOTFOUND;
619 giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index);
620 goto cleanup;
621 }
622
b15df1d9 623 if ((error = git_reflog_drop(reflog, index, true)) < 0)
e4c64cf2 624 goto cleanup;
625
626 if ((error = git_reflog_write(reflog)) < 0)
627 goto cleanup;
628
629 if (max == 1) {
630 error = git_reference_delete(stash);
d00d5464 631 git_reference_free(stash);
e4c64cf2 632 stash = NULL;
9ccab8df 633 } else if (index == 0) {
634 const git_reflog_entry *entry;
635
636 entry = git_reflog_entry_byindex(reflog, 0);
52c52737 637
d00d5464
ET
638 git_reference_free(stash);
639 error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1);
e4c64cf2 640 }
641
642cleanup:
643 git_reference_free(stash);
644 git_reflog_free(reflog);
645 return error;
646}