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
#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));
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)
/* 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 */
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;
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;
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(
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;
#include "signature.h"
#include "iterator.h"
#include "merge.h"
+#include "diff.h"
static int create_error(int error, const char *msg)
{
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,
{
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;
error = build_tree_from_index(tree_out, index);
cleanup:
+ git_diff_free(idx_to_wd);
git_diff_free(diff);
git_tree_free(b_tree);