Skip to content

Commit 164dcb9

Browse files
Linus TorvaldsJunio C Hamano
authored andcommitted
git-merge-tree: generalize the "traverse <n> trees in sync" functionality
It's actually very useful for other things too. Notably, we could do the combined diff a lot more efficiently with this. Signed-off-by: Linus Torvalds <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 01df529 commit 164dcb9

File tree

1 file changed

+67
-53
lines changed

1 file changed

+67
-53
lines changed

merge-tree.c

Lines changed: 67 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -125,44 +125,19 @@ static void unresolved(const char *base, struct name_entry n[3])
125125
printf("3 %06o %s %s%s\n", n[2].mode, sha1_to_hex(n[2].sha1), base, n[2].path);
126126
}
127127

128-
/*
129-
* Merge two trees together (t[1] and t[2]), using a common base (t[0])
130-
* as the origin.
131-
*
132-
* This walks the (sorted) trees in lock-step, checking every possible
133-
* name. Note that directories automatically sort differently from other
134-
* files (see "base_name_compare"), so you'll never see file/directory
135-
* conflicts, because they won't ever compare the same.
136-
*
137-
* IOW, if a directory changes to a filename, it will automatically be
138-
* seen as the directory going away, and the filename being created.
139-
*
140-
* Think of this as a three-way diff.
141-
*
142-
* The output will be either:
143-
* - successful merge
144-
* "0 mode sha1 filename"
145-
* NOTE NOTE NOTE! FIXME! We really really need to walk the index
146-
* in parallel with this too!
147-
*
148-
* - conflict:
149-
* "1 mode sha1 filename"
150-
* "2 mode sha1 filename"
151-
* "3 mode sha1 filename"
152-
* where not all of the 1/2/3 lines may exist, of course.
153-
*
154-
* The successful merge rules are the same as for the three-way merge
155-
* in git-read-tree.
156-
*/
157-
static void merge_trees(struct tree_desc t[3], const char *base)
128+
typedef void (*traverse_callback_t)(int n, unsigned long mask, struct name_entry *entry, const char *base);
129+
130+
static void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback)
158131
{
132+
struct name_entry *entry = xmalloc(n*sizeof(*entry));
133+
159134
for (;;) {
160135
struct name_entry entry[3];
161-
unsigned int mask = 0;
136+
unsigned long mask = 0;
162137
int i, last;
163138

164139
last = -1;
165-
for (i = 0; i < 3; i++) {
140+
for (i = 0; i < n; i++) {
166141
if (!t[i].size)
167142
continue;
168143
entry_extract(t+i, entry+i);
@@ -182,7 +157,7 @@ static void merge_trees(struct tree_desc t[3], const char *base)
182157
if (cmp < 0)
183158
mask = 0;
184159
}
185-
mask |= 1u << i;
160+
mask |= 1ul << i;
186161
last = i;
187162
}
188163
if (!mask)
@@ -192,38 +167,77 @@ static void merge_trees(struct tree_desc t[3], const char *base)
192167
* Update the tree entries we've walked, and clear
193168
* all the unused name-entries.
194169
*/
195-
for (i = 0; i < 3; i++) {
196-
if (mask & (1u << i)) {
170+
for (i = 0; i < n; i++) {
171+
if (mask & (1ul << i)) {
197172
update_tree_entry(t+i);
198173
continue;
199174
}
200175
entry_clear(entry + i);
201176
}
177+
callback(n, mask, entry, base);
178+
}
179+
free(entry);
180+
}
202181

203-
/* Same in both? */
204-
if (same_entry(entry+1, entry+2)) {
205-
if (entry[0].sha1) {
206-
resolve(base, NULL, entry+1);
207-
continue;
208-
}
182+
/*
183+
* Merge two trees together (t[1] and t[2]), using a common base (t[0])
184+
* as the origin.
185+
*
186+
* This walks the (sorted) trees in lock-step, checking every possible
187+
* name. Note that directories automatically sort differently from other
188+
* files (see "base_name_compare"), so you'll never see file/directory
189+
* conflicts, because they won't ever compare the same.
190+
*
191+
* IOW, if a directory changes to a filename, it will automatically be
192+
* seen as the directory going away, and the filename being created.
193+
*
194+
* Think of this as a three-way diff.
195+
*
196+
* The output will be either:
197+
* - successful merge
198+
* "0 mode sha1 filename"
199+
* NOTE NOTE NOTE! FIXME! We really really need to walk the index
200+
* in parallel with this too!
201+
*
202+
* - conflict:
203+
* "1 mode sha1 filename"
204+
* "2 mode sha1 filename"
205+
* "3 mode sha1 filename"
206+
* where not all of the 1/2/3 lines may exist, of course.
207+
*
208+
* The successful merge rules are the same as for the three-way merge
209+
* in git-read-tree.
210+
*/
211+
static void threeway_callback(int n, unsigned long mask, struct name_entry *entry, const char *base)
212+
{
213+
/* Same in both? */
214+
if (same_entry(entry+1, entry+2)) {
215+
if (entry[0].sha1) {
216+
resolve(base, NULL, entry+1);
217+
return;
209218
}
219+
}
210220

211-
if (same_entry(entry+0, entry+1)) {
212-
if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) {
213-
resolve(base, entry+1, entry+2);
214-
continue;
215-
}
221+
if (same_entry(entry+0, entry+1)) {
222+
if (entry[2].sha1 && !S_ISDIR(entry[2].mode)) {
223+
resolve(base, entry+1, entry+2);
224+
return;
216225
}
226+
}
217227

218-
if (same_entry(entry+0, entry+2)) {
219-
if (entry[1].sha1 && !S_ISDIR(entry[1].mode)) {
220-
resolve(base, NULL, entry+1);
221-
continue;
222-
}
228+
if (same_entry(entry+0, entry+2)) {
229+
if (entry[1].sha1 && !S_ISDIR(entry[1].mode)) {
230+
resolve(base, NULL, entry+1);
231+
return;
223232
}
224-
225-
unresolved(base, entry);
226233
}
234+
235+
unresolved(base, entry);
236+
}
237+
238+
static void merge_trees(struct tree_desc t[3], const char *base)
239+
{
240+
traverse_trees(3, t, base, threeway_callback);
227241
}
228242

229243
static void *get_tree_descriptor(struct tree_desc *desc, const char *rev)

0 commit comments

Comments
 (0)