diff options
-rw-r--r-- | src/diff.h | 9 | ||||
-rw-r--r-- | src/diff_tform.c | 22 | ||||
-rw-r--r-- | src/stash.c | 29 |
3 files changed, 46 insertions, 14 deletions
diff --git a/src/diff.h b/src/diff.h index 39d15fa6d..a202a086c 100644 --- a/src/diff.h +++ b/src/diff.h @@ -133,6 +133,15 @@ typedef git_diff_delta *(*git_diff__merge_cb)( extern int git_diff__merge( git_diff *onto, const git_diff *from, git_diff__merge_cb cb); +extern git_diff_delta *git_diff__merge_like_cgit( + const git_diff_delta *a, + const git_diff_delta *b, + git_pool *pool); + +/* Duplicate a `git_diff_delta` out of the `git_pool` */ +extern git_diff_delta *git_diff__delta_dup( + const git_diff_delta *d, git_pool *pool); + /* * Sometimes a git_diff_file will have a zero size; this attempts to * fill in the size without loading the blob if possible. If that is diff --git a/src/diff_tform.c b/src/diff_tform.c index 52a7b528b..92647e330 100644 --- a/src/diff_tform.c +++ b/src/diff_tform.c @@ -15,7 +15,7 @@ #include "fileops.h" #include "config.h" -static git_diff_delta *diff_delta__dup( +git_diff_delta *git_diff__delta_dup( const git_diff_delta *d, git_pool *pool) { git_diff_delta *delta = git__malloc(sizeof(git_diff_delta)); @@ -46,7 +46,7 @@ fail: return NULL; } -static git_diff_delta *diff_delta__merge_like_cgit( +git_diff_delta *git_diff__merge_like_cgit( const git_diff_delta *a, const git_diff_delta *b, git_pool *pool) @@ -67,16 +67,16 @@ static git_diff_delta *diff_delta__merge_like_cgit( /* If one of the diffs is a conflict, just dup it */ if (b->status == GIT_DELTA_CONFLICTED) - return diff_delta__dup(b, pool); + return git_diff__delta_dup(b, pool); if (a->status == GIT_DELTA_CONFLICTED) - return diff_delta__dup(a, pool); + return git_diff__delta_dup(a, pool); /* if f2 == f3 or f2 is deleted, then just dup the 'a' diff */ if (b->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_DELETED) - return diff_delta__dup(a, pool); + return git_diff__delta_dup(a, pool); /* otherwise, base this diff on the 'b' diff */ - if ((dup = diff_delta__dup(b, pool)) == NULL) + if ((dup = git_diff__delta_dup(b, pool)) == NULL) return NULL; /* If 'a' status is uninteresting, then we're done */ @@ -109,7 +109,7 @@ static git_diff_delta *diff_delta__merge_like_cgit( return dup; } -int git_diff__merge_deltas( +int git_diff__merge( git_diff *onto, const git_diff *from, git_diff__merge_cb cb) { int error = 0; @@ -146,10 +146,10 @@ int git_diff__merge_deltas( STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path); if (cmp < 0) { - delta = diff_delta__dup(o, &onto_pool); + delta = git_diff__delta_dup(o, &onto_pool); i++; } else if (cmp > 0) { - delta = diff_delta__dup(f, &onto_pool); + delta = git_diff__delta_dup(f, &onto_pool); j++; } else { const git_diff_delta *left = reversed ? f : o; @@ -196,7 +196,7 @@ int git_diff__merge_deltas( int git_diff_merge(git_diff *onto, const git_diff *from) { - return git_diff__merge_deltas(onto, from, diff_delta__merge_like_cgit); + return git_diff__merge(onto, from, git_diff__merge_like_cgit); } int git_diff_find_similar__hashsig_for_file( @@ -347,7 +347,7 @@ static int insert_delete_side_of_split( git_diff *diff, git_vector *onto, const git_diff_delta *delta) { /* make new record for DELETED side of split */ - git_diff_delta *deleted = diff_delta__dup(delta, &diff->pool); + git_diff_delta *deleted = git_diff__delta_dup(delta, &diff->pool); GITERR_CHECK_ALLOC(deleted); deleted->status = GIT_DELTA_DELETED; diff --git a/src/stash.c b/src/stash.c index 8f512d4ad..9010c476d 100644 --- a/src/stash.c +++ b/src/stash.c @@ -22,6 +22,7 @@ #include "signature.h" #include "iterator.h" #include "merge.h" +#include "diff.h" static int create_error(int error, const char *msg) { @@ -292,6 +293,25 @@ cleanup: return error; } +static git_diff_delta *stash_delta_merge( + const git_diff_delta *a, + const git_diff_delta *b, + git_pool *pool) +{ + /* Special case for stash: if a file is deleted in the index, but exists + * in the working tree, we need to stash the workdir copy for the workdir. + */ + if (a->status == GIT_DELTA_DELETED && b->status == GIT_DELTA_UNTRACKED) { + git_diff_delta *dup = git_diff__delta_dup(b, pool); + + if (dup) + dup->status = GIT_DELTA_MODIFIED; + return dup; + } + + return git_diff__merge_like_cgit(a, b, pool); +} + static int build_workdir_tree( git_tree **tree_out, git_index *index, @@ -299,17 +319,19 @@ static int build_workdir_tree( { git_repository *repo = git_index_owner(index); git_tree *b_tree = NULL; - git_diff *diff = NULL; + git_diff *diff = NULL, *idx_to_wd = NULL; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; struct stash_update_rules data = {0}; int error; - opts.flags = GIT_DIFF_IGNORE_SUBMODULES; + opts.flags = GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_INCLUDE_UNTRACKED; if ((error = git_commit_tree(&b_tree, b_commit)) < 0) goto cleanup; - if ((error = git_diff_tree_to_workdir(&diff, repo, b_tree, &opts)) < 0) + if ((error = git_diff_tree_to_index(&diff, repo, b_tree, index, &opts)) < 0 || + (error = git_diff_index_to_workdir(&idx_to_wd, repo, index, &opts)) < 0 || + (error = git_diff__merge(diff, idx_to_wd, stash_delta_merge)) < 0) goto cleanup; data.include_changed = true; @@ -320,6 +342,7 @@ static int build_workdir_tree( error = build_tree_from_index(tree_out, index); cleanup: + git_diff_free(idx_to_wd); git_diff_free(diff); git_tree_free(b_tree); |