Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9017711

Browse files
author
Edward Thomson
committed
stash: save the workdir file when deleted in index
When stashing the workdir tree, examine the index as well. Using a mechanism similar to `git_diff_tree_to_workdir_with_index` allows us to determine that a file was added in the index and subsequently modified in the working directory. Without examining the index, we would erroneously believe that this file was untracked and fail to include it in the working directory tree. Use a slightly modified `git_diff_tree_to_workdir_with_index` in order to avoid some of the behavior custom to `git diff`. In particular, be sure to include the working directory side of a file when it was deleted in the index.
1 parent 14304b0 commit 9017711

File tree

3 files changed

+46
-14
lines changed

3 files changed

+46
-14
lines changed

src/diff.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ typedef git_diff_delta *(*git_diff__merge_cb)(
133133
extern int git_diff__merge(
134134
git_diff *onto, const git_diff *from, git_diff__merge_cb cb);
135135

136+
extern git_diff_delta *git_diff__merge_like_cgit(
137+
const git_diff_delta *a,
138+
const git_diff_delta *b,
139+
git_pool *pool);
140+
141+
/* Duplicate a `git_diff_delta` out of the `git_pool` */
142+
extern git_diff_delta *git_diff__delta_dup(
143+
const git_diff_delta *d, git_pool *pool);
144+
136145
/*
137146
* Sometimes a git_diff_file will have a zero size; this attempts to
138147
* fill in the size without loading the blob if possible. If that is

src/diff_tform.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include "fileops.h"
1616
#include "config.h"
1717

18-
static git_diff_delta *diff_delta__dup(
18+
git_diff_delta *git_diff__delta_dup(
1919
const git_diff_delta *d, git_pool *pool)
2020
{
2121
git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
@@ -46,7 +46,7 @@ static git_diff_delta *diff_delta__dup(
4646
return NULL;
4747
}
4848

49-
static git_diff_delta *diff_delta__merge_like_cgit(
49+
git_diff_delta *git_diff__merge_like_cgit(
5050
const git_diff_delta *a,
5151
const git_diff_delta *b,
5252
git_pool *pool)
@@ -67,16 +67,16 @@ static git_diff_delta *diff_delta__merge_like_cgit(
6767

6868
/* If one of the diffs is a conflict, just dup it */
6969
if (b->status == GIT_DELTA_CONFLICTED)
70-
return diff_delta__dup(b, pool);
70+
return git_diff__delta_dup(b, pool);
7171
if (a->status == GIT_DELTA_CONFLICTED)
72-
return diff_delta__dup(a, pool);
72+
return git_diff__delta_dup(a, pool);
7373

7474
/* if f2 == f3 or f2 is deleted, then just dup the 'a' diff */
7575
if (b->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_DELETED)
76-
return diff_delta__dup(a, pool);
76+
return git_diff__delta_dup(a, pool);
7777

7878
/* otherwise, base this diff on the 'b' diff */
79-
if ((dup = diff_delta__dup(b, pool)) == NULL)
79+
if ((dup = git_diff__delta_dup(b, pool)) == NULL)
8080
return NULL;
8181

8282
/* If 'a' status is uninteresting, then we're done */
@@ -109,7 +109,7 @@ static git_diff_delta *diff_delta__merge_like_cgit(
109109
return dup;
110110
}
111111

112-
int git_diff__merge_deltas(
112+
int git_diff__merge(
113113
git_diff *onto, const git_diff *from, git_diff__merge_cb cb)
114114
{
115115
int error = 0;
@@ -146,10 +146,10 @@ int git_diff__merge_deltas(
146146
STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path);
147147

148148
if (cmp < 0) {
149-
delta = diff_delta__dup(o, &onto_pool);
149+
delta = git_diff__delta_dup(o, &onto_pool);
150150
i++;
151151
} else if (cmp > 0) {
152-
delta = diff_delta__dup(f, &onto_pool);
152+
delta = git_diff__delta_dup(f, &onto_pool);
153153
j++;
154154
} else {
155155
const git_diff_delta *left = reversed ? f : o;
@@ -196,7 +196,7 @@ int git_diff__merge_deltas(
196196

197197
int git_diff_merge(git_diff *onto, const git_diff *from)
198198
{
199-
return git_diff__merge_deltas(onto, from, diff_delta__merge_like_cgit);
199+
return git_diff__merge(onto, from, git_diff__merge_like_cgit);
200200
}
201201

202202
int git_diff_find_similar__hashsig_for_file(
@@ -347,7 +347,7 @@ static int insert_delete_side_of_split(
347347
git_diff *diff, git_vector *onto, const git_diff_delta *delta)
348348
{
349349
/* make new record for DELETED side of split */
350-
git_diff_delta *deleted = diff_delta__dup(delta, &diff->pool);
350+
git_diff_delta *deleted = git_diff__delta_dup(delta, &diff->pool);
351351
GITERR_CHECK_ALLOC(deleted);
352352

353353
deleted->status = GIT_DELTA_DELETED;

src/stash.c

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "signature.h"
2323
#include "iterator.h"
2424
#include "merge.h"
25+
#include "diff.h"
2526

2627
static int create_error(int error, const char *msg)
2728
{
@@ -292,24 +293,45 @@ static int commit_untracked(
292293
return error;
293294
}
294295

296+
static git_diff_delta *stash_delta_merge(
297+
const git_diff_delta *a,
298+
const git_diff_delta *b,
299+
git_pool *pool)
300+
{
301+
/* Special case for stash: if a file is deleted in the index, but exists
302+
* in the working tree, we need to stash the workdir copy for the workdir.
303+
*/
304+
if (a->status == GIT_DELTA_DELETED && b->status == GIT_DELTA_UNTRACKED) {
305+
git_diff_delta *dup = git_diff__delta_dup(b, pool);
306+
307+
if (dup)
308+
dup->status = GIT_DELTA_MODIFIED;
309+
return dup;
310+
}
311+
312+
return git_diff__merge_like_cgit(a, b, pool);
313+
}
314+
295315
static int build_workdir_tree(
296316
git_tree **tree_out,
297317
git_index *index,
298318
git_commit *b_commit)
299319
{
300320
git_repository *repo = git_index_owner(index);
301321
git_tree *b_tree = NULL;
302-
git_diff *diff = NULL;
322+
git_diff *diff = NULL, *idx_to_wd = NULL;
303323
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
304324
struct stash_update_rules data = {0};
305325
int error;
306326

307-
opts.flags = GIT_DIFF_IGNORE_SUBMODULES;
327+
opts.flags = GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_INCLUDE_UNTRACKED;
308328

309329
if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
310330
goto cleanup;
311331

312-
if ((error = git_diff_tree_to_workdir(&diff, repo, b_tree, &opts)) < 0)
332+
if ((error = git_diff_tree_to_index(&diff, repo, b_tree, index, &opts)) < 0 ||
333+
(error = git_diff_index_to_workdir(&idx_to_wd, repo, index, &opts)) < 0 ||
334+
(error = git_diff__merge(diff, idx_to_wd, stash_delta_merge)) < 0)
313335
goto cleanup;
314336

315337
data.include_changed = true;
@@ -320,6 +342,7 @@ static int build_workdir_tree(
320342
error = build_tree_from_index(tree_out, index);
321343

322344
cleanup:
345+
git_diff_free(idx_to_wd);
323346
git_diff_free(diff);
324347
git_tree_free(b_tree);
325348

0 commit comments

Comments
 (0)