X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=read-tree.c;h=fbd0da0ebdc5c523b7f35399a39a3b8efd373112;hb=b0bf8f24e9c7bdeb7e472e6b1fa5f51de1cdcc97;hp=4acbb6b3f25cd5cb50dda3ac34b1b37edad639b1;hpb=d723c690637f2918d9861e8d602e592c5fa0bc5b;p=git.git diff --git a/read-tree.c b/read-tree.c index 4acbb6b3..fbd0da0e 100644 --- a/read-tree.c +++ b/read-tree.c @@ -155,28 +155,51 @@ static int threeway_merge(struct cache_entry *stages[4], struct cache_entry **ds /* * Two-way merge. * - * The rule is: - * - every current entry has to match the old tree - * - if the current entry matches the new tree, we leave it - * as-is. Otherwise we require that it be up-to-date. + * The rule is to "carry forward" what is in the index without losing + * information across a "fast forward", favoring a successful merge + * over a merge failure when it makes sense. For details of the + * "carry forward" rule, please see . + * */ static int twoway_merge(struct cache_entry **src, struct cache_entry **dst) { - struct cache_entry *old = src[0]; - struct cache_entry *a = src[1], *b = src[2]; + struct cache_entry *current = src[0]; + struct cache_entry *oldtree = src[1], *newtree = src[2]; if (src[3]) return -1; - if (old) { - if (!a || !same(old, a)) + if (current) { + if ((!oldtree && !newtree) || /* 4 and 5 */ + (!oldtree && newtree && + same(current, newtree)) || /* 6 and 7 */ + (oldtree && newtree && + same(oldtree, newtree)) || /* 14 and 15 */ + (oldtree && newtree && + !same(oldtree, newtree) && /* 18 and 19*/ + same(current, newtree))) { + *dst++ = current; + return 1; + } + else if (oldtree && !newtree && same(current, oldtree)) { + /* 10 or 11 */ + verify_uptodate(current); + return 0; + } + else if (oldtree && newtree && + same(current, oldtree) && !same(current, newtree)) { + /* 20 or 21 */ + verify_uptodate(current); + return merged_entry(newtree, NULL, dst); + } + else + /* all other failures */ return -1; } - if (b) - return merged_entry(b, old, dst); - if (old) - verify_uptodate(old); - return 0; + else if (newtree) + return merged_entry(newtree, NULL, dst); + else + return 0; } /* @@ -195,11 +218,11 @@ static int oneway_merge(struct cache_entry **src, struct cache_entry **dst) if (!a) return 0; - if (old && same(old, a)) - *a = *old; - a->ce_flags &= ~htons(CE_STAGEMASK); - *dst++ = a; - return 1; + if (old && same(old, a)) { + *dst++ = old; + return 1; + } + return merged_entry(a, NULL, dst); } static void check_updates(struct cache_entry **src, int nr) @@ -221,7 +244,9 @@ static void check_updates(struct cache_entry **src, int nr) } } -static void merge_cache(struct cache_entry **src, int nr, int (*fn)(struct cache_entry **, struct cache_entry **)) +typedef int (*merge_fn_t)(struct cache_entry **, struct cache_entry **); + +static void merge_cache(struct cache_entry **src, int nr, merge_fn_t fn) { struct cache_entry **dst = src; @@ -250,7 +275,7 @@ static void merge_cache(struct cache_entry **src, int nr, int (*fn)(struct cache check_updates(active_cache, active_nr); } -static char *read_tree_usage = "git-read-tree ( | -m [ []])"; +static char *read_tree_usage = "git-read-tree ( | -m [-u] [ []])"; static struct cache_file cache_file; @@ -295,21 +320,17 @@ int main(int argc, char **argv) die("failed to unpack tree object %s", arg); stage++; } + if (update && !merge) + usage(read_tree_usage); if (merge) { - switch (stage) { - case 4: /* Three-way merge */ - merge_cache(active_cache, active_nr, threeway_merge); - break; - case 3: /* Update from one tree to another */ - merge_cache(active_cache, active_nr, twoway_merge); - check_updates(active_cache, active_nr); - break; - case 2: /* Just read a tree, merge with old cache contents */ - merge_cache(active_cache, active_nr, oneway_merge); - break; - default: + static const merge_fn_t merge_function[] = { + [1] = oneway_merge, + [2] = twoway_merge, + [3] = threeway_merge, + }; + if (stage < 2 || stage > 4) die("just how do you expect me to merge %d trees?", stage-1); - } + merge_cache(active_cache, active_nr, merge_function[stage-1]); } if (write_cache(newfd, active_cache, active_nr) || commit_index_file(&cache_file))