[PATCH] Teach read-tree about commit objects
[git.git] / read-tree.c
1 /*
2  * GIT - The information manager from hell
3  *
4  * Copyright (C) Linus Torvalds, 2005
5  */
6 #include "cache.h"
7
8 static int stage = 0;
9
10 static int read_one_entry(unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode)
11 {
12         int len = strlen(pathname);
13         unsigned int size = cache_entry_size(baselen + len);
14         struct cache_entry *ce = malloc(size);
15
16         memset(ce, 0, size);
17
18         ce->ce_mode = create_ce_mode(mode);
19         ce->ce_flags = create_ce_flags(baselen + len, stage);
20         memcpy(ce->name, base, baselen);
21         memcpy(ce->name + baselen, pathname, len+1);
22         memcpy(ce->sha1, sha1, 20);
23         return add_cache_entry(ce, 1);
24 }
25
26 static int read_tree_recursive(void *buffer, const char *type,
27                                unsigned long size,
28                                const char *base, int baselen)
29 {
30         if (!buffer || strcmp(type, "tree"))
31                 return -1;
32         while (size) {
33                 int len = strlen(buffer)+1;
34                 unsigned char *sha1 = buffer + len;
35                 char *path = strchr(buffer, ' ')+1;
36                 unsigned int mode;
37
38                 if (size < len + 20 || sscanf(buffer, "%o", &mode) != 1)
39                         return -1;
40
41                 buffer = sha1 + 20;
42                 size -= len + 20;
43
44                 if (S_ISDIR(mode)) {
45                         int retval;
46                         int pathlen = strlen(path);
47                         char *newbase = malloc(baselen + 1 + pathlen);
48                         void *eltbuf;
49                         char elttype[20];
50                         unsigned long eltsize;
51
52                         eltbuf = read_sha1_file(sha1, elttype, &eltsize);
53                         if (!eltbuf)
54                                 return -1;
55                         memcpy(newbase, base, baselen);
56                         memcpy(newbase + baselen, path, pathlen);
57                         newbase[baselen + pathlen] = '/';
58                         retval = read_tree_recursive(eltbuf, elttype, eltsize,
59                                                      newbase,
60                                                      baselen + pathlen + 1);
61                         free(eltbuf);
62                         free(newbase);
63                         if (retval)
64                                 return -1;
65                         continue;
66                 }
67                 if (read_one_entry(sha1, base, baselen, path, mode) < 0)
68                         return -1;
69         }
70         return 0;
71 }
72
73 static int read_tree(unsigned char *sha1, const char *base, int baselen)
74 {
75         void *buffer;
76         unsigned long size;
77
78         buffer = read_tree_with_tree_or_commit_sha1(sha1, &size, 0);
79         return read_tree_recursive(buffer, "tree", size, base, baselen);
80 }
81
82 static int remove_lock = 0;
83
84 static void remove_lock_file(void)
85 {
86         if (remove_lock)
87                 unlink(".git/index.lock");
88 }
89
90 static int path_matches(struct cache_entry *a, struct cache_entry *b)
91 {
92         int len = ce_namelen(a);
93         return ce_namelen(b) == len &&
94                 !memcmp(a->name, b->name, len);
95 }
96
97 static int same(struct cache_entry *a, struct cache_entry *b)
98 {
99         return a->ce_mode == b->ce_mode && 
100                 !memcmp(a->sha1, b->sha1, 20);
101 }
102
103
104 /*
105  * This removes all trivial merges that don't change the tree
106  * and collapses them to state 0.
107  *
108  * _Any_ other merge is left to user policy.  That includes "both
109  * created the same file", and "both removed the same file" - which are
110  * trivial, but the user might still want to _note_ it. 
111  */
112 static struct cache_entry *merge_entries(struct cache_entry *a,
113                                          struct cache_entry *b,
114                                          struct cache_entry *c)
115 {
116         int len = ce_namelen(a);
117
118         /*
119          * Are they all the same filename? We won't do
120          * any name merging
121          */
122         if (ce_namelen(b) != len ||
123             ce_namelen(c) != len ||
124             memcmp(a->name, b->name, len) ||
125             memcmp(a->name, c->name, len))
126                 return NULL;
127
128         /*
129          * Ok, all three entries describe the same
130          * filename, but maybe the contents or file
131          * mode have changed?
132          *
133          * The trivial cases end up being the ones where two
134          * out of three files are the same:
135          *  - both destinations the same, trivially take either
136          *  - one of the destination versions hasn't changed,
137          *    take the other.
138          *
139          * The "all entries exactly the same" case falls out as
140          * a special case of any of the "two same" cases.
141          *
142          * Here "a" is "original", and "b" and "c" are the two
143          * trees we are merging.
144          */
145         if (same(b,c))
146                 return c;
147         if (same(a,b))
148                 return c;
149         if (same(a,c))
150                 return b;
151         return NULL;
152 }
153
154 static void trivially_merge_cache(struct cache_entry **src, int nr)
155 {
156         static struct cache_entry null_entry;
157         struct cache_entry **dst = src;
158         struct cache_entry *old = &null_entry;
159
160         while (nr) {
161                 struct cache_entry *ce, *result;
162
163                 ce = src[0];
164
165                 /* We throw away original cache entries except for the stat information */
166                 if (!ce_stage(ce)) {
167                         old = ce;
168                         src++;
169                         nr--;
170                         active_nr--;
171                         continue;
172                 }
173                 if (nr > 2 && (result = merge_entries(ce, src[1], src[2])) != NULL) {
174                         /*
175                          * See if we can re-use the old CE directly?
176                          * That way we get the uptodate stat info.
177                          */
178                         if (path_matches(result, old) && same(result, old))
179                                 *result = *old;
180                         ce = result;
181                         ce->ce_flags &= ~htons(CE_STAGEMASK);
182                         src += 2;
183                         nr -= 2;
184                         active_nr -= 2;
185                 }
186                 *dst++ = ce;
187                 src++;
188                 nr--;
189         }
190 }
191
192 static void merge_stat_info(struct cache_entry **src, int nr)
193 {
194         static struct cache_entry null_entry;
195         struct cache_entry **dst = src;
196         struct cache_entry *old = &null_entry;
197
198         while (nr) {
199                 struct cache_entry *ce;
200
201                 ce = src[0];
202
203                 /* We throw away original cache entries except for the stat information */
204                 if (!ce_stage(ce)) {
205                         old = ce;
206                         src++;
207                         nr--;
208                         active_nr--;
209                         continue;
210                 }
211                 if (path_matches(ce, old) && same(ce, old))
212                         *ce = *old;
213                 ce->ce_flags &= ~htons(CE_STAGEMASK);
214                 *dst++ = ce;
215                 src++;
216                 nr--;
217         }
218 }
219
220 int main(int argc, char **argv)
221 {
222         int i, newfd, merge;
223         unsigned char sha1[20];
224
225         newfd = open(".git/index.lock", O_RDWR | O_CREAT | O_EXCL, 0600);
226         if (newfd < 0)
227                 die("unable to create new cachefile");
228         atexit(remove_lock_file);
229         remove_lock = 1;
230
231         merge = 0;
232         for (i = 1; i < argc; i++) {
233                 const char *arg = argv[i];
234
235                 /* "-m" stands for "merge", meaning we start in stage 1 */
236                 if (!strcmp(arg, "-m")) {
237                         int i;
238                         if (stage)
239                                 usage("-m needs to come first");
240                         read_cache();
241                         for (i = 0; i < active_nr; i++) {
242                                 if (ce_stage(active_cache[i]))
243                                         usage("you need to resolve your current index first");
244                         }
245                         stage = 1;
246                         merge = 1;
247                         continue;
248                 }
249                 if (get_sha1_hex(arg, sha1) < 0)
250                         usage("read-tree [-m] <sha1>");
251                 if (stage > 3)
252                         usage("can't merge more than two trees");
253                 if (read_tree(sha1, "", 0) < 0)
254                         die("failed to unpack tree object %s", arg);
255                 stage++;
256         }
257         if (merge) {
258                 switch (stage) {
259                 case 4: /* Three-way merge */
260                         trivially_merge_cache(active_cache, active_nr);
261                         break;
262                 case 2: /* Just read a tree, merge with old cache contents */
263                         merge_stat_info(active_cache, active_nr);
264                         break;
265                 default:
266                         die("just how do you expect me to merge %d trees?", stage-1);
267                 }
268         }
269         if (write_cache(newfd, active_cache, active_nr) ||
270             rename(".git/index.lock", ".git/index"))
271                 die("unable to write new index file");
272         remove_lock = 0;
273         return 0;
274 }