checkout: allow "checkout -m path" to unmerge removed paths
"git checkout -m -- path" uses the unmerge_marked_index() API, whose implementation is incapable of unresolving a path that was resolved as removed. Extend the unmerge_index() API function so that we can mark the ce_flags member of the cache entries we add to the index as unmerged, and replace use of unmerge_marked_index() with it. Now, together with its unmerge_index_entry_at() helper function, unmerge_marked_index() function is no longer called by anybody, and can safely be removed. This makes two known test failures in t2070 and t7201 to succeed. Signed-off-by: Junio C Hamano <gitster@pobox.com>
Junio C Hamano committed
Jul 31, 2023 at 15:44 UTC
5bdedac3c7b0894643256304cd5f0dd29cf8eec9
7 files changed
+13
-82
builtin/checkout.c
+2
-4
@@ -566,6 +566,8 @@ static int checkout_paths(const struct checkout_opts *opts,
566
567
if (opts->source_tree)
568
read_tree_some(opts->source_tree, &opts->pathspec);
569
+ if (opts->merge)
570
+ unmerge_index(&the_index, &opts->pathspec, CE_MATCHED);
571
572
ps_matched = xcalloc(opts->pathspec.nr, 1);
573
@@ -589,10 +591,6 @@ static int checkout_paths(const struct checkout_opts *opts,
591
}
592
free(ps_matched);
593
592
- /* "checkout -m path" to recreate conflicted state */
593
- if (opts->merge)
594
- unmerge_marked_index(&the_index);
595
-
594
/* Any unmerged paths? */
595
for (pos = 0; pos < the_index.cache_nr; pos++) {
596
const struct cache_entry *ce = the_index.cache[pos];
builtin/update-index.c
+1
-1
@@ -646,7 +646,7 @@ static int unresolve_one(const char *path)
646
item = string_list_lookup(the_index.resolve_undo, path);
647
if (!item)
648
return res; /* no resolve-undo record for the path */
649
- res = unmerge_index_entry(&the_index, path, item->util);
649
+ res = unmerge_index_entry(&the_index, path, item->util, 0);
650
FREE_AND_NULL(item->util);
651
return res;
652
}
rerere.c
+1
-1
@@ -1112,7 +1112,7 @@ int rerere_forget(struct repository *r, struct pathspec *pathspec)
1112
* recover the original conflicted state and then
1113
* find the conflicted paths.
1114
*/
1115
- unmerge_index(r->index, pathspec);
1115
+ unmerge_index(r->index, pathspec, 0);
1116
find_conflict(r, &conflict);
1117
for (i = 0; i < conflict.nr; i++) {
1118
struct string_list_item *it = &conflict.items[i];
resolve-undo.c
+5
-70
@@ -115,75 +115,8 @@ void resolve_undo_clear_index(struct index_state *istate)
115
istate->cache_changed |= RESOLVE_UNDO_CHANGED;
116
}
117
118
-int unmerge_index_entry_at(struct index_state *istate, int pos)
119
-{
120
- const struct cache_entry *ce;
121
- struct string_list_item *item;
122
- struct resolve_undo_info *ru;
123
- int i, err = 0, matched;
124
- char *name;
125
-
126
- if (!istate->resolve_undo)
127
- return pos;
128
-
129
- ce = istate->cache[pos];
130
- if (ce_stage(ce)) {
131
- /* already unmerged */
132
- while ((pos < istate->cache_nr) &&
133
- ! strcmp(istate->cache[pos]->name, ce->name))
134
- pos++;
135
- return pos - 1; /* return the last entry processed */
136
- }
137
- item = string_list_lookup(istate->resolve_undo, ce->name);
138
- if (!item)
139
- return pos;
140
- ru = item->util;
141
- if (!ru)
142
- return pos;
143
- matched = ce->ce_flags & CE_MATCHED;
144
- name = xstrdup(ce->name);
145
- remove_index_entry_at(istate, pos);
146
- for (i = 0; i < 3; i++) {
147
- struct cache_entry *nce;
148
- if (!ru->mode[i])
149
- continue;
150
- nce = make_cache_entry(istate,
151
- ru->mode[i],
152
- &ru->oid[i],
153
- name, i + 1, 0);
154
- if (matched)
155
- nce->ce_flags |= CE_MATCHED;
156
- if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) {
157
- err = 1;
158
- error("cannot unmerge '%s'", name);
159
- }
160
- }
161
- free(name);
162
- if (err)
163
- return pos;
164
- free(ru);
165
- item->util = NULL;
166
- return unmerge_index_entry_at(istate, pos);
167
-}
168
-
169
-void unmerge_marked_index(struct index_state *istate)
170
-{
171
- int i;
172
-
173
- if (!istate->resolve_undo)
174
- return;
175
-
176
- /* TODO: audit for interaction with sparse-index. */
177
- ensure_full_index(istate);
178
- for (i = 0; i < istate->cache_nr; i++) {
179
- const struct cache_entry *ce = istate->cache[i];
180
- if (ce->ce_flags & CE_MATCHED)
181
- i = unmerge_index_entry_at(istate, i);
182
- }
183
-}
184
-
118
int unmerge_index_entry(struct index_state *istate, const char *path,
186
- struct resolve_undo_info *ru)
119
+ struct resolve_undo_info *ru, unsigned ce_flags)
120
{
121
int i = index_name_pos(istate, path, strlen(path));
122
@@ -206,13 +139,15 @@ int unmerge_index_entry(struct index_state *istate, const char *path,
139
continue;
140
ce = make_cache_entry(istate, ru->mode[i], &ru->oid[i],
141
path, i + 1, 0);
142
+ ce->ce_flags |= ce_flags;
143
if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD))
144
return error("cannot unmerge '%s'", path);
145
}
146
return 0;
147
}
148
215
-void unmerge_index(struct index_state *istate, const struct pathspec *pathspec)
149
+void unmerge_index(struct index_state *istate, const struct pathspec *pathspec,
150
+ unsigned ce_flags)
151
{
152
struct string_list_item *item;
153
@@ -231,7 +166,7 @@ void unmerge_index(struct index_state *istate, const struct pathspec *pathspec)
166
item->string, strlen(item->string),
167
0, NULL, 0))
168
continue;
234
- unmerge_index_entry(istate, path, ru);
169
+ unmerge_index_entry(istate, path, ru, ce_flags);
170
free(ru);
171
item->util = NULL;
172
}
resolve-undo.h
+2
-4
@@ -17,9 +17,7 @@ void record_resolve_undo(struct index_state *, struct cache_entry *);
17
void resolve_undo_write(struct strbuf *, struct string_list *);
18
struct string_list *resolve_undo_read(const char *, unsigned long);
19
void resolve_undo_clear_index(struct index_state *);
20
-int unmerge_index_entry_at(struct index_state *, int);
21
-int unmerge_index_entry(struct index_state *, const char *, struct resolve_undo_info *);
22
-void unmerge_index(struct index_state *, const struct pathspec *);
23
-void unmerge_marked_index(struct index_state *);
20
+int unmerge_index_entry(struct index_state *, const char *, struct resolve_undo_info *, unsigned);
21
+void unmerge_index(struct index_state *, const struct pathspec *, unsigned);
22
23
#endif
t/t2070-restore.sh
+1
-1
@@ -180,7 +180,7 @@ test_expect_success 'restore --merge to unresolve after (mistaken) resolution' '
180
test_cmp expect file
181
'
182
183
-test_expect_failure 'restore --merge to unresolve after (mistaken) resolution' '
183
+test_expect_success 'restore --merge to unresolve after (mistaken) resolution' '
184
O=$(echo original | git hash-object -w --stdin) &&
185
A=$(echo ourside | git hash-object -w --stdin) &&
186
B=$(echo theirside | git hash-object -w --stdin) &&
t/t7201-co.sh
+1
-1
@@ -543,7 +543,7 @@ test_expect_success 'checkout -m works after (mistaken) resolution' '
543
test_cmp merged file
544
'
545
546
-test_expect_failure 'checkout -m works after (mistaken) resolution to remove' '
546
+test_expect_success 'checkout -m works after (mistaken) resolution to remove' '
547
setup_conflicting_index &&
548
echo "none of the above" >sample &&
549
cat sample >fild &&