summaryrefslogtreecommitdiff
path: root/merge-tree.c
blob: ef9a54d4106771b82b13eabe3203dc59e72e1c9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include "cache.h"
 
static int line_termination = '\n';
 
struct tree_entry {
	unsigned mode;
	unsigned char *sha1;
	char *path;
	struct tree_entry *next;
};
 
static struct tree_entry *read_tree(unsigned char *sha1)
{
	char type[20];
	unsigned long size;
	void *buf = read_sha1_file(sha1, type, &size);
	struct tree_entry *ret = NULL, **tp = &ret;
 
	if (!buf || strcmp(type, "tree"))
		die("unable to read 'tree' object %s", sha1_to_hex(sha1));
	while (size) {
		int len = strlen(buf)+1;
		struct tree_entry * entry = malloc(sizeof(struct tree_entry));
		if (size < len + 20 || sscanf(buf, "%o", &entry->mode) != 1)
			die("corrupt 'tree' object %s", sha1_to_hex(sha1));
		entry->path = strchr(buf, ' ')+1;
		entry->sha1 = buf + len;
		entry->next = NULL;
		*tp = entry;
		tp = &entry->next;
		len += 20;
		buf += len;
		size -= len;
	}
	return ret;
}
 
static void show(const struct tree_entry *a, const char *path)
{
	printf("select %o %s %s%c", a->mode, sha1_to_hex(a->sha1), path,
	       line_termination);
}
 
static void merge(const struct tree_entry *a, const struct tree_entry *b, const struct tree_entry *c, const char *path)
{
	char hex_a[60], hex_b[60], hex_c[60];
	strcpy(hex_a, sha1_to_hex(a->sha1));
	strcpy(hex_b, sha1_to_hex(b->sha1));
	strcpy(hex_c, sha1_to_hex(c->sha1));
	printf("merge %o->%o,%o %s->%s,%s %s%c",
		a->mode, b->mode, c->mode,
		hex_a, hex_b, hex_c, path, line_termination);
}
 
static int same(const struct tree_entry *a, const struct tree_entry *b)
{
	return a->mode == b->mode && !memcmp(a->sha1, b->sha1, 20);
}
 
static void merge_entry(const struct tree_entry *src, const struct tree_entry *dst1, const struct tree_entry *dst2)
{
	static unsigned char nullsha1[20];
	static const struct tree_entry none = { 0, nullsha1, "", NULL };
	const char *path = NULL;
	const struct tree_entry *a, *b, *c;
 
	a = &none;
	b = &none;
	c = &none;
	if (src) { a = src; path = src->path; }
	if (dst1) { b = dst1; path = dst1->path; }
	if (dst2) { c = dst2; path = dst2->path; }
	if (same(b, c)) {
		show(b, path);
		return;
	}
	if (same(a, b)) {
		show(c, path);
		return;
	}
	if (same(a, c)) {
		show(b, path);
		return;
	}
	merge(a, b, c, path);
}
 
/* For two entries, select the smaller one, clear the bigger one */
static void smaller(struct tree_entry **ap, struct tree_entry **bp)
{
	struct tree_entry *a = *ap, *b = *bp;
	if (a && b) {
		int cmp = cache_name_compare(a->path, strlen(a->path), b->path, strlen(b->path));
		if (cmp) {
			if (cmp < 0)
				*bp = NULL;
			else
				*ap = NULL;
		}
	}
}
 
static void merge_tree(struct tree_entry *src, struct tree_entry *dst1, struct tree_entry *dst2)
{
	while (src || dst1 || dst2) {
		struct tree_entry *a, *b, *c;
		a = src;
		b = dst1;
		c = dst2;
		smaller(&a,&b);
		smaller(&a,&c);
		smaller(&b,&c);
		if (a) src = a->next;
		if (b) dst1 = b->next;
		if (c) dst2 = c->next;
		merge_entry(a,b,c);
	}
}
 
static const char *merge_tree_usage =
    "merge-tree [-z] <src> <dst1> <dst2>";
 
int main(int argc, char **argv)
{
	unsigned char src[20], dst1[20], dst2[20];
 
	while ((1 < argc) && argv[1][0] == '-') {
		switch (argv[1][1]) {
		case 'z':
			line_termination = 0;
			break;
		default:
			usage(merge_tree_usage);
		}
		argc--; argv++;
	}
 
	if (argc != 4 ||
	    get_sha1_hex(argv[1], src) ||
	    get_sha1_hex(argv[2], dst1) ||
	    get_sha1_hex(argv[3], dst2))
		usage(merge_tree_usage);
	merge_tree(read_tree(src), read_tree(dst1), read_tree(dst2));
	return 0;
}