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