Instead of a fork, we can use the plumbing ;-) Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> --- graph.c | 26 ----- merge-recursive.c | 270 +++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 191 insertions(+), 105 deletions(-) diff --git a/graph.c b/graph.c index fa2bfee..b784ea2 100644 --- a/graph.c +++ b/graph.c @@ -5,32 +5,6 @@ #include "cache.h" #include "commit.h" #include "graph.h" -// does not belong here -struct tree *git_write_tree() -{ -#if 0 - fprintf(stderr, "GIT_INDEX_FILE='%s' git-write-tree\n", - getenv("GIT_INDEX_FILE")); -#endif - FILE *fp = popen("git-write-tree 2>/dev/null", "r"); - char buf[41]; - unsigned char sha1[20]; - int ch; - unsigned i = 0; - while ( (ch = fgetc(fp)) != EOF ) - if ( i < sizeof(buf)-1 && ch >= '0' && ch <= 'f' ) - buf[i++] = ch; - else - break; - int 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); -} - const char *node_title(struct node *node, int *len) { const char *s = "(null commit)"; diff --git a/merge-recursive.c b/merge-recursive.c index 9bbb426..6d4e797 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -9,6 +9,7 @@ #include <sys/wait.h> #include <sys/types.h> #include <sys/stat.h> #include "cache.h" +#include "cache-tree.h" #include "commit.h" #include "blob.h" #include "tree-walk.h" @@ -19,6 +20,47 @@ #include "run-command.h" #include "graph.h" #include "path-list.h" +#ifdef DEBUG +#include "quote.h" +static void show_ce_entry(const char *tag, struct cache_entry *ce) +{ + if (tag && *tag && + (ce->ce_flags & htons(CE_VALID))) { + static char alttag[4]; + memcpy(alttag, tag, 3); + if (isalpha(tag[0])) + alttag[0] = tolower(tag[0]); + else if (tag[0] == '?') + alttag[0] = '!'; + else { + alttag[0] = 'v'; + alttag[1] = tag[0]; + alttag[2] = ' '; + alttag[3] = 0; + } + tag = alttag; + } + + fprintf(stderr,"%s%06o %s %d\t", + tag, + ntohl(ce->ce_mode), + sha1_to_hex(ce->sha1), + ce_stage(ce)); + write_name_quoted("", 0, ce->name, + '\n', stderr); + fputc('\n', stderr); +} + +static void ls_files() { + int i; + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + show_ce_entry("", ce); + } + fprintf(stderr, "---\n"); +} +#endif + #define for_each_commit(p,list) for ( p = (list); p; p = p->next ) struct merge_result @@ -94,12 +136,68 @@ static void output(const char *fmt, ...) static const char *original_index_file; static const char *temporary_index_file; +static int cache_dirty = 0; + +static int flush_cache() +{ + /* 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")); + if (fd < 0) + die("could not lock %s", temporary_index_file); + if (write_cache(fd, active_cache, active_nr) || + commit_lock_file(lock)) + die ("unable to write %s", temporary_index_file); + discard_cache(); + cache_dirty = 0; + return 0; +} static void setup_index(int temp) { const char *idx = 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(); +} + +static struct cache_entry *make_cache_entry(unsigned int mode, + const unsigned char *sha1, const char *path, int stage, int refresh) +{ + int size, len; + struct cache_entry *ce; + + if (!verify_path(path)) + return NULL; + + len = strlen(path); + size = cache_entry_size(len); + ce = xcalloc(1, size); + + memcpy(ce->sha1, sha1, 20); + memcpy(ce->name, path, len); + ce->ce_flags = create_ce_flags(len, stage); + ce->ce_mode = create_ce_mode(mode); + + if (refresh) + return refresh_cache_entry(ce, 0); + + return ce; +} + +static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, + const char *path, int stage, int refresh, int options) +{ + struct cache_entry *ce; + if (!cache_dirty) + read_cache_from(getenv("GIT_INDEX_FILE")); + cache_dirty++; + ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh); + if (!ce) + return error("cache_addinfo failed: %s", strerror(cache_errno)); + return add_cache_entry(ce, options); } // This is a global variable which is used in a number of places but @@ -119,6 +217,8 @@ #if 0 sha1_to_hex(tree->object.sha1)); #endif const char *argv[] = { "git-read-tree", NULL, NULL, }; + if (cache_dirty) + die("read-tree with dirty cache"); argv[1] = sha1_to_hex(tree->object.sha1); int rc = run_command_v(2, argv); return rc < 0 ? -1: rc; @@ -141,6 +241,8 @@ #endif "git-read-tree", NULL, "-m", NULL, NULL, NULL, NULL, }; + if (cache_dirty) + flush_cache(); argv[1] = update_arg; argv[3] = sha1_to_hex(common->object.sha1); argv[4] = sha1_to_hex(head->object.sha1); @@ -149,6 +251,33 @@ #endif return rc < 0 ? -1: rc; } +struct tree *git_write_tree() +{ +#if 0 + fprintf(stderr, "GIT_INDEX_FILE='%s' git-write-tree\n", + getenv("GIT_INDEX_FILE")); +#endif + if (cache_dirty) + flush_cache(); + FILE *fp = popen("git-write-tree 2>/dev/null", "r"); + char buf[41]; + unsigned char sha1[20]; + int ch; + unsigned i = 0; + while ( (ch = fgetc(fp)) != EOF ) + if ( i < sizeof(buf)-1 && ch >= '0' && ch <= 'f' ) + buf[i++] = ch; + else + break; + int 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); +} + struct merge_tree_result merge_trees(struct tree *head, struct tree *merge, struct tree *common, @@ -640,36 +769,25 @@ struct rename_entry *getRenames(struct t return renames; } -static FILE *git_update_index_pipe() -{ - return popen("git-update-index -z --index-info", "w"); -} - -int setIndexStages(FILE *fp, - const char *path, +int setIndexStages(const char *path, unsigned char *osha, unsigned omode, unsigned char *asha, unsigned amode, unsigned char *bsha, unsigned bmode, int clear /* =True */) { - if ( !fp ) - return -1; - if ( clear ) { - fprintf(fp, "0 %s\t%s", sha1_to_hex(null_sha1), path); - fputc('\0', fp); - } - if ( omode ) { - fprintf(fp, "0%o %s 1\t%s", omode, sha1_to_hex(osha), path); - fputc('\0', fp); - } - if ( amode ) { - fprintf(fp, "0%o %s 2\t%s", amode, sha1_to_hex(asha), path); - fputc('\0', fp); - } - if ( bmode ) { - fprintf(fp, "0%o %s 3\t%s", bmode, sha1_to_hex(bsha), path); - fputc('\0', fp); - } + int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE; + if ( clear ) + if (add_cacheinfo(0, null_sha1, path, 0, 0, options)) + return -1; + if ( omode ) + if (add_cacheinfo(omode, osha, path, 1, 0, options)) + return -1; + if ( amode ) + if (add_cacheinfo(omode, osha, path, 2, 0, options)) + return -1; + if ( bmode ) + if (add_cacheinfo(omode, osha, path, 3, 0, options)) + return -1; return 0; } @@ -695,17 +813,17 @@ static int remove_path(const char *name) return ret; } -int removeFile(FILE *fp, int clean, const char *path) +int removeFile(int clean, const char *path) { int updateCache = index_only || clean; int updateWd = !index_only; if ( updateCache ) { - if ( !fp ) + if (!cache_dirty) + read_cache_from(getenv("GIT_INDEX_FILE")); + cache_dirty++; + if (remove_file_from_cache(path)) return -1; - fprintf(fp, "0 %s\t%s", sha1_to_hex(null_sha1), path); - fputc('\0', fp); - return 0; } if ( updateWd ) { @@ -785,8 +903,7 @@ static void flush_buffer(int fd, const c } } -void updateFileExt(FILE *fp, - const unsigned char *sha, +void updateFileExt(const unsigned char *sha, unsigned mode, const char *path, int updateCache, @@ -830,20 +947,15 @@ void updateFileExt(FILE *fp, mode, sha1_to_hex(sha), path); } if ( updateCache ) - { - // XXX just always use "git update-index --index-info"? - fprintf(fp, "%06o %s\t%s", mode, sha1_to_hex(sha), path); - fputc('\0', fp); - } + add_cacheinfo(mode, sha, path, 0, updateWd, ADD_CACHE_OK_TO_ADD); } -void updateFile(FILE *fp, - int clean, +void updateFile(int clean, const unsigned char *sha, unsigned mode, const char *path) { - updateFileExt(fp, sha, mode, path, index_only || clean, !index_only); + updateFileExt(sha, mode, path, index_only || clean, !index_only); } // Low level file merging, update and removal @@ -1004,7 +1116,6 @@ int processRenames(struct rename_entry * for (sre = renamesB; sre; sre = sre->next) path_list_insert(sre->src, &srcNames); - FILE *fp = git_update_index_pipe(); for_each_path(src,&srcNames) { struct rename_entry *renames1, *renames2, *ren1, *ren2; const char *branchName1, *branchName2; @@ -1050,26 +1161,26 @@ int processRenames(struct rename_entry * dstName1 = uniquePath(ren1->dst, branchName1); output("%s is a directory in %s adding as %s instead", ren1->dst, branchName2, dstName1); - removeFile(fp, 0, ren1->dst); + removeFile(0, ren1->dst); } if ( path_list_has_path(¤tDirectorySet, ren2->dst) ) { dstName2 = uniquePath(ren2->dst, branchName2); output("%s is a directory in %s adding as %s instead", ren2->dst, branchName1, dstName2); - removeFile(fp, 0, ren2->dst); + removeFile(0, ren2->dst); } - setIndexStages(fp, dstName1, + setIndexStages(dstName1, NULL, 0, ren1->dst_sha, ren1->dst_mode, NULL, 0, 1 /* clear */); - setIndexStages(fp, dstName2, + setIndexStages(dstName2, NULL, 0, NULL, 0, ren2->dst_sha, ren2->dst_mode, 1 /* clear */); } else { - removeFile(fp, 1, ren1->src); + removeFile(1, ren1->src); struct merge_file_info mfi; mfi = mergeFile(ren1->src, ren1->src_sha, ren1->src_mode, ren1->dst, ren1->dst_sha, ren1->dst_mode, @@ -1087,18 +1198,17 @@ int processRenames(struct rename_entry * cleanMerge = 0; if ( !index_only ) - setIndexStages(fp, - ren1->dst, + setIndexStages(ren1->dst, ren1->src_sha, ren1->src_mode, ren1->dst_sha, ren1->dst_mode, ren2->dst_sha, ren2->dst_mode, 1 /* clear */); } - updateFile(fp, mfi.clean, mfi.sha, mfi.mode, ren1->dst); + updateFile(mfi.clean, mfi.sha, mfi.mode, ren1->dst); } } else { // Renamed in 1, maybe changed in 2 - removeFile(fp, 1, ren1->src); + removeFile(1, ren1->src); unsigned char srcShaOtherBranch[20], dstShaOtherBranch[20]; unsigned srcModeOtherBranch, dstModeOtherBranch; @@ -1123,15 +1233,15 @@ int processRenames(struct rename_entry * ren1->dst, branchName2); output("Renaming %s to %s instead", ren1->src, newPath); cleanMerge = 0; - removeFile(fp, 0, ren1->dst); - updateFile(fp, 0, ren1->dst_sha, ren1->dst_mode, newPath); + removeFile(0, ren1->dst); + updateFile(0, ren1->dst_sha, ren1->dst_mode, newPath); } else if ( memcmp(srcShaOtherBranch, null_sha1, 20) == 0 ) { output("CONFLICT (rename/delete): Rename %s->%s in %s " "and deleted in %s", ren1->src, ren1->dst, branchName1, branchName2); cleanMerge = 0; - updateFile(fp, 0, ren1->dst_sha, ren1->dst_mode, ren1->dst); + updateFile(0, ren1->dst_sha, ren1->dst_mode, ren1->dst); } else if ( memcmp(dstShaOtherBranch, null_sha1, 20) != 0 ) { newPath = uniquePath(ren1->dst, branchName2); output("CONFLICT (rename/add): Rename %s->%s in %s. " @@ -1139,7 +1249,7 @@ int processRenames(struct rename_entry * ren1->src, ren1->dst, branchName1, ren1->dst, branchName2); output("Adding as %s instead", newPath); - updateFile(fp, 0, dstShaOtherBranch, dstModeOtherBranch, newPath); + updateFile(0, dstShaOtherBranch, dstModeOtherBranch, newPath); cleanMerge = 0; tryMerge = 1; } else if ( (dst2 = find_rename_bydst(renames2, ren1->dst)) ) { @@ -1151,9 +1261,9 @@ int processRenames(struct rename_entry * dst2->src, dst2->dst, branchName2); output("Renaming %s to %s and %s to %s instead", ren1->src, newPath1, dst2->src, newPath2); - removeFile(fp, 0, ren1->dst); - updateFile(fp, 0, ren1->dst_sha, ren1->dst_mode, newPath1); - updateFile(fp, 0, dst2->dst_sha, dst2->dst_mode, newPath2); + removeFile(0, ren1->dst); + updateFile(0, ren1->dst_sha, ren1->dst_mode, newPath1); + updateFile(0, dst2->dst_sha, dst2->dst_mode, newPath2); dst2->processed = 1; cleanMerge = 0; } else @@ -1194,21 +1304,19 @@ int processRenames(struct rename_entry * cleanMerge = 0; if ( !index_only ) - setIndexStages(fp, - ren1->dst, + setIndexStages(ren1->dst, osha, omode, asha, amode, bsha, bmode, 1 /* clear */); } - updateFile(fp, mfi.clean, mfi.sha, mfi.mode, ren1->dst); + updateFile(mfi.clean, mfi.sha, mfi.mode, ren1->dst); } } } path_list_clear(&srcNames, 0); - if (pclose(fp)) { - die("git update-index --index-info failed"); - } + if (cache_dirty) + flush_cache(); return cleanMerge; } @@ -1241,7 +1349,6 @@ int processEntry(struct index_entry *ent unsigned oMode = entry->stages[1].mode; unsigned aMode = entry->stages[2].mode; unsigned bMode = entry->stages[3].mode; - FILE *fp = git_update_index_pipe(); if ( oSha && (!aSha || !bSha) ) { // @@ -1253,7 +1360,7 @@ int processEntry(struct index_entry *ent // Deleted in both or deleted in one and unchanged in the other if ( aSha ) output("Removing %s", path); - removeFile(fp, 1, path); + removeFile(1, path); } else { // Deleted in one and changed in the other cleanMerge = 0; @@ -1262,13 +1369,13 @@ int processEntry(struct index_entry *ent "and modified in %s. Version %s of %s left in tree.", path, branch1Name, branch2Name, branch2Name, path); - updateFile(fp, 0, bSha, bMode, path); + updateFile(0, bSha, bMode, path); } else { output("CONFLICT (delete/modify): %s deleted in %s " "and modified in %s. Version %s of %s left in tree.", path, branch2Name, branch1Name, branch1Name, path); - updateFile(fp, 0, aSha, aMode, path); + updateFile(0, aSha, aMode, path); } } @@ -1302,11 +1409,11 @@ int processEntry(struct index_entry *ent output("CONFLICT (%s): There is a directory with name %s in %s. " "Adding %s as %s", conf, path, otherBranch, path, newPath); - removeFile(fp, 0, path); - updateFile(fp, 0, sha, mode, newPath); + removeFile(0, path); + updateFile(0, sha, mode, newPath); } else { output("Adding %s", path); - updateFile(fp, 1, sha, mode, path); + updateFile(1, sha, mode, path); } } else if ( !oSha && aSha && bSha ) { // @@ -1319,7 +1426,7 @@ int processEntry(struct index_entry *ent "but permissions conflict %06o->%06o", path, aMode, bMode); output("CONFLICT: adding with permission: %06o", aMode); - updateFile(fp, 0, aSha, aMode, path); + updateFile(0, aSha, aMode, path); } else { // This case is handled by git-read-tree assert(0 && "This case must be handled by git-read-tree"); @@ -1331,9 +1438,9 @@ int processEntry(struct index_entry *ent output("CONFLICT (add/add): File %s added non-identically " "in both branches. Adding as %s and %s instead.", path, newPath1, newPath2); - removeFile(fp, 0, path); - updateFile(fp, 0, aSha, aMode, newPath1); - updateFile(fp, 0, bSha, bMode, newPath2); + removeFile(0, path); + updateFile(0, aSha, aMode, newPath1); + updateFile(0, bSha, bMode, newPath2); } } else if ( oSha && aSha && bSha ) { @@ -1348,22 +1455,23 @@ int processEntry(struct index_entry *ent branch1Name, branch2Name); if ( mfi.clean ) - updateFile(fp, 1, mfi.sha, mfi.mode, path); + updateFile(1, mfi.sha, mfi.mode, path); else { cleanMerge = 0; output("CONFLICT (content): Merge conflict in %s", path); if ( index_only ) - updateFile(fp, 0, mfi.sha, mfi.mode, path); + updateFile(0, mfi.sha, mfi.mode, path); else - updateFileExt(fp, mfi.sha, mfi.mode, path, + updateFileExt(mfi.sha, mfi.mode, path, 0 /* updateCache */, 1 /* updateWd */); } } else die("Fatal merge failure, shouldn't happen."); - if (pclose(fp)) - die("updating entry failed in git update-index"); + if (cache_dirty) + flush_cache(); + return cleanMerge; } @@ -1570,6 +1678,10 @@ int main(int argc, char *argv[]) struct graph *graph = graph_build(commits); result = merge(h1, h2, branch1, branch2, graph, 0, NULL); } + + if (cache_dirty) + flush_cache(); + return result.clean ? 0: 1; } -- 1.4.1.rc1.gb2d14 - : send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html