Skip to content

Commit 8378807

Browse files
Linus TorvaldsJunio C Hamano
authored andcommitted
Prepare "git-merge-tree" for future work
This changes how "git-merge-tree" works in two ways: - instead of printing things out as we walk the trees, we save the results in memory. - when we've walked the tree fully, we print out the results in a more explicit way, describing the data. This is basically preparatory work for extending the git-merge-tree functionality in interesting directions. In particular, git-merge-tree is also how you would create a diff between two trees _without_ necessarily creating the merge commit itself. In other words, if you were to just wonder what another branch adds, you should be able to (eventually) just do git merge-tree -p $base HEAD $otherbranch to generate a diff of what the merge would look like. The current merge tree already basically has all the smarts for this, and the explanation of the results just means that hopefully somebody else than me could do the boring work. (You'd basically be able to do the above diff by just changing the printout format for the explanation, and making the "changed in both" first do a three-way merge before it diffs the result). The other thing that the in-memory format allows is rename detection (which the current code does not do). That's the basic reason why we don't want to just explain the differences as we go along - because we want to be able to look at the _other_ differences to see whether the reason an entry got deleted in either branch was perhaps because it got added in another place.. Rename detection should be a fairly trivial pass in between the tree diffing and the explanation. In the meantime, this doesn't actually do anything new, it just outputs the information in a more verbose manner. For an example merge, commit 5ab2c0a in the git tree works well and shows renames, along with true removals and additions and files that got changed in both branches. To see that as a tree merge, do: git-merge-tree 64e86c5 c5c2374 928e47e where the two last ones are the tips that got merged, and the first one is the merge base. Signed-off-by: Linus Torvalds <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a9ed376 commit 8378807

File tree

1 file changed

+119
-15
lines changed

1 file changed

+119
-15
lines changed

merge-tree.c

Lines changed: 119 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,79 @@
11
#include "cache.h"
22
#include "tree-walk.h"
3+
#include "blob.h"
34

45
static const char merge_tree_usage[] = "git-merge-tree <base-tree> <branch1> <branch2>";
56
static int resolve_directories = 1;
67

8+
struct merge_list {
9+
struct merge_list *next;
10+
struct merge_list *link; /* other stages for this object */
11+
12+
unsigned int stage : 2,
13+
flags : 30;
14+
unsigned int mode;
15+
const char *path;
16+
struct blob *blob;
17+
};
18+
19+
static struct merge_list *merge_result, **merge_result_end = &merge_result;
20+
21+
static void add_merge_entry(struct merge_list *entry)
22+
{
23+
*merge_result_end = entry;
24+
merge_result_end = &entry->next;
25+
}
26+
727
static void merge_trees(struct tree_desc t[3], const char *base);
828

29+
static const char *explanation(struct merge_list *entry)
30+
{
31+
switch (entry->stage) {
32+
case 0:
33+
return "merged";
34+
case 3:
35+
return "added in remote";
36+
case 2:
37+
if (entry->link)
38+
return "added in both";
39+
return "added in local";
40+
}
41+
42+
/* Existed in base */
43+
entry = entry->link;
44+
if (!entry)
45+
return "removed in both";
46+
47+
if (entry->link)
48+
return "changed in both";
49+
50+
if (entry->stage == 3)
51+
return "removed in local";
52+
return "removed in remote";
53+
}
54+
55+
static void show_result_list(struct merge_list *entry)
56+
{
57+
printf("%s\n", explanation(entry));
58+
do {
59+
struct merge_list *link = entry->link;
60+
static const char *desc[4] = { "result", "base", "our", "their" };
61+
printf(" %-6s %o %s %s\n", desc[entry->stage], entry->mode, sha1_to_hex(entry->blob->object.sha1), entry->path);
62+
entry = link;
63+
} while (entry);
64+
}
65+
66+
static void show_result(void)
67+
{
68+
struct merge_list *walk;
69+
70+
walk = merge_result;
71+
while (walk) {
72+
show_result_list(walk);
73+
walk = walk->next;
74+
}
75+
}
76+
977
/* An empty entry never compares same, not even to another empty entry */
1078
static int same_entry(struct name_entry *a, struct name_entry *b)
1179
{
@@ -15,24 +83,34 @@ static int same_entry(struct name_entry *a, struct name_entry *b)
1583
a->mode == b->mode;
1684
}
1785

18-
static const char *sha1_to_hex_zero(const unsigned char *sha1)
86+
static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
1987
{
20-
if (sha1)
21-
return sha1_to_hex(sha1);
22-
return "0000000000000000000000000000000000000000";
88+
struct merge_list *res = xmalloc(sizeof(*res));
89+
90+
memset(res, 0, sizeof(*res));
91+
res->stage = stage;
92+
res->path = path;
93+
res->mode = mode;
94+
res->blob = lookup_blob(sha1);
95+
return res;
2396
}
2497

2598
static void resolve(const char *base, struct name_entry *branch1, struct name_entry *result)
2699
{
100+
struct merge_list *orig, *final;
101+
const char *path;
102+
27103
/* If it's already branch1, don't bother showing it */
28104
if (!branch1)
29105
return;
30106

31-
printf("0 %06o->%06o %s->%s %s%s\n",
32-
branch1->mode, result->mode,
33-
sha1_to_hex_zero(branch1->sha1),
34-
sha1_to_hex_zero(result->sha1),
35-
base, result->path);
107+
path = strdup(mkpath("%s%s", base, result->path));
108+
orig = create_entry(2, branch1->mode, branch1->sha1, path);
109+
final = create_entry(0, result->mode, result->sha1, path);
110+
111+
final->link = orig;
112+
113+
add_merge_entry(final);
36114
}
37115

38116
static int unresolved_directory(const char *base, struct name_entry n[3])
@@ -71,16 +149,40 @@ static int unresolved_directory(const char *base, struct name_entry n[3])
71149
return 1;
72150
}
73151

152+
153+
static struct merge_list *link_entry(unsigned stage, const char *base, struct name_entry *n, struct merge_list *entry)
154+
{
155+
const char *path;
156+
struct merge_list *link;
157+
158+
if (!n->mode)
159+
return entry;
160+
if (entry)
161+
path = entry->path;
162+
else
163+
path = strdup(mkpath("%s%s", base, n->path));
164+
link = create_entry(stage, n->mode, n->sha1, path);
165+
link->link = entry;
166+
return link;
167+
}
168+
74169
static void unresolved(const char *base, struct name_entry n[3])
75170
{
171+
struct merge_list *entry = NULL;
172+
76173
if (unresolved_directory(base, n))
77174
return;
78-
if (n[0].sha1)
79-
printf("1 %06o %s %s%s\n", n[0].mode, sha1_to_hex(n[0].sha1), base, n[0].path);
80-
if (n[1].sha1)
81-
printf("2 %06o %s %s%s\n", n[1].mode, sha1_to_hex(n[1].sha1), base, n[1].path);
82-
if (n[2].sha1)
83-
printf("3 %06o %s %s%s\n", n[2].mode, sha1_to_hex(n[2].sha1), base, n[2].path);
175+
176+
/*
177+
* Do them in reverse order so that the resulting link
178+
* list has the stages in order - link_entry adds new
179+
* links at the front.
180+
*/
181+
entry = link_entry(3, base, n + 2, entry);
182+
entry = link_entry(2, base, n + 1, entry);
183+
entry = link_entry(1, base, n + 0, entry);
184+
185+
add_merge_entry(entry);
84186
}
85187

86188
/*
@@ -172,5 +274,7 @@ int main(int argc, char **argv)
172274
free(buf1);
173275
free(buf2);
174276
free(buf3);
277+
278+
show_result();
175279
return 0;
176280
}

0 commit comments

Comments
 (0)