From 5b982f84ee6eb3027e5bdf2e917a0efa48aded6a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 9 Aug 2006 15:04:16 +0200 Subject: [PATCH 1/5] merge-recur: do not call git-write-tree Since merge-recur is in C, and uses libgit, it can call the relevant functions directly, without writing the index file. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index f5c0080a51..b8b095179b 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -248,38 +248,34 @@ static int git_merge_trees(int index_only, return rc; } -/* - * TODO: this can be streamlined by refactoring builtin-write-tree.c - */ static struct tree *git_write_tree(void) { - FILE *fp; - int rc; - char buf[41]; - unsigned char sha1[20]; - int ch; - unsigned i = 0; + struct tree *result = NULL; + if (cache_dirty) { + unsigned i; for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; if (ce_stage(ce)) return NULL; } - flush_cache(); - } - fp = popen("git-write-tree 2>/dev/null", "r"); - while ((ch = fgetc(fp)) != EOF) - if (i < sizeof(buf)-1 && ch >= '0' && ch <= 'f') - buf[i++] = ch; - else - break; - rc = pclose(fp); - if (rc == -1 || WEXITSTATUS(rc)) - return NULL; - buf[i] = '\0'; - if (get_sha1(buf, sha1) != 0) - return NULL; - return lookup_tree(sha1); + } else + read_cache_from(getenv("GIT_INDEX_FILE")); + + if (!active_cache_tree) + active_cache_tree = cache_tree(); + + if (!cache_tree_fully_valid(active_cache_tree) && + cache_tree_update(active_cache_tree, + active_cache, active_nr, 0, 0) < 0) + die("error building trees"); + + result = lookup_tree(active_cache_tree->sha1); + + flush_cache(); + cache_dirty = 0; + + return result; } static int save_files_dirs(const unsigned char *sha1, From c1964a006f9035cbdc6de8e55768fc6ad00d4825 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 9 Aug 2006 15:07:31 +0200 Subject: [PATCH 2/5] merge-recur: do not setenv("GIT_INDEX_FILE") Since there are no external calls left in merge-recur, we do not need to set the environment variable GIT_INDEX_FILE all the time. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index b8b095179b..7a93dd9208 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -116,6 +116,7 @@ static void output_commit_title(struct commit *commit) } } +static const char *current_index_file = NULL; static const char *original_index_file; static const char *temporary_index_file; static int cache_dirty = 0; @@ -124,12 +125,12 @@ static int flush_cache(void) { /* flush temporary index */ struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); - int fd = hold_lock_file_for_update(lock, getenv("GIT_INDEX_FILE")); + int fd = hold_lock_file_for_update(lock, current_index_file); if (fd < 0) die("could not lock %s", lock->filename); if (write_cache(fd, active_cache, active_nr) || close(fd) || commit_lock_file(lock)) - die ("unable to write %s", getenv("GIT_INDEX_FILE")); + die ("unable to write %s", current_index_file); discard_cache(); cache_dirty = 0; return 0; @@ -137,11 +138,10 @@ static int flush_cache(void) static void setup_index(int temp) { - const char *idx = temp ? temporary_index_file: original_index_file; + current_index_file = temp ? temporary_index_file: original_index_file; if (cache_dirty) die("fatal: cache changed flush_cache();"); unlink(temporary_index_file); - setenv("GIT_INDEX_FILE", idx, 1); discard_cache(); } @@ -174,7 +174,7 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, { struct cache_entry *ce; if (!cache_dirty) - read_cache_from(getenv("GIT_INDEX_FILE")); + read_cache_from(current_index_file); cache_dirty++; ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh); if (!ce) @@ -223,7 +223,7 @@ static int git_merge_trees(int index_only, struct unpack_trees_options opts; if (!cache_dirty) { - read_cache_from(getenv("GIT_INDEX_FILE")); + read_cache_from(current_index_file); cache_dirty = 1; } @@ -260,7 +260,7 @@ static struct tree *git_write_tree(void) return NULL; } } else - read_cache_from(getenv("GIT_INDEX_FILE")); + read_cache_from(current_index_file); if (!active_cache_tree) active_cache_tree = cache_tree(); @@ -338,7 +338,7 @@ static struct path_list *get_unmerged(void) unmerged->strdup_paths = 1; if (!cache_dirty) { - read_cache_from(getenv("GIT_INDEX_FILE")); + read_cache_from(current_index_file); cache_dirty++; } for (i = 0; i < active_nr; i++) { @@ -468,10 +468,6 @@ static int remove_path(const char *name) return ret; } -/* - * TODO: once we no longer call external programs, we'd probably be better off - * not setting / getting the environment variable GIT_INDEX_FILE all the time. - */ int remove_file(int clean, const char *path) { int update_cache = index_only || clean; @@ -479,7 +475,7 @@ int remove_file(int clean, const char *path) if (update_cache) { if (!cache_dirty) - read_cache_from(getenv("GIT_INDEX_FILE")); + read_cache_from(current_index_file); cache_dirty++; if (remove_file_from_cache(path)) return -1; From 934d9a24078e65111e9946ad3449c3fa9c06475e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 9 Aug 2006 18:43:03 +0200 Subject: [PATCH 3/5] merge-recur: if there is no common ancestor, fake empty one This fixes the coolest merge ever. [jc: with two "Oops that's not it" fixes from Johannes and Alex, and an obvious type mismatch fix.] Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/merge-recursive.c b/merge-recursive.c index 7a93dd9208..d4de1adfe2 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1223,6 +1223,18 @@ int merge(struct commit *h1, output_commit_title(iter->item); merged_common_ancestors = pop_commit(&ca); + if (merged_common_ancestors == NULL) { + /* if there is no common ancestor, make an empty tree */ + struct tree *tree = xcalloc(1, sizeof(struct tree)); + unsigned char hdr[40]; + int hdrlen; + + tree->object.parsed = 1; + tree->object.type = OBJ_TREE; + write_sha1_file_prepare(NULL, 0, tree_type, tree->object.sha1, + hdr, &hdrlen); + merged_common_ancestors = make_virtual_commit(tree, "ancestor"); + } for (iter = ca; iter; iter = iter->next) { output_indent = call_depth + 1; From 8918b0c9c2667c5a69461955135c709b09561f72 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 9 Aug 2006 22:30:58 +0200 Subject: [PATCH 4/5] merge-recur: try to merge older merge bases first It seems to be the only sane way to do it: when a two-head merge is done, and the merge-base and one of the two branches agree, the merge assumes that the other branch has something new. If we start creating virtual commits from newer merge-bases, and go back to older merge-bases, and then merge with newer commits again, chances are that a patch is lost, _because_ the merge-base and the head agree on it. Unlikely, yes, but it happened to me. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/merge-recursive.c b/merge-recursive.c index d4de1adfe2..9281cd183a 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1191,6 +1191,17 @@ static int merge_trees(struct tree *head, return clean; } +static struct commit_list *reverse_commit_list(struct commit_list *list) +{ + struct commit_list *next = NULL, *current, *backup; + for (current = list; current; current = backup) { + backup = current->next; + current->next = next; + next = current; + } + return next; +} + /* * Merge the commits h1 and h2, return the resulting virtual * commit object and a flag indicating the cleaness of the merge. @@ -1216,7 +1227,7 @@ int merge(struct commit *h1, if (ancestor) commit_list_insert(ancestor, &ca); else - ca = get_merge_bases(h1, h2, 1); + ca = reverse_commit_list(get_merge_bases(h1, h2, 1)); output("found %u common ancestor(s):", commit_list_count(ca)); for (iter = ca; iter; iter = iter->next) From 984b65707e25c426a32feb9b9d46f077b605cb31 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 9 Aug 2006 22:31:49 +0200 Subject: [PATCH 5/5] merge-recur: do not die unnecessarily When the cache is dirty, and we switch the index file from temporary to final, we want to discard the cache without complaint. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index 9281cd183a..454e293578 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -139,8 +139,10 @@ static int flush_cache(void) static void setup_index(int temp) { current_index_file = temp ? temporary_index_file: original_index_file; - if (cache_dirty) - die("fatal: cache changed flush_cache();"); + if (cache_dirty) { + discard_cache(); + cache_dirty = 0; + } unlink(temporary_index_file); discard_cache(); }