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

Skip to content

Commit 36fd9e3

Browse files
committed
Fix checkout of modified file when missing from wd
This fixes the checkout case when a file is modified between the baseline and the target and yet missing in the working directory. The logic for that case appears to have been wrong. This also adds a useful checkout notify callback to the checkout test helpers that will count notifications and also has a debug mode to visualize what checkout thinks that it's doing.
1 parent dacce80 commit 36fd9e3

File tree

5 files changed

+128
-1
lines changed

5 files changed

+128
-1
lines changed

include/git2/checkout.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ typedef enum {
183183
GIT_CHECKOUT_NOTIFY_UPDATED = (1u << 2),
184184
GIT_CHECKOUT_NOTIFY_UNTRACKED = (1u << 3),
185185
GIT_CHECKOUT_NOTIFY_IGNORED = (1u << 4),
186+
187+
GIT_CHECKOUT_NOTIFY_ALL = 0x0FFFFu
186188
} git_checkout_notify_t;
187189

188190
/** Checkout notification callback function */

src/checkout.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,11 @@ static int checkout_action_no_wd(
220220
action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE);
221221
break;
222222
case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */
223-
case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */
224223
action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
225224
break;
225+
case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */
226+
action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, CONFLICT);
227+
break;
226228
case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/
227229
if (delta->new_file.mode == GIT_FILEMODE_TREE)
228230
action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);

tests-clar/checkout/checkout_helpers.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,98 @@ void check_file_contents_nocr_at_line(
9191
{
9292
check_file_contents_internal(path, expected, true, file, line, msg);
9393
}
94+
95+
int checkout_count_callback(
96+
git_checkout_notify_t why,
97+
const char *path,
98+
const git_diff_file *baseline,
99+
const git_diff_file *target,
100+
const git_diff_file *workdir,
101+
void *payload)
102+
{
103+
checkout_counts *ct = payload;
104+
105+
GIT_UNUSED(baseline); GIT_UNUSED(target); GIT_UNUSED(workdir);
106+
107+
if (why & GIT_CHECKOUT_NOTIFY_CONFLICT) {
108+
ct->n_conflicts++;
109+
110+
if (ct->debug) {
111+
if (workdir) {
112+
if (baseline) {
113+
if (target)
114+
fprintf(stderr, "M %s (conflicts with M %s)\n",
115+
workdir->path, target->path);
116+
else
117+
fprintf(stderr, "M %s (conflicts with D %s)\n",
118+
workdir->path, baseline->path);
119+
} else {
120+
if (target)
121+
fprintf(stderr, "Existing %s (conflicts with A %s)\n",
122+
workdir->path, target->path);
123+
else
124+
fprintf(stderr, "How can an untracked file be a conflict (%s)\n", workdir->path);
125+
}
126+
} else {
127+
if (baseline) {
128+
if (target)
129+
fprintf(stderr, "D %s (conflicts with M %s)\n",
130+
target->path, baseline->path);
131+
else
132+
fprintf(stderr, "D %s (conflicts with D %s)\n",
133+
baseline->path, baseline->path);
134+
} else {
135+
if (target)
136+
fprintf(stderr, "How can an added file with no workdir be a conflict (%s)\n", target->path);
137+
else
138+
fprintf(stderr, "How can a nonexistent file be a conflict (%s)\n", path);
139+
}
140+
}
141+
}
142+
}
143+
144+
if (why & GIT_CHECKOUT_NOTIFY_DIRTY) {
145+
ct->n_dirty++;
146+
147+
if (ct->debug) {
148+
if (workdir)
149+
fprintf(stderr, "M %s\n", workdir->path);
150+
else
151+
fprintf(stderr, "D %s\n", baseline->path);
152+
}
153+
}
154+
155+
if (why & GIT_CHECKOUT_NOTIFY_UPDATED) {
156+
ct->n_updates++;
157+
158+
if (ct->debug) {
159+
if (baseline) {
160+
if (target)
161+
fprintf(stderr, "update: M %s\n", path);
162+
else
163+
fprintf(stderr, "update: D %s\n", path);
164+
} else {
165+
if (target)
166+
fprintf(stderr, "update: A %s\n", path);
167+
else
168+
fprintf(stderr, "update: this makes no sense %s\n", path);
169+
}
170+
}
171+
}
172+
173+
if (why & GIT_CHECKOUT_NOTIFY_UNTRACKED) {
174+
ct->n_untracked++;
175+
176+
if (ct->debug)
177+
fprintf(stderr, "? %s\n", path);
178+
}
179+
180+
if (why & GIT_CHECKOUT_NOTIFY_IGNORED) {
181+
ct->n_ignored++;
182+
183+
if (ct->debug)
184+
fprintf(stderr, "I %s\n", path);
185+
}
186+
187+
return 0;
188+
}

tests-clar/checkout/checkout_helpers.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,20 @@ extern void check_file_contents_nocr_at_line(
1919

2020
#define check_file_contents_nocr(PATH,EXP) \
2121
check_file_contents_nocr_at_line(PATH,EXP,__FILE__,__LINE__,"String mismatch: " #EXP " != " #PATH)
22+
23+
typedef struct {
24+
int n_conflicts;
25+
int n_dirty;
26+
int n_updates;
27+
int n_untracked;
28+
int n_ignored;
29+
int debug;
30+
} checkout_counts;
31+
32+
extern int checkout_count_callback(
33+
git_checkout_notify_t why,
34+
const char *path,
35+
const git_diff_file *baseline,
36+
const git_diff_file *target,
37+
const git_diff_file *workdir,
38+
void *payload);

tests-clar/checkout/tree.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,9 +448,15 @@ void test_checkout_tree__donot_update_deleted_file_by_default(void)
448448
git_oid old_id, new_id;
449449
git_commit *old_commit = NULL, *new_commit = NULL;
450450
git_index *index = NULL;
451+
checkout_counts ct;
451452

452453
opts.checkout_strategy = GIT_CHECKOUT_SAFE;
453454

455+
memset(&ct, 0, sizeof(ct));
456+
opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
457+
opts.notify_cb = checkout_count_callback;
458+
opts.notify_payload = &ct;
459+
454460
cl_git_pass(git_repository_index(&index, g_repo));
455461

456462
cl_git_pass(git_oid_fromstr(&old_id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
@@ -465,8 +471,13 @@ void test_checkout_tree__donot_update_deleted_file_by_default(void)
465471

466472
cl_git_pass(git_oid_fromstr(&new_id, "099fabac3a9ea935598528c27f866e34089c2eff"));
467473
cl_git_pass(git_commit_lookup(&new_commit, g_repo, &new_id));
474+
475+
468476
cl_git_fail(git_checkout_tree(g_repo, (git_object *)new_commit, &opts));
469477

478+
cl_assert_equal_i(1, ct.n_conflicts);
479+
cl_assert_equal_i(1, ct.n_updates);
480+
470481
git_commit_free(old_commit);
471482
git_commit_free(new_commit);
472483
git_index_free(index);

0 commit comments

Comments
 (0)