From 6d297f81373e19d86b8f02cb68120201d1b0ab1d Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 8 Jul 2006 18:42:41 +0200 Subject: [PATCH 01/21] Status update on merge-recursive in C This is just an update for people being interested. Alex and me were busy with that project for a few days now. While it has progressed nicely, there are quite a couple TODOs in merge-recursive.c, just search for "TODO". For impatient people: yes, it passes all the tests, and yes, according to the evil test Alex did, it is faster than the Python script. But no, it is not yet finished. Biggest points are: - there are still three external calls - in the end, it should not be necessary to write the index more than once (just before exiting) - a lot of things can be refactored to make the code easier and shorter BTW we cannot just plug in git-merge-tree yet, because git-merge-tree does not handle renames at all. This patch is meant for testing, and as such, - it compile the program to git-merge-recur - it adjusts the scripts and tests to use git-merge-recur instead of git-merge-recursive - it provides "TEST", a script to execute the tests regarding -recursive - it inlines the changes to read-cache.c (read_cache_from(), discard_cache() and refresh_cache_entry()) Brought to you by Alex Riesen and Dscho Signed-off-by: Junio C Hamano --- Makefile | 8 +- TEST | 10 + cache.h | 4 + git-merge.sh | 6 +- git-rebase.sh | 4 +- merge-recursive.c | 1560 +++++++++++++++++++++++++++++++++++++++ path-list.c | 105 +++ path-list.h | 22 + read-cache.c | 104 +-- t/t3402-rebase-merge.sh | 2 +- 10 files changed, 1773 insertions(+), 52 deletions(-) create mode 100755 TEST create mode 100644 merge-recursive.c create mode 100644 path-list.c create mode 100644 path-list.h diff --git a/Makefile b/Makefile index 01fb9cfdbd..a749aa4f0e 100644 --- a/Makefile +++ b/Makefile @@ -167,7 +167,8 @@ PROGRAMS = \ git-upload-pack$X git-verify-pack$X \ git-symbolic-ref$X \ git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \ - git-describe$X git-merge-tree$X git-blame$X git-imap-send$X + git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \ + git-merge-recur$X BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \ git-count-objects$X git-diff$X git-push$X git-mailsplit$X \ @@ -615,6 +616,11 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS) $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) +merge-recursive.o path-list.o: path-list.h +git-merge-recur$X: merge-recursive.o path-list.o $(LIB_FILE) + $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ + $(LIBS) + $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) $(DIFF_OBJS): diffcore.h diff --git a/TEST b/TEST new file mode 100755 index 0000000000..d530983474 --- /dev/null +++ b/TEST @@ -0,0 +1,10 @@ +#!/bin/sh -x +cd t || exit +./t3400-rebase.sh "$@" && \ +./t6020-merge-df.sh "$@" && \ +./t3401-rebase-partial.sh "$@" && \ +./t6021-merge-criss-cross.sh "$@" && \ +./t3402-rebase-merge.sh "$@" && \ +./t6022-merge-rename.sh "$@" && \ +./t6010-merge-base.sh "$@" && \ +: diff --git a/cache.h b/cache.h index d433d46f23..8cc0ccbd0f 100644 --- a/cache.h +++ b/cache.h @@ -115,6 +115,7 @@ static inline unsigned int create_ce_mode(unsigned int mode) extern struct cache_entry **active_cache; extern unsigned int active_nr, active_alloc, active_cache_changed; extern struct cache_tree *active_cache_tree; +extern int cache_errno; #define GIT_DIR_ENVIRONMENT "GIT_DIR" #define DEFAULT_GIT_DIR_ENVIRONMENT ".git" @@ -142,13 +143,16 @@ extern void verify_non_filename(const char *prefix, const char *name); /* Initialize and use the cache information */ extern int read_cache(void); +extern int read_cache_from(const char *path); extern int write_cache(int newfd, struct cache_entry **cache, int entries); +extern int discard_cache(void); extern int verify_path(const char *path); extern int cache_name_pos(const char *name, int namelen); #define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ #define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */ #define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */ extern int add_cache_entry(struct cache_entry *ce, int option); +extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); extern int remove_cache_entry_at(int pos); extern int remove_file_from_cache(const char *path); extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); diff --git a/git-merge.sh b/git-merge.sh index 24e3b507ef..b26ca147c0 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -9,15 +9,15 @@ USAGE='[-n] [--no-commit] [--squash] [-s ]... < LF=' ' -all_strategies='recursive octopus resolve stupid ours' -default_twohead_strategies='recursive' +all_strategies='recur recur octopus resolve stupid ours' +default_twohead_strategies='recur' default_octopus_strategies='octopus' no_trivial_merge_strategies='ours' use_strategies= index_merge=t if test "@@NO_PYTHON@@"; then - all_strategies='resolve octopus stupid ours' + all_strategies='recur resolve octopus stupid ours' default_twohead_strategies='resolve' fi diff --git a/git-rebase.sh b/git-rebase.sh index 1b9e986926..2a4c8c8a89 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -35,7 +35,7 @@ If you would prefer to skip this patch, instead run \"git rebase --skip\". To restore the original branch and stop rebasing run \"git rebase --abort\". " unset newbase -strategy=recursive +strategy=recur do_merge= dotest=$GIT_DIR/.dotest-merge prec=4 @@ -292,7 +292,7 @@ then exit $? fi -if test "@@NO_PYTHON@@" && test "$strategy" = "recursive" +if test "@@NO_PYTHON@@" && test "$strategy" = "recur" then die 'The recursive merge strategy currently relies on Python, which this installation of git was not configured with. Please consider diff --git a/merge-recursive.c b/merge-recursive.c new file mode 100644 index 0000000000..cf81768ee9 --- /dev/null +++ b/merge-recursive.c @@ -0,0 +1,1560 @@ +/* + * Recursive Merge algorithm stolen from git-merge-recursive.py by + * Fredrik Kuivinen. + * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006 + */ +#include +#include +#include +#include +#include +#include +#include +#include "cache.h" +#include "cache-tree.h" +#include "commit.h" +#include "blob.h" +#include "tree-walk.h" +#include "diff.h" +#include "diffcore.h" +#include "run-command.h" +#include "tag.h" + +#include "path-list.h" + +/*#define DEBUG*/ + +#ifdef DEBUG +#define debug(args, ...) fprintf(stderr, args, ## __VA_ARGS__) +#else +#define debug(args, ...) +#endif + +#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 + +/* + * A virtual commit has + * - (const char *)commit->util set to the name, and + * - *(int *)commit->object.sha1 set to the virtual id. + */ +static const char *commit_title(struct commit *commit, int *len) +{ + const char *s = "(null commit)"; + *len = strlen(s); + + if ( commit->util ) { + s = commit->util; + *len = strlen(s); + } else { + if ( parse_commit(commit) != 0 ) { + s = "(bad commit)"; + *len = strlen(s); + } else { + s = commit->buffer; + char prev = '\0'; + while ( *s ) { + if ( '\n' == prev && '\n' == *s ) { + ++s; + break; + } + prev = *s++; + } + *len = 0; + while ( s[*len] && '\n' != s[*len] ) + ++(*len); + } + } + return s; +} + +static const char *commit_hex_sha1(const struct commit *commit) +{ + return commit->util ? "virtual" : commit ? + sha1_to_hex(commit->object.sha1) : "undefined"; +} + +static unsigned commit_list_count(const struct commit_list *l) +{ + unsigned c = 0; + for (; l; l = l->next ) + c++; + return c; +} + +static struct commit *make_virtual_commit(struct tree *tree, const char *comment) +{ + struct commit *commit = xcalloc(1, sizeof(struct commit)); + static unsigned virtual_id = 1; + commit->tree = tree; + commit->util = (void*)comment; + *(int*)commit->object.sha1 = virtual_id++; + return commit; +} + +/* + * TODO: we should not have to copy the SHA1s around, but rather reference + * them. That way, sha_eq() is just sha1 == sha2. + */ +static int sha_eq(const unsigned char *a, const unsigned char *b) +{ + if ( !a && !b ) + return 2; + return a && b && memcmp(a, b, 20) == 0; +} + +static void memswp(void *p1, void *p2, unsigned n) +{ + unsigned char *a = p1, *b = p2; + while ( n-- ) { + *a ^= *b; + *b ^= *a; + *a ^= *b; + ++a; + ++b; + } +} + +/* + * TODO: we should convert the merge_result users to + * int blabla(..., struct commit **result) + * like everywhere else in git. + * Same goes for merge_tree_result and merge_file_info. + */ +struct merge_result +{ + struct commit *commit; + unsigned clean:1; +}; + +struct merge_tree_result +{ + struct tree *tree; + unsigned clean:1; +}; + +/* + * TODO: check if we can just reuse the active_cache structure: it is already + * sorted (by name, stage). + * Only problem: do not write it when flushing the cache. + */ +struct stage_data +{ + struct + { + unsigned mode; + unsigned char sha[20]; + } stages[4]; + unsigned processed:1; +}; + +static struct path_list currentFileSet = {NULL, 0, 0, 1}; +static struct path_list currentDirectorySet = {NULL, 0, 0, 1}; + +static int output_indent = 0; + +static void output(const char *fmt, ...) +{ + va_list args; + int i; + for ( i = output_indent; i--; ) + fputs(" ", stdout); + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); + fputc('\n', stdout); +} + +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) || + close(fd) || commit_lock_file(lock)) + die ("unable to write %s", getenv("GIT_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 + * only written to in the 'merge' function. + * + * index_only == 1 => Don't leave any non-stage 0 entries in the cache and + * don't update the working directory. + * 0 => Leave unmerged entries in the cache and update + * the working directory. + */ +static int index_only = 0; + +/* + * TODO: this can be streamlined by refactoring builtin-read-tree.c + */ +static int git_read_tree(const struct tree *tree) +{ +#if 0 + fprintf(stderr, "GIT_INDEX_FILE='%s' git-read-tree %s\n", + getenv("GIT_INDEX_FILE"), + 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; +} + +/* + * TODO: this can be streamlined by refactoring builtin-read-tree.c + */ +static int git_merge_trees(const char *update_arg, + struct tree *common, + struct tree *head, + struct tree *merge) +{ +#if 0 + fprintf(stderr, "GIT_INDEX_FILE='%s' git-read-tree %s -m %s %s %s\n", + getenv("GIT_INDEX_FILE"), + update_arg, + sha1_to_hex(common->object.sha1), + sha1_to_hex(head->object.sha1), + sha1_to_hex(merge->object.sha1)); +#endif + const char *argv[] = { + "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); + argv[5] = sha1_to_hex(merge->object.sha1); + int rc = run_command_v(6, argv); + return rc < 0 ? -1: rc; +} + +/* + * TODO: this can be streamlined by refactoring builtin-write-tree.c + */ +static 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); +} + +/* + * TODO: get rid of files_and_dirs; we do not use it except for + * current_file_set and current_dir_set, which are global already. + */ +static struct +{ + struct path_list *files; + struct path_list *dirs; +} files_and_dirs; + +static int save_files_dirs(const unsigned char *sha1, + const char *base, int baselen, const char *path, + unsigned int mode, int stage) +{ + int len = strlen(path); + char *newpath = malloc(baselen + len + 1); + memcpy(newpath, base, baselen); + memcpy(newpath + baselen, path, len); + newpath[baselen + len] = '\0'; + + if (S_ISDIR(mode)) + path_list_insert(newpath, files_and_dirs.dirs); + else + path_list_insert(newpath, files_and_dirs.files); + free(newpath); + + return READ_TREE_RECURSIVE; +} + +static int get_files_dirs(struct tree *tree, + struct path_list *files, + struct path_list *dirs) +{ + int n; + files_and_dirs.files = files; + files_and_dirs.dirs = dirs; + debug("get_files_dirs ...\n"); + if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs) != 0) { + debug(" get_files_dirs done (0)\n"); + return 0; + } + n = files->nr + dirs->nr; + debug(" get_files_dirs done (%d)\n", n); + return n; +} + +/* + * TODO: this wrapper is so small, we can use path_list_lookup directly. + * Same goes for index_entry_get(), free_index_entries(), find_rename_bysrc(), + * free_rename_entries(). + */ +static struct stage_data *index_entry_find(struct path_list *ents, + const char *path) +{ + struct path_list_item *item = path_list_lookup(path, ents); + if (item) + return item->util; + return NULL; +} + +static struct stage_data *index_entry_get(struct path_list *ents, + const char *path) +{ + struct path_list_item *item = path_list_lookup(path, ents); + + if (item == NULL) { + item = path_list_insert(path, ents); + item->util = xcalloc(1, sizeof(struct stage_data)); + } + return item->util; +} + +/* + * TODO: since the result of index_entry_from_db() is tucked into a + * path_list anyway, this helper can do that already. + */ +/* + * Returns a index_entry instance which doesn't have to correspond to + * a real cache entry in Git's index. + */ +static struct stage_data *index_entry_from_db(const char *path, + struct tree *o, + struct tree *a, + struct tree *b) +{ + struct stage_data *e = xcalloc(1, sizeof(struct stage_data)); + get_tree_entry(o->object.sha1, path, + e->stages[1].sha, &e->stages[1].mode); + get_tree_entry(a->object.sha1, path, + e->stages[2].sha, &e->stages[2].mode); + get_tree_entry(b->object.sha1, path, + e->stages[3].sha, &e->stages[3].mode); + return e; +} + +static void free_index_entries(struct path_list **ents) +{ + if (!*ents) + return; + + path_list_clear(*ents, 1); + free(*ents); + *ents = NULL; +} + +/* + * Create a dictionary mapping file names to CacheEntry objects. The + * dictionary contains one entry for every path with a non-zero stage entry. + */ +static struct path_list *get_unmerged() +{ + struct path_list *unmerged = xcalloc(1, sizeof(struct path_list)); + int i; + + unmerged->strdup_paths = 1; + if (!cache_dirty) { + read_cache_from(getenv("GIT_INDEX_FILE")); + cache_dirty++; + } + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (!ce_stage(ce)) + continue; + + struct stage_data *e = index_entry_get(unmerged, ce->name); + e->stages[ce_stage(ce)].mode = ntohl(ce->ce_mode); + memcpy(e->stages[ce_stage(ce)].sha, ce->sha1, 20); + } + + debug(" get_unmerged done\n"); + return unmerged; +} + +struct rename +{ + struct diff_filepair *pair; + struct stage_data *src_entry; + struct stage_data *dst_entry; + unsigned processed:1; +}; + +static struct rename *find_rename_bysrc(struct path_list *e, + const char *name) +{ + struct path_list_item *item = path_list_lookup(name, e); + if (item) + return item->util; + return NULL; +} + +static void free_rename_entries(struct path_list **list) +{ + if (!*list) + return; + + path_list_clear(*list, 0); + free(*list); + *list = NULL; +} + +/* + * Get information of all renames which occured between 'oTree' and + * 'tree'. We need the three trees in the merge ('oTree', 'aTree' and + * 'bTree') to be able to associate the correct cache entries with + * the rename information. 'tree' is always equal to either aTree or bTree. + */ +static struct path_list *get_renames(struct tree *tree, + struct tree *oTree, + struct tree *aTree, + struct tree *bTree, + struct path_list *entries) +{ +#ifdef DEBUG + time_t t = time(0); + debug("getRenames ...\n"); +#endif + int i; + struct path_list *renames = xcalloc(1, sizeof(struct path_list)); + struct diff_options opts; + diff_setup(&opts); + opts.recursive = 1; + opts.detect_rename = DIFF_DETECT_RENAME; + opts.output_format = DIFF_FORMAT_NO_OUTPUT; + if (diff_setup_done(&opts) < 0) + die("diff setup failed"); + diff_tree_sha1(oTree->object.sha1, tree->object.sha1, "", &opts); + diffcore_std(&opts); + for (i = 0; i < diff_queued_diff.nr; ++i) { + struct rename *re; + struct diff_filepair *pair = diff_queued_diff.queue[i]; + if (pair->status != 'R') { + diff_free_filepair(pair); + continue; + } + re = xmalloc(sizeof(*re)); + re->processed = 0; + re->pair = pair; + re->src_entry = index_entry_find(entries, re->pair->one->path); + /* TODO: should it not be an error, if src_entry was found? */ + if ( !re->src_entry ) { + re->src_entry = index_entry_from_db(re->pair->one->path, + oTree, aTree, bTree); + struct path_list_item *item = + path_list_insert(re->pair->one->path, entries); + item->util = re->src_entry; + } + re->dst_entry = index_entry_find(entries, re->pair->two->path); + if ( !re->dst_entry ) { + re->dst_entry = index_entry_from_db(re->pair->two->path, + oTree, aTree, bTree); + struct path_list_item *item = + path_list_insert(re->pair->two->path, entries); + item->util = re->dst_entry; + } + struct path_list_item *item = path_list_insert(pair->one->path, renames); + item->util = re; + } + opts.output_format = DIFF_FORMAT_NO_OUTPUT; + diff_queued_diff.nr = 0; + diff_flush(&opts); + debug(" getRenames done in %ld\n", time(0)-t); + return renames; +} + +/* + * TODO: the code would be way nicer, if we had a struct containing just sha1 and mode. + * In this particular case, we might get away reusing stage_data, no? + */ +int update_stages(const char *path, + unsigned char *osha, unsigned omode, + unsigned char *asha, unsigned amode, + unsigned char *bsha, unsigned bmode, + int clear /* =True */) +{ + 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; +} + +/* + * TODO: there has to be a function in libgit doing this exact thing. + */ +static int remove_path(const char *name) +{ + int ret; + char *slash; + + ret = unlink(name); + if ( ret ) + return ret; + int len = strlen(name); + char *dirs = malloc(len+1); + memcpy(dirs, name, len); + dirs[len] = '\0'; + while ( (slash = strrchr(name, '/')) ) { + *slash = '\0'; + len = slash - name; + if ( rmdir(name) != 0 ) + break; + } + free(dirs); + return ret; +} + +/* General TODO: unC99ify the code: no declaration after code */ +/* General TODO: no javaIfiCation: rename updateCache to update_cache */ +/* + * TODO: once we no longer call external programs, we'd probably be better of + * not setting / getting the environment variable GIT_INDEX_FILE all the time. + */ +int remove_file(int clean, const char *path) +{ + int updateCache = index_only || clean; + int updateWd = !index_only; + + if ( updateCache ) { + if (!cache_dirty) + read_cache_from(getenv("GIT_INDEX_FILE")); + cache_dirty++; + if (remove_file_from_cache(path)) + return -1; + } + if ( updateWd ) + { + unlink(path); + if ( errno != ENOENT || errno != EISDIR ) + return -1; + remove_path(path); + } + return 0; +} + +static char *unique_path(const char *path, const char *branch) +{ + char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1); + strcpy(newpath, path); + strcat(newpath, "~"); + char *p = newpath + strlen(newpath); + strcpy(p, branch); + for ( ; *p; ++p ) + if ( '/' == *p ) + *p = '_'; + int suffix = 0; + struct stat st; + while ( path_list_has_path(¤tFileSet, newpath) || + path_list_has_path(¤tDirectorySet, newpath) || + lstat(newpath, &st) == 0 ) { + sprintf(p, "_%d", suffix++); + } + path_list_insert(newpath, ¤tFileSet); + return newpath; +} + +/* + * TODO: except for create_last, this so looks like + * safe_create_leading_directories(). + */ +static int mkdir_p(const char *path, unsigned long mode, int create_last) +{ + char *buf = strdup(path); + char *p; + + for ( p = buf; *p; ++p ) { + if ( *p != '/' ) + continue; + *p = '\0'; + if (mkdir(buf, mode)) { + int e = errno; + if ( e == EEXIST ) { + struct stat st; + if ( !stat(buf, &st) && S_ISDIR(st.st_mode) ) + goto next; /* ok */ + errno = e; + } + free(buf); + return -1; + } + next: + *p = '/'; + } + free(buf); + if ( create_last && mkdir(path, mode) ) + return -1; + return 0; +} + +static void flush_buffer(int fd, const char *buf, unsigned long size) +{ + while (size > 0) { + long ret = xwrite(fd, buf, size); + if (ret < 0) { + /* Ignore epipe */ + if (errno == EPIPE) + break; + die("merge-recursive: %s", strerror(errno)); + } else if (!ret) { + die("merge-recursive: disk full?"); + } + size -= ret; + buf += ret; + } +} + +/* General TODO: reindent according to guide lines (no if ( blabla )) */ +void update_file_flags(const unsigned char *sha, + unsigned mode, + const char *path, + int updateCache, + int updateWd) +{ + if ( index_only ) + updateWd = 0; + + if ( updateWd ) { + char type[20]; + void *buf; + unsigned long size; + + buf = read_sha1_file(sha, type, &size); + if (!buf) + die("cannot read object %s '%s'", sha1_to_hex(sha), path); + if ( strcmp(type, blob_type) != 0 ) + die("blob expected for %s '%s'", sha1_to_hex(sha), path); + + if ( S_ISREG(mode) ) { + if ( mkdir_p(path, 0777, 0 /* don't create last element */) ) + die("failed to create path %s: %s", path, strerror(errno)); + unlink(path); + if ( mode & 0100 ) + mode = 0777; + else + mode = 0666; + int fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); + if ( fd < 0 ) + die("failed to open %s: %s", path, strerror(errno)); + flush_buffer(fd, buf, size); + close(fd); + } else if ( S_ISLNK(mode) ) { + char *linkTarget = malloc(size + 1); + memcpy(linkTarget, buf, size); + linkTarget[size] = '\0'; + mkdir_p(path, 0777, 0); + symlink(linkTarget, path); + } else + die("do not know what to do with %06o %s '%s'", + mode, sha1_to_hex(sha), path); + } + if ( updateCache ) + add_cacheinfo(mode, sha, path, 0, updateWd, ADD_CACHE_OK_TO_ADD); +} + +/* TODO: is this often used? if not, do direct call */ +void update_file(int clean, + const unsigned char *sha, + unsigned mode, + const char *path) +{ + update_file_flags(sha, mode, path, index_only || clean, !index_only); +} + +/* Low level file merging, update and removal */ + +struct merge_file_info +{ + unsigned char sha[20]; + unsigned mode; + unsigned clean:1, + merge:1; +}; + +static char *git_unpack_file(const unsigned char *sha1, char *path) +{ + void *buf; + char type[20]; + unsigned long size; + int fd; + + buf = read_sha1_file(sha1, type, &size); + if (!buf || strcmp(type, blob_type)) + die("unable to read blob object %s", sha1_to_hex(sha1)); + + strcpy(path, ".merge_file_XXXXXX"); + fd = mkstemp(path); + if (fd < 0) + die("unable to create temp-file"); + flush_buffer(fd, buf, size); + close(fd); + return path; +} + +/* + * TODO: the signature would be much more efficient using stage_data + */ +static struct merge_file_info merge_file(const char *oPath, + const unsigned char *oSha, + unsigned oMode, + const char *aPath, + const unsigned char *aSha, + unsigned aMode, + const char *bPath, + const unsigned char *bSha, + unsigned bMode, + const char *branch1Name, + const char *branch2Name) +{ + struct merge_file_info result; + result.merge = 0; + result.clean = 1; + + if ( (S_IFMT & aMode) != (S_IFMT & bMode) ) { + result.clean = 0; + if ( S_ISREG(aMode) ) { + result.mode = aMode; + memcpy(result.sha, aSha, 20); + } else { + result.mode = bMode; + memcpy(result.sha, bSha, 20); + } + } else { + if ( memcmp(aSha, oSha, 20) != 0 && memcmp(bSha, oSha, 20) != 0 ) + result.merge = 1; + + result.mode = aMode == oMode ? bMode: aMode; + + if ( memcmp(aSha, oSha, 20) == 0 ) + memcpy(result.sha, bSha, 20); + else if ( memcmp(bSha, oSha, 20) == 0 ) + memcpy(result.sha, aSha, 20); + else if ( S_ISREG(aMode) ) { + + int code = 1; + char orig[PATH_MAX]; + char src1[PATH_MAX]; + char src2[PATH_MAX]; + + git_unpack_file(oSha, orig); + git_unpack_file(aSha, src1); + git_unpack_file(bSha, src2); + + const char *argv[] = { + "merge", "-L", NULL, "-L", NULL, "-L", NULL, + src1, orig, src2, + NULL + }; + char *la, *lb, *lo; + argv[2] = la = strdup(mkpath("%s/%s", branch1Name, aPath)); + argv[6] = lb = strdup(mkpath("%s/%s", branch2Name, bPath)); + argv[4] = lo = strdup(mkpath("orig/%s", oPath)); + +#if 0 + printf("%s %s %s %s %s %s %s %s %s %s\n", + argv[0], argv[1], argv[2], argv[3], argv[4], + argv[5], argv[6], argv[7], argv[8], argv[9]); +#endif + code = run_command_v(10, argv); + + free(la); + free(lb); + free(lo); + if ( code && code < -256 ) { + die("Failed to execute 'merge'. merge(1) is used as the " + "file-level merge tool. Is 'merge' in your path?"); + } + struct stat st; + int fd = open(src1, O_RDONLY); + if (fd < 0 || fstat(fd, &st) < 0 || + index_fd(result.sha, fd, &st, 1, + "blob")) + die("Unable to add %s to database", src1); + close(fd); + + unlink(orig); + unlink(src1); + unlink(src2); + + result.clean = WEXITSTATUS(code) == 0; + } else { + if ( !(S_ISLNK(aMode) || S_ISLNK(bMode)) ) + die("cannot merge modes?"); + + memcpy(result.sha, aSha, 20); + + if ( memcmp(aSha, bSha, 20) != 0 ) + result.clean = 0; + } + } + + return result; +} + +static void conflict_rename_rename(struct rename *ren1, + const char *branch1, + struct rename *ren2, + const char *branch2) +{ + char *del[2]; + int delp = 0; + const char *ren1_dst = ren1->pair->two->path; + const char *ren2_dst = ren2->pair->two->path; + const char *dstName1 = ren1_dst; + const char *dstName2 = ren2_dst; + if (path_list_has_path(¤tDirectorySet, ren1_dst)) { + dstName1 = del[delp++] = unique_path(ren1_dst, branch1); + output("%s is a directory in %s adding as %s instead", + ren1_dst, branch2, dstName1); + remove_file(0, ren1_dst); + } + if (path_list_has_path(¤tDirectorySet, ren2_dst)) { + dstName2 = del[delp++] = unique_path(ren2_dst, branch2); + output("%s is a directory in %s adding as %s instead", + ren2_dst, branch1, dstName2); + remove_file(0, ren2_dst); + } + update_stages(dstName1, + NULL, 0, + ren1->pair->two->sha1, ren1->pair->two->mode, + NULL, 0, + 1 /* clear */); + update_stages(dstName2, + NULL, 0, + NULL, 0, + ren2->pair->two->sha1, ren2->pair->two->mode, + 1 /* clear */); + while ( delp-- ) + free(del[delp]); +} + +static void conflict_rename_dir(struct rename *ren1, + const char *branch1) +{ + char *newPath = unique_path(ren1->pair->two->path, branch1); + output("Renaming %s to %s instead", ren1->pair->one->path, newPath); + remove_file(0, ren1->pair->two->path); + update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, newPath); + free(newPath); +} + +static void conflict_rename_rename_2(struct rename *ren1, + const char *branch1, + struct rename *ren2, + const char *branch2) +{ + char *newPath1 = unique_path(ren1->pair->two->path, branch1); + char *newPath2 = unique_path(ren2->pair->two->path, branch2); + output("Renaming %s to %s and %s to %s instead", + ren1->pair->one->path, newPath1, + ren2->pair->one->path, newPath2); + remove_file(0, ren1->pair->two->path); + update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, newPath1); + update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, newPath2); + free(newPath2); + free(newPath1); +} + +/* General TODO: get rid of all the debug messages */ +static int process_renames(struct path_list *renamesA, + struct path_list *renamesB, + const char *branchNameA, + const char *branchNameB) +{ + int cleanMerge = 1, i; + struct path_list srcNames = {NULL, 0, 0, 0}, byDstA = {NULL, 0, 0, 0}, byDstB = {NULL, 0, 0, 0}; + const struct rename *sre; + + /* + * TODO: think about a saner way to do this. + * Since both renamesA and renamesB are sorted, it should + * be much more efficient to traverse both simultaneously, + * only byDstA and byDstB should be needed. + */ + debug("processRenames...\n"); + for (i = 0; i < renamesA->nr; i++) { + sre = renamesA->items[i].util; + path_list_insert(sre->pair->one->path, &srcNames); + path_list_insert(sre->pair->two->path, &byDstA)->util + = sre->dst_entry; + } + for (i = 0; i < renamesB->nr; i++) { + sre = renamesB->items[i].util; + path_list_insert(sre->pair->one->path, &srcNames); + path_list_insert(sre->pair->two->path, &byDstB)->util + = sre->dst_entry; + } + + for (i = 0; i < srcNames.nr; i++) { + char *src = srcNames.items[i].path; + struct path_list *renames1, *renames2, *renames2Dst; + struct rename *ren1, *ren2; + const char *branchName1, *branchName2; + ren1 = find_rename_bysrc(renamesA, src); + ren2 = find_rename_bysrc(renamesB, src); + /* TODO: refactor, so that 1/2 are not needed */ + if ( ren1 ) { + renames1 = renamesA; + renames2 = renamesB; + renames2Dst = &byDstB; + branchName1 = branchNameA; + branchName2 = branchNameB; + } else { + renames1 = renamesB; + renames2 = renamesA; + renames2Dst = &byDstA; + branchName1 = branchNameB; + branchName2 = branchNameA; + struct rename *tmp = ren2; + ren2 = ren1; + ren1 = tmp; + } + + ren1->dst_entry->processed = 1; + ren1->src_entry->processed = 1; + + if ( ren1->processed ) + continue; + ren1->processed = 1; + + const char *ren1_src = ren1->pair->one->path; + const char *ren1_dst = ren1->pair->two->path; + + if ( ren2 ) { + const char *ren2_src = ren2->pair->one->path; + const char *ren2_dst = ren2->pair->two->path; + /* Renamed in 1 and renamed in 2 */ + if (strcmp(ren1_src, ren2_src) != 0) + die("ren1.src != ren2.src"); + ren2->dst_entry->processed = 1; + ren2->processed = 1; + if (strcmp(ren1_dst, ren2_dst) != 0) { + cleanMerge = 0; + output("CONFLICT (rename/rename): " + "Rename %s->%s in branch %s " + "rename %s->%s in %s", + src, ren1_dst, branchName1, + src, ren2_dst, branchName2); + conflict_rename_rename(ren1, branchName1, ren2, branchName2); + } else { + remove_file(1, ren1_src); + struct merge_file_info mfi; + mfi = merge_file(ren1_src, + ren1->pair->one->sha1, + ren1->pair->one->mode, + ren1_dst, + ren1->pair->two->sha1, + ren1->pair->two->mode, + ren2_dst, + ren2->pair->two->sha1, + ren2->pair->two->mode, + branchName1, + branchName2); + if ( mfi.merge || !mfi.clean ) + output("Renaming %s->%s", src, ren1_dst); + + if ( mfi.merge ) + output("Auto-merging %s", ren1_dst); + + if ( !mfi.clean ) { + output("CONFLICT (content): merge conflict in %s", + ren1_dst); + cleanMerge = 0; + + if ( !index_only ) + update_stages(ren1_dst, + ren1->pair->one->sha1, + ren1->pair->one->mode, + ren1->pair->two->sha1, + ren1->pair->two->mode, + ren2->pair->two->sha1, + ren2->pair->two->mode, + 1 /* clear */); + } + update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst); + } + } else { + /* Renamed in 1, maybe changed in 2 */ + remove_file(1, ren1_src); + + unsigned char srcShaOtherBranch[20], dstShaOtherBranch[20]; + unsigned srcModeOtherBranch, dstModeOtherBranch; + + int stage = renamesA == renames1 ? 3: 2; + + memcpy(srcShaOtherBranch, ren1->src_entry->stages[stage].sha, 20); + srcModeOtherBranch = ren1->src_entry->stages[stage].mode; + + memcpy(dstShaOtherBranch, ren1->dst_entry->stages[stage].sha, 20); + dstModeOtherBranch = ren1->dst_entry->stages[stage].mode; + + int tryMerge = 0; + char *newPath; + + if (path_list_has_path(¤tDirectorySet, ren1_dst)) { + cleanMerge = 0; + output("CONFLICT (rename/directory): Rename %s->%s in %s " + " directory %s added in %s", + ren1_src, ren1_dst, branchName1, + ren1_dst, branchName2); + conflict_rename_dir(ren1, branchName1); + } else if ( memcmp(srcShaOtherBranch, null_sha1, 20) == 0 ) { + cleanMerge = 0; + output("CONFLICT (rename/delete): Rename %s->%s in %s " + "and deleted in %s", + ren1_src, ren1_dst, branchName1, + branchName2); + update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst); + } else if ( memcmp(dstShaOtherBranch, null_sha1, 20) != 0 ) { + cleanMerge = 0; + tryMerge = 1; + output("CONFLICT (rename/add): Rename %s->%s in %s. " + "%s added in %s", + ren1_src, ren1_dst, branchName1, + ren1_dst, branchName2); + newPath = unique_path(ren1_dst, branchName2); + output("Adding as %s instead", newPath); + update_file(0, dstShaOtherBranch, dstModeOtherBranch, newPath); + } else if ( (ren2 = find_rename_bysrc(renames2Dst, ren1_dst)) ) { + cleanMerge = 0; + ren2->processed = 1; + output("CONFLICT (rename/rename): Rename %s->%s in %s. " + "Rename %s->%s in %s", + ren1_src, ren1_dst, branchName1, + ren2->pair->one->path, ren2->pair->two->path, branchName2); + conflict_rename_rename_2(ren1, branchName1, ren2, branchName2); + } else + tryMerge = 1; + + if ( tryMerge ) { + const char *oname = ren1_src; + const char *aname = ren1_dst; + const char *bname = ren1_src; + unsigned char osha[20], asha[20], bsha[20]; + unsigned omode = ren1->pair->one->mode; + unsigned amode = ren1->pair->two->mode; + unsigned bmode = srcModeOtherBranch; + memcpy(osha, ren1->pair->one->sha1, 20); + memcpy(asha, ren1->pair->two->sha1, 20); + memcpy(bsha, srcShaOtherBranch, 20); + const char *aBranch = branchName1; + const char *bBranch = branchName2; + + if ( renamesA != renames1 ) { + memswp(&aname, &bname, sizeof(aname)); + memswp(asha, bsha, 20); + memswp(&aBranch, &bBranch, sizeof(aBranch)); + } + struct merge_file_info mfi; + mfi = merge_file(oname, osha, omode, + aname, asha, amode, + bname, bsha, bmode, + aBranch, bBranch); + + if ( mfi.merge || !mfi.clean ) + output("Renaming %s => %s", ren1_src, ren1_dst); + if ( mfi.merge ) + output("Auto-merging %s", ren1_dst); + if ( !mfi.clean ) { + output("CONFLICT (rename/modify): Merge conflict in %s", + ren1_dst); + cleanMerge = 0; + + if ( !index_only ) + update_stages(ren1_dst, + osha, omode, + asha, amode, + bsha, bmode, + 1 /* clear */); + } + update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst); + } + } + } + path_list_clear(&srcNames, 0); + debug(" processRenames done\n"); + + if (cache_dirty) + flush_cache(); + return cleanMerge; +} + +static unsigned char *has_sha(const unsigned char *sha) +{ + return memcmp(sha, null_sha1, 20) == 0 ? NULL: (unsigned char *)sha; +} + +/* Per entry merge function */ +static int process_entry(const char *path, struct stage_data *entry, + const char *branch1Name, + const char *branch2Name) +{ + /* + printf("processing entry, clean cache: %s\n", index_only ? "yes": "no"); + print_index_entry("\tpath: ", entry); + */ + int cleanMerge = 1; + unsigned char *oSha = has_sha(entry->stages[1].sha); + unsigned char *aSha = has_sha(entry->stages[2].sha); + unsigned char *bSha = has_sha(entry->stages[3].sha); + unsigned oMode = entry->stages[1].mode; + unsigned aMode = entry->stages[2].mode; + unsigned bMode = entry->stages[3].mode; + + if ( oSha && (!aSha || !bSha) ) { + /* Case A: Deleted in one */ + if ( (!aSha && !bSha) || + (sha_eq(aSha, oSha) && !bSha) || + (!aSha && sha_eq(bSha, oSha)) ) { + /* Deleted in both or deleted in one and + * unchanged in the other */ + if ( aSha ) + output("Removing %s", path); + remove_file(1, path); + } else { + /* Deleted in one and changed in the other */ + cleanMerge = 0; + if ( !aSha ) { + output("CONFLICT (delete/modify): %s deleted in %s " + "and modified in %s. Version %s of %s left in tree.", + path, branch1Name, + branch2Name, branch2Name, path); + update_file(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); + update_file(0, aSha, aMode, path); + } + } + + } else if ( (!oSha && aSha && !bSha) || + (!oSha && !aSha && bSha) ) { + /* Case B: Added in one. */ + const char *addBranch; + const char *otherBranch; + unsigned mode; + const unsigned char *sha; + const char *conf; + + if ( aSha ) { + addBranch = branch1Name; + otherBranch = branch2Name; + mode = aMode; + sha = aSha; + conf = "file/directory"; + } else { + addBranch = branch2Name; + otherBranch = branch1Name; + mode = bMode; + sha = bSha; + conf = "directory/file"; + } + if ( path_list_has_path(¤tDirectorySet, path) ) { + cleanMerge = 0; + const char *newPath = unique_path(path, addBranch); + output("CONFLICT (%s): There is a directory with name %s in %s. " + "Adding %s as %s", + conf, path, otherBranch, path, newPath); + remove_file(0, path); + update_file(0, sha, mode, newPath); + } else { + output("Adding %s", path); + update_file(1, sha, mode, path); + } + } else if ( !oSha && aSha && bSha ) { + /* Case C: Added in both (check for same permissions). */ + if ( sha_eq(aSha, bSha) ) { + if ( aMode != bMode ) { + cleanMerge = 0; + output("CONFLICT: File %s added identically in both branches, " + "but permissions conflict %06o->%06o", + path, aMode, bMode); + output("CONFLICT: adding with permission: %06o", aMode); + update_file(0, aSha, aMode, path); + } else { + /* This case is handled by git-read-tree */ + assert(0 && "This case must be handled by git-read-tree"); + } + } else { + cleanMerge = 0; + const char *newPath1 = unique_path(path, branch1Name); + const char *newPath2 = unique_path(path, branch2Name); + output("CONFLICT (add/add): File %s added non-identically " + "in both branches. Adding as %s and %s instead.", + path, newPath1, newPath2); + remove_file(0, path); + update_file(0, aSha, aMode, newPath1); + update_file(0, bSha, bMode, newPath2); + } + + } else if ( oSha && aSha && bSha ) { + /* case D: Modified in both, but differently. */ + output("Auto-merging %s", path); + struct merge_file_info mfi; + mfi = merge_file(path, oSha, oMode, + path, aSha, aMode, + path, bSha, bMode, + branch1Name, branch2Name); + + if ( mfi.clean ) + update_file(1, mfi.sha, mfi.mode, path); + else { + cleanMerge = 0; + output("CONFLICT (content): Merge conflict in %s", path); + + if ( index_only ) + update_file(0, mfi.sha, mfi.mode, path); + else + update_file_flags(mfi.sha, mfi.mode, path, + 0 /* updateCache */, 1 /* updateWd */); + } + } else + die("Fatal merge failure, shouldn't happen."); + + if (cache_dirty) + flush_cache(); + + return cleanMerge; +} + +static struct merge_tree_result merge_trees(struct tree *head, + struct tree *merge, + struct tree *common, + const char *branch1Name, + const char *branch2Name) +{ + int code; + struct merge_tree_result result = { NULL, 0 }; + if ( !memcmp(common->object.sha1, merge->object.sha1, 20) ) { + output("Already uptodate!"); + result.tree = head; + result.clean = 1; + return result; + } + + debug("merge_trees ...\n"); + code = git_merge_trees(index_only ? "-i": "-u", common, head, merge); + + if ( code != 0 ) + die("merging of trees %s and %s failed", + sha1_to_hex(head->object.sha1), + sha1_to_hex(merge->object.sha1)); + + result.tree = git_write_tree(); + + if ( !result.tree ) { + path_list_clear(¤tFileSet, 1); + path_list_clear(¤tDirectorySet, 1); + get_files_dirs(head, ¤tFileSet, ¤tDirectorySet); + get_files_dirs(merge, ¤tFileSet, ¤tDirectorySet); + + struct path_list *entries = get_unmerged(); + struct path_list *re_head, *re_merge; + re_head = get_renames(head, common, head, merge, entries); + re_merge = get_renames(merge, common, head, merge, entries); + result.clean = process_renames(re_head, re_merge, + branch1Name, branch2Name); + debug("\tprocessing entries...\n"); + int i; + for (i = 0; i < entries->nr; i++) { + const char *path = entries->items[i].path; + struct stage_data *e = entries->items[i].util; + if (e->processed) + continue; + if (!process_entry(path, e, branch1Name, branch2Name)) + result.clean = 0; + } + + free_rename_entries(&re_merge); + free_rename_entries(&re_head); + free_index_entries(&entries); + + if (result.clean || index_only) + result.tree = git_write_tree(); + else + result.tree = NULL; + debug("\t processing entries done\n"); + } else { + result.clean = 1; + printf("merging of trees %s and %s resulted in %s\n", + sha1_to_hex(head->object.sha1), + sha1_to_hex(merge->object.sha1), + sha1_to_hex(result.tree->object.sha1)); + } + + debug(" merge_trees done\n"); + return result; +} + +/* + * Merge the commits h1 and h2, return the resulting virtual + * commit object and a flag indicating the cleaness of the merge. + */ +static +struct merge_result merge(struct commit *h1, + struct commit *h2, + const char *branch1Name, + const char *branch2Name, + int callDepth /* =0 */, + struct commit *ancestor /* =None */) +{ + struct merge_result result = { NULL, 0 }; + const char *msg; + int msglen; + struct commit_list *ca = NULL, *iter; + struct commit *mergedCA; + struct merge_tree_result mtr; + + output("Merging:"); + msg = commit_title(h1, &msglen); + /* TODO: refactor. we always show the sha1 with the title */ + output("%s %.*s", commit_hex_sha1(h1), msglen, msg); + msg = commit_title(h2, &msglen); + output("%s %.*s", commit_hex_sha1(h2), msglen, msg); + + if ( ancestor ) + commit_list_insert(ancestor, &ca); + else + ca = get_merge_bases(h1, h2, 1); + + output("found %u common ancestor(s):", commit_list_count(ca)); + for (iter = ca; iter; iter = iter->next) { + msg = commit_title(iter->item, &msglen); + output("%s %.*s", commit_hex_sha1(iter->item), msglen, msg); + } + + mergedCA = pop_commit(&ca); + + /* TODO: what happens when merge with virtual commits fails? */ + for (iter = ca; iter; iter = iter->next) { + output_indent = callDepth + 1; + result = merge(mergedCA, iter->item, + "Temporary merge branch 1", + "Temporary merge branch 2", + callDepth + 1, + NULL); + mergedCA = result.commit; + output_indent = callDepth; + + if ( !mergedCA ) + die("merge returned no commit"); + } + + if ( callDepth == 0 ) { + setup_index(0); + index_only = 0; + } else { + setup_index(1); + git_read_tree(h1->tree); + index_only = 1; + } + + mtr = merge_trees(h1->tree, h2->tree, + mergedCA->tree, branch1Name, branch2Name); + + if ( !ancestor && (mtr.clean || index_only) ) { + result.commit = make_virtual_commit(mtr.tree, "merged tree"); + commit_list_insert(h1, &result.commit->parents); + commit_list_insert(h2, &result.commit->parents->next); + } else + result.commit = NULL; + + result.clean = mtr.clean; + return result; +} + +static struct commit *get_ref(const char *ref) +{ + unsigned char sha1[20]; + struct object *object; + + if (get_sha1(ref, sha1)) + die("Could not resolve ref '%s'", ref); + object = deref_tag(parse_object(sha1), ref, strlen(ref)); + if (object->type != TYPE_COMMIT) + return NULL; + if (parse_commit((struct commit *)object)) + die("Could not parse commit '%s'", sha1_to_hex(object->sha1)); + return (struct commit *)object; +} + +int main(int argc, char *argv[]) +{ + static const char *bases[2]; + static unsigned bases_count = 0; + + original_index_file = getenv("GIT_INDEX_FILE"); + + if (!original_index_file) + original_index_file = strdup(git_path("index")); + + temporary_index_file = strdup(git_path("mrg-rcrsv-tmp-idx")); + + if (argc < 4) + die("Usage: %s ... -- ...\n", argv[0]); + + int i; + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--")) + break; + if (bases_count < sizeof(bases)/sizeof(*bases)) + bases[bases_count++] = argv[i]; + } + if (argc - i != 3) /* "--" "" "" */ + die("Not handling anything other than two heads merge."); + + const char *branch1, *branch2; + + branch1 = argv[++i]; + branch2 = argv[++i]; + printf("Merging %s with %s\n", branch1, branch2); + + struct merge_result result; + struct commit *h1 = get_ref(branch1); + struct commit *h2 = get_ref(branch2); + + if (bases_count == 1) { + struct commit *ancestor = get_ref(bases[0]); + result = merge(h1, h2, branch1, branch2, 0, ancestor); + } else + result = merge(h1, h2, branch1, branch2, 0, NULL); + + if (cache_dirty) + flush_cache(); + + return result.clean ? 0: 1; +} + +/* +vim: sw=8 noet +*/ diff --git a/path-list.c b/path-list.c new file mode 100644 index 0000000000..f15a10de37 --- /dev/null +++ b/path-list.c @@ -0,0 +1,105 @@ +#include +#include "cache.h" +#include "path-list.h" + +/* if there is no exact match, point to the index where the entry could be + * inserted */ +static int get_entry_index(const struct path_list *list, const char *path, + int *exact_match) +{ + int left = -1, right = list->nr; + + while (left + 1 < right) { + int middle = (left + right) / 2; + int compare = strcmp(path, list->items[middle].path); + if (compare < 0) + right = middle; + else if (compare > 0) + left = middle; + else { + *exact_match = 1; + return middle; + } + } + + *exact_match = 0; + return right; +} + +/* returns -1-index if already exists */ +static int add_entry(struct path_list *list, const char *path) +{ + int exact_match; + int index = get_entry_index(list, path, &exact_match); + + if (exact_match) + return -1 - index; + + if (list->nr + 1 >= list->alloc) { + list->alloc += 32; + list->items = xrealloc(list->items, list->alloc + * sizeof(struct path_list_item)); + } + if (index < list->nr) + memmove(list->items + index + 1, list->items + index, + (list->nr - index) + * sizeof(struct path_list_item)); + list->items[index].path = list->strdup_paths ? + strdup(path) : (char *)path; + list->items[index].util = NULL; + list->nr++; + + return index; +} + +struct path_list_item *path_list_insert(const char *path, struct path_list *list) +{ + int index = add_entry(list, path); + + if (index < 0) + index = 1 - index; + + return list->items + index; +} + +int path_list_has_path(const struct path_list *list, const char *path) +{ + int exact_match; + get_entry_index(list, path, &exact_match); + return exact_match; +} + +struct path_list_item *path_list_lookup(const char *path, struct path_list *list) +{ + int exact_match, i = get_entry_index(list, path, &exact_match); + if (!exact_match) + return NULL; + return list->items + i; +} + +void path_list_clear(struct path_list *list, int free_items) +{ + if (list->items) { + int i; + if (free_items) + for (i = 0; i < list->nr; i++) { + if (list->strdup_paths) + free(list->items[i].path); + if (list->items[i].util) + free(list->items[i].util); + } + free(list->items); + } + list->items = NULL; + list->nr = list->alloc = 0; +} + +void print_path_list(const char *text, const struct path_list *p) +{ + int i; + if ( text ) + printf("%s\n", text); + for (i = 0; i < p->nr; i++) + printf("%s:%p\n", p->items[i].path, p->items[i].util); +} + diff --git a/path-list.h b/path-list.h new file mode 100644 index 0000000000..d6401eaa35 --- /dev/null +++ b/path-list.h @@ -0,0 +1,22 @@ +#ifndef _PATH_LIST_H_ +#define _PATH_LIST_H_ + +struct path_list_item { + char *path; + void *util; +}; +struct path_list +{ + struct path_list_item *items; + unsigned int nr, alloc; + unsigned int strdup_paths:1; +}; + +void print_path_list(const char *text, const struct path_list *p); + +int path_list_has_path(const struct path_list *list, const char *path); +void path_list_clear(struct path_list *list, int free_items); +struct path_list_item *path_list_insert(const char *path, struct path_list *list); +struct path_list_item *path_list_lookup(const char *path, struct path_list *list); + +#endif /* _PATH_LIST_H_ */ diff --git a/read-cache.c b/read-cache.c index a50d3612c8..9c0a9fc2a1 100644 --- a/read-cache.c +++ b/read-cache.c @@ -24,6 +24,11 @@ unsigned int active_nr = 0, active_alloc = 0, active_cache_changed = 0; struct cache_tree *active_cache_tree = NULL; +int cache_errno = 0; + +static void *cache_mmap = NULL; +static size_t cache_mmap_size = 0; + /* * This only updates the "non-critical" parts of the directory * cache, ie the parts that aren't tracked by GIT, and only used @@ -577,22 +582,6 @@ int add_cache_entry(struct cache_entry *ce, int option) return 0; } -/* Three functions to allow overloaded pointer return; see linux/err.h */ -static inline void *ERR_PTR(long error) -{ - return (void *) error; -} - -static inline long PTR_ERR(const void *ptr) -{ - return (long) ptr; -} - -static inline long IS_ERR(const void *ptr) -{ - return (unsigned long)ptr > (unsigned long)-1000L; -} - /* * "refresh" does not calculate a new sha1 file or bring the * cache up-to-date for mode/content changes. But what it @@ -604,14 +593,16 @@ static inline long IS_ERR(const void *ptr) * For example, you'd want to do this after doing a "git-read-tree", * to link up the stat cache details with the proper files. */ -static struct cache_entry *refresh_entry(struct cache_entry *ce, int really) +struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really) { struct stat st; struct cache_entry *updated; int changed, size; - if (lstat(ce->name, &st) < 0) - return ERR_PTR(-errno); + if (lstat(ce->name, &st) < 0) { + cache_errno = errno; + return NULL; + } changed = ce_match_stat(ce, &st, really); if (!changed) { @@ -619,11 +610,13 @@ static struct cache_entry *refresh_entry(struct cache_entry *ce, int really) !(ce->ce_flags & htons(CE_VALID))) ; /* mark this one VALID again */ else - return NULL; + return ce; } - if (ce_modified(ce, &st, really)) - return ERR_PTR(-EINVAL); + if (ce_modified(ce, &st, really)) { + cache_errno = EINVAL; + return NULL; + } size = ce_size(ce); updated = xmalloc(size); @@ -666,13 +659,13 @@ int refresh_cache(unsigned int flags) continue; } - new = refresh_entry(ce, really); - if (!new) + new = refresh_cache_entry(ce, really); + if (new == ce) continue; - if (IS_ERR(new)) { - if (not_new && PTR_ERR(new) == -ENOENT) + if (!new) { + if (not_new && cache_errno == ENOENT) continue; - if (really && PTR_ERR(new) == -EINVAL) { + if (really && cache_errno == EINVAL) { /* If we are doing --really-refresh that * means the index is not valid anymore. */ @@ -728,40 +721,44 @@ static int read_index_extension(const char *ext, void *data, unsigned long sz) } int read_cache(void) +{ + return read_cache_from(get_index_file()); +} + +/* remember to discard_cache() before reading a different cache! */ +int read_cache_from(const char *path) { int fd, i; struct stat st; - unsigned long size, offset; - void *map; + unsigned long offset; struct cache_header *hdr; errno = EBUSY; - if (active_cache) + if (cache_mmap) return active_nr; errno = ENOENT; index_file_timestamp = 0; - fd = open(get_index_file(), O_RDONLY); + fd = open(path, O_RDONLY); if (fd < 0) { if (errno == ENOENT) return 0; die("index file open failed (%s)", strerror(errno)); } - size = 0; /* avoid gcc warning */ - map = MAP_FAILED; + cache_mmap = MAP_FAILED; if (!fstat(fd, &st)) { - size = st.st_size; + cache_mmap_size = st.st_size; errno = EINVAL; - if (size >= sizeof(struct cache_header) + 20) - map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + if (cache_mmap_size >= sizeof(struct cache_header) + 20) + cache_mmap = mmap(NULL, cache_mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); } close(fd); - if (map == MAP_FAILED) + if (cache_mmap == MAP_FAILED) die("index file mmap failed (%s)", strerror(errno)); - hdr = map; - if (verify_hdr(hdr, size) < 0) + hdr = cache_mmap; + if (verify_hdr(hdr, cache_mmap_size) < 0) goto unmap; active_nr = ntohl(hdr->hdr_entries); @@ -770,12 +767,12 @@ int read_cache(void) offset = sizeof(*hdr); for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = (struct cache_entry *) ((char *) map + offset); + struct cache_entry *ce = (struct cache_entry *) ((char *) cache_mmap + offset); offset = offset + ce_size(ce); active_cache[i] = ce; } index_file_timestamp = st.st_mtime; - while (offset <= size - 20 - 8) { + while (offset <= cache_mmap_size - 20 - 8) { /* After an array of active_nr index entries, * there can be arbitrary number of extended * sections, each of which is prefixed with @@ -783,10 +780,10 @@ int read_cache(void) * in 4-byte network byte order. */ unsigned long extsize; - memcpy(&extsize, (char *) map + offset + 4, 4); + memcpy(&extsize, (char *) cache_mmap + offset + 4, 4); extsize = ntohl(extsize); - if (read_index_extension(((const char *) map) + offset, - (char *) map + offset + 8, + if (read_index_extension(((const char *) cache_mmap) + offset, + (char *) cache_mmap + offset + 8, extsize) < 0) goto unmap; offset += 8; @@ -795,11 +792,28 @@ int read_cache(void) return active_nr; unmap: - munmap(map, size); + munmap(cache_mmap, cache_mmap_size); errno = EINVAL; die("index file corrupt"); } +int discard_cache() +{ + int ret; + + if (cache_mmap == NULL) + return 0; + ret = munmap(cache_mmap, cache_mmap_size); + cache_mmap = NULL; + cache_mmap_size = 0; + active_nr = active_cache_changed = 0; + index_file_timestamp = 0; + cache_tree_free(&active_cache_tree); + + /* no need to throw away allocated active_cache */ + return ret; +} + #define WRITE_BUFFER_SIZE 8192 static unsigned char write_buffer[WRITE_BUFFER_SIZE]; static unsigned long write_buffer_len; diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh index d34c6cf6f3..b70e177ddd 100755 --- a/t/t3402-rebase-merge.sh +++ b/t/t3402-rebase-merge.sh @@ -51,7 +51,7 @@ test_expect_success setup ' ' test_expect_success 'reference merge' ' - git merge -s recursive "reference merge" HEAD master + git merge -s recur "reference merge" HEAD master ' test_expect_success rebase ' From 06d30f4f3eea71bce4cf48db3ea384976b3983b7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 9 Jul 2006 00:42:26 -0700 Subject: [PATCH 02/21] recur vs recursive: help testing without touching too many stuff. During git-merge-recur development, you could set an environment variable GIT_USE_RECUR_FOR_RECURSIVE to use WIP recur in place of the recursive strategy. Signed-off-by: Junio C Hamano --- TEST | 18 +++++++++++------- git-merge.sh | 13 +++++++++++-- git-rebase.sh | 15 +++++++++++++-- t/t3402-rebase-merge.sh | 2 +- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/TEST b/TEST index d530983474..7286e2a546 100755 --- a/TEST +++ b/TEST @@ -1,10 +1,14 @@ #!/bin/sh -x + cd t || exit -./t3400-rebase.sh "$@" && \ -./t6020-merge-df.sh "$@" && \ -./t3401-rebase-partial.sh "$@" && \ -./t6021-merge-criss-cross.sh "$@" && \ -./t3402-rebase-merge.sh "$@" && \ -./t6022-merge-rename.sh "$@" && \ -./t6010-merge-base.sh "$@" && \ +GIT_USE_RECUR_FOR_RECURSIVE=LetsTryIt +export GIT_USE_RECUR_FOR_RECURSIVE + +./t3400-rebase.sh "$@" && +./t6020-merge-df.sh "$@" && +./t3401-rebase-partial.sh "$@" && +./t6021-merge-criss-cross.sh "$@" && +./t3402-rebase-merge.sh "$@" && +./t6022-merge-rename.sh "$@" && +./t6010-merge-base.sh "$@" && : diff --git a/git-merge.sh b/git-merge.sh index b26ca147c0..9b681159dc 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -9,8 +9,13 @@ USAGE='[-n] [--no-commit] [--squash] [-s ]... < LF=' ' -all_strategies='recur recur octopus resolve stupid ours' -default_twohead_strategies='recur' +all_strategies='recursive recur octopus resolve stupid ours' +case "${GIT_USE_RECUR_FOR_RECURSIVE}" in +'') + default_twohead_strategies=recursive ;; +?*) + default_twohead_strategies=recur ;; +esac default_octopus_strategies='octopus' no_trivial_merge_strategies='ours' use_strategies= @@ -110,6 +115,10 @@ do strategy="$2" shift ;; esac + case "$strategy,${GIT_USE_RECUR_FOR_RECURSIVE}" in + recursive,?*) + strategy=recur ;; + esac case " $all_strategies " in *" $strategy "*) use_strategies="$use_strategies$strategy " ;; diff --git a/git-rebase.sh b/git-rebase.sh index 2a4c8c8a89..8c5da7219e 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -35,7 +35,13 @@ If you would prefer to skip this patch, instead run \"git rebase --skip\". To restore the original branch and stop rebasing run \"git rebase --abort\". " unset newbase -strategy=recur +case "${GIT_USE_RECUR_FOR_RECURSIVE}" in +'') + strategy=recursive ;; +?*) + strategy=recur ;; +esac + do_merge= dotest=$GIT_DIR/.dotest-merge prec=4 @@ -198,6 +204,11 @@ do shift done +case "$strategy,${GIT_USE_RECUR_FOR_RECURSIVE}" in +recursive,?*) + strategy=recur ;; +esac + # Make sure we do not have .dotest if test -z "$do_merge" then @@ -292,7 +303,7 @@ then exit $? fi -if test "@@NO_PYTHON@@" && test "$strategy" = "recur" +if test "@@NO_PYTHON@@" && test "$strategy" = "recursive" then die 'The recursive merge strategy currently relies on Python, which this installation of git was not configured with. Please consider diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh index b70e177ddd..d34c6cf6f3 100755 --- a/t/t3402-rebase-merge.sh +++ b/t/t3402-rebase-merge.sh @@ -51,7 +51,7 @@ test_expect_success setup ' ' test_expect_success 'reference merge' ' - git merge -s recur "reference merge" HEAD master + git merge -s recursive "reference merge" HEAD master ' test_expect_success rebase ' From 3af244caa8297793f29d7422bb19d9da1bf07b5e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 27 Jul 2006 13:17:07 +0200 Subject: [PATCH 03/21] Cumulative update of merge-recursive in C This contains mainly three sorts of fixes: - get rid of small wrapper functions - reuse the diff_filespec structure when sha1, mode & path are needed - Junio's pedantic updates Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 894 +++++++++++++++++++--------------------------- 1 file changed, 366 insertions(+), 528 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index 8d30519db1..d78f58d8d2 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -25,9 +25,9 @@ /*#define DEBUG*/ #ifdef DEBUG -#define debug(args, ...) fprintf(stderr, args, ## __VA_ARGS__) +#define debug(...) fprintf(stderr, __VA_ARGS__) #else -#define debug(args, ...) +#define debug(...) do { ; /* nothing */ } while (0) #endif #ifdef DEBUG @@ -61,13 +61,14 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) fputc('\n', stderr); } -static void ls_files() { +static void ls_files(void) { int i; for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; show_ce_entry("", ce); } fprintf(stderr, "---\n"); + if (0) ls_files(); /* avoid "unused" warning */ } #endif @@ -76,41 +77,6 @@ static void ls_files() { * - (const char *)commit->util set to the name, and * - *(int *)commit->object.sha1 set to the virtual id. */ -static const char *commit_title(struct commit *commit, int *len) -{ - const char *s = "(null commit)"; - *len = strlen(s); - - if ( commit->util ) { - s = commit->util; - *len = strlen(s); - } else { - if ( parse_commit(commit) != 0 ) { - s = "(bad commit)"; - *len = strlen(s); - } else { - s = commit->buffer; - char prev = '\0'; - while ( *s ) { - if ( '\n' == prev && '\n' == *s ) { - ++s; - break; - } - prev = *s++; - } - *len = 0; - while ( s[*len] && '\n' != s[*len] ) - ++(*len); - } - } - return s; -} - -static const char *commit_hex_sha1(const struct commit *commit) -{ - return commit->util ? "virtual" : commit ? - sha1_to_hex(commit->object.sha1) : "undefined"; -} static unsigned commit_list_count(const struct commit_list *l) { @@ -136,41 +102,11 @@ static struct commit *make_virtual_commit(struct tree *tree, const char *comment */ static int sha_eq(const unsigned char *a, const unsigned char *b) { - if ( !a && !b ) + if (!a && !b) return 2; return a && b && memcmp(a, b, 20) == 0; } -static void memswp(void *p1, void *p2, unsigned n) -{ - unsigned char *a = p1, *b = p2; - while ( n-- ) { - *a ^= *b; - *b ^= *a; - *a ^= *b; - ++a; - ++b; - } -} - -/* - * TODO: we should convert the merge_result users to - * int blabla(..., struct commit **result) - * like everywhere else in git. - * Same goes for merge_tree_result and merge_file_info. - */ -struct merge_result -{ - struct commit *commit; - unsigned clean:1; -}; - -struct merge_tree_result -{ - struct tree *tree; - unsigned clean:1; -}; - /* * TODO: check if we can just reuse the active_cache structure: it is already * sorted (by name, stage). @@ -195,7 +131,7 @@ static void output(const char *fmt, ...) { va_list args; int i; - for ( i = output_indent; i--; ) + for (i = output_indent; i--;) fputs(" ", stdout); va_start(args, fmt); vfprintf(stdout, fmt, args); @@ -203,11 +139,37 @@ static void output(const char *fmt, ...) fputc('\n', stdout); } +static void output_commit_title(struct commit *commit) +{ + int i; + for (i = output_indent; i--;) + fputs(" ", stdout); + if (commit->util) + printf("virtual %s\n", (char *)commit->util); + else { + printf("%s ", sha1_to_hex(commit->object.sha1)); + if (parse_commit(commit) != 0) + printf("(bad commit)\n"); + else { + const char *s; + int len; + for (s = commit->buffer; *s; s++) + if (*s == '\n' && s[1] == '\n') { + s += 2; + break; + } + for (len = 0; s[len] && '\n' != s[len]; len++) + ; /* do nothing */ + printf("%.*s\n", len, s); + } + } +} + static const char *original_index_file; static const char *temporary_index_file; static int cache_dirty = 0; -static int flush_cache() +static int flush_cache(void) { /* flush temporary index */ struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); @@ -290,11 +252,12 @@ static int git_read_tree(const struct tree *tree) getenv("GIT_INDEX_FILE"), sha1_to_hex(tree->object.sha1)); #endif + int rc; 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); + rc = run_command_v(2, argv); return rc < 0 ? -1: rc; } @@ -314,6 +277,7 @@ static int git_merge_trees(const char *update_arg, sha1_to_hex(head->object.sha1), sha1_to_hex(merge->object.sha1)); #endif + int rc; const char *argv[] = { "git-read-tree", NULL, "-m", NULL, NULL, NULL, NULL, @@ -324,50 +288,42 @@ static int git_merge_trees(const char *update_arg, argv[3] = sha1_to_hex(common->object.sha1); argv[4] = sha1_to_hex(head->object.sha1); argv[5] = sha1_to_hex(merge->object.sha1); - int rc = run_command_v(6, argv); + rc = run_command_v(6, argv); return rc < 0 ? -1: rc; } /* * TODO: this can be streamlined by refactoring builtin-write-tree.c */ -static struct tree *git_write_tree() +static struct tree *git_write_tree(void) { #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"); + FILE *fp; + int rc; 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' ) + if (cache_dirty) + 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; - int rc = pclose(fp); - if ( rc == -1 || WEXITSTATUS(rc) ) + rc = pclose(fp); + if (rc == -1 || WEXITSTATUS(rc)) return NULL; buf[i] = '\0'; - if ( get_sha1(buf, sha1) != 0 ) + if (get_sha1(buf, sha1) != 0) return NULL; return lookup_tree(sha1); } -/* - * TODO: get rid of files_and_dirs; we do not use it except for - * current_file_set and current_dir_set, which are global already. - */ -static struct -{ - struct path_list *files; - struct path_list *dirs; -} files_and_dirs; - static int save_files_dirs(const unsigned char *sha1, const char *base, int baselen, const char *path, unsigned int mode, int stage) @@ -379,70 +335,36 @@ static int save_files_dirs(const unsigned char *sha1, newpath[baselen + len] = '\0'; if (S_ISDIR(mode)) - path_list_insert(newpath, files_and_dirs.dirs); + path_list_insert(newpath, ¤tDirectorySet); else - path_list_insert(newpath, files_and_dirs.files); + path_list_insert(newpath, ¤tFileSet); free(newpath); return READ_TREE_RECURSIVE; } -static int get_files_dirs(struct tree *tree, - struct path_list *files, - struct path_list *dirs) +static int get_files_dirs(struct tree *tree) { int n; - files_and_dirs.files = files; - files_and_dirs.dirs = dirs; debug("get_files_dirs ...\n"); if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs) != 0) { debug(" get_files_dirs done (0)\n"); return 0; } - n = files->nr + dirs->nr; + n = currentFileSet.nr + currentDirectorySet.nr; debug(" get_files_dirs done (%d)\n", n); return n; } -/* - * TODO: this wrapper is so small, we can use path_list_lookup directly. - * Same goes for index_entry_get(), free_index_entries(), find_rename_bysrc(), - * free_rename_entries(). - */ -static struct stage_data *index_entry_find(struct path_list *ents, - const char *path) -{ - struct path_list_item *item = path_list_lookup(path, ents); - if (item) - return item->util; - return NULL; -} - -static struct stage_data *index_entry_get(struct path_list *ents, - const char *path) -{ - struct path_list_item *item = path_list_lookup(path, ents); - - if (item == NULL) { - item = path_list_insert(path, ents); - item->util = xcalloc(1, sizeof(struct stage_data)); - } - return item->util; -} - -/* - * TODO: since the result of index_entry_from_db() is tucked into a - * path_list anyway, this helper can do that already. - */ /* * Returns a index_entry instance which doesn't have to correspond to * a real cache entry in Git's index. */ -static struct stage_data *index_entry_from_db(const char *path, - struct tree *o, - struct tree *a, - struct tree *b) +static struct stage_data *insert_stage_data(const char *path, + struct tree *o, struct tree *a, struct tree *b, + struct path_list *entries) { + struct path_list_item *item; struct stage_data *e = xcalloc(1, sizeof(struct stage_data)); get_tree_entry(o->object.sha1, path, e->stages[1].sha, &e->stages[1].mode); @@ -450,24 +372,16 @@ static struct stage_data *index_entry_from_db(const char *path, e->stages[2].sha, &e->stages[2].mode); get_tree_entry(b->object.sha1, path, e->stages[3].sha, &e->stages[3].mode); + item = path_list_insert(path, entries); + item->util = e; return e; } -static void free_index_entries(struct path_list **ents) -{ - if (!*ents) - return; - - path_list_clear(*ents, 1); - free(*ents); - *ents = NULL; -} - /* * Create a dictionary mapping file names to CacheEntry objects. The * dictionary contains one entry for every path with a non-zero stage entry. */ -static struct path_list *get_unmerged() +static struct path_list *get_unmerged(void) { struct path_list *unmerged = xcalloc(1, sizeof(struct path_list)); int i; @@ -478,16 +392,22 @@ static struct path_list *get_unmerged() cache_dirty++; } for (i = 0; i < active_nr; i++) { + struct path_list_item *item; + struct stage_data *e; struct cache_entry *ce = active_cache[i]; if (!ce_stage(ce)) continue; - struct stage_data *e = index_entry_get(unmerged, ce->name); + item = path_list_lookup(ce->name, unmerged); + if (!item) { + item = path_list_insert(ce->name, unmerged); + item->util = xcalloc(1, sizeof(struct stage_data)); + } + e = item->util; e->stages[ce_stage(ce)].mode = ntohl(ce->ce_mode); memcpy(e->stages[ce_stage(ce)].sha, ce->sha1, 20); } - debug(" get_unmerged done\n"); return unmerged; } @@ -499,25 +419,6 @@ struct rename unsigned processed:1; }; -static struct rename *find_rename_bysrc(struct path_list *e, - const char *name) -{ - struct path_list_item *item = path_list_lookup(name, e); - if (item) - return item->util; - return NULL; -} - -static void free_rename_entries(struct path_list **list) -{ - if (!*list) - return; - - path_list_clear(*list, 0); - free(*list); - *list = NULL; -} - /* * Get information of all renames which occured between 'oTree' and * 'tree'. We need the three trees in the merge ('oTree', 'aTree' and @@ -530,13 +431,16 @@ static struct path_list *get_renames(struct tree *tree, struct tree *bTree, struct path_list *entries) { + int i; + struct path_list *renames; + struct diff_options opts; #ifdef DEBUG time_t t = time(0); + debug("getRenames ...\n"); #endif - int i; - struct path_list *renames = xcalloc(1, sizeof(struct path_list)); - struct diff_options opts; + + renames = xcalloc(1, sizeof(struct path_list)); diff_setup(&opts); opts.recursive = 1; opts.detect_rename = DIFF_DETECT_RENAME; @@ -546,6 +450,7 @@ static struct path_list *get_renames(struct tree *tree, diff_tree_sha1(oTree->object.sha1, tree->object.sha1, "", &opts); diffcore_std(&opts); for (i = 0; i < diff_queued_diff.nr; ++i) { + struct path_list_item *item; struct rename *re; struct diff_filepair *pair = diff_queued_diff.queue[i]; if (pair->status != 'R') { @@ -555,78 +460,66 @@ static struct path_list *get_renames(struct tree *tree, re = xmalloc(sizeof(*re)); re->processed = 0; re->pair = pair; - re->src_entry = index_entry_find(entries, re->pair->one->path); - /* TODO: should it not be an error, if src_entry was found? */ - if ( !re->src_entry ) { - re->src_entry = index_entry_from_db(re->pair->one->path, - oTree, aTree, bTree); - struct path_list_item *item = - path_list_insert(re->pair->one->path, entries); - item->util = re->src_entry; - } - re->dst_entry = index_entry_find(entries, re->pair->two->path); - if ( !re->dst_entry ) { - re->dst_entry = index_entry_from_db(re->pair->two->path, - oTree, aTree, bTree); - struct path_list_item *item = - path_list_insert(re->pair->two->path, entries); - item->util = re->dst_entry; - } - struct path_list_item *item = path_list_insert(pair->one->path, renames); + item = path_list_lookup(re->pair->one->path, entries); + if (!item) + re->src_entry = insert_stage_data(re->pair->one->path, + oTree, aTree, bTree, entries); + else + re->src_entry = item->util; + + item = path_list_lookup(re->pair->two->path, entries); + if (!item) + re->dst_entry = insert_stage_data(re->pair->two->path, + oTree, aTree, bTree, entries); + else + re->dst_entry = item->util; + item = path_list_insert(pair->one->path, renames); item->util = re; } opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_queued_diff.nr = 0; diff_flush(&opts); +#ifdef DEBUG debug(" getRenames done in %ld\n", time(0)-t); +#endif return renames; } -/* - * TODO: the code would be way nicer, if we had a struct containing just sha1 and mode. - * In this particular case, we might get away reusing stage_data, no? - */ -int update_stages(const char *path, - unsigned char *osha, unsigned omode, - unsigned char *asha, unsigned amode, - unsigned char *bsha, unsigned bmode, - int clear /* =True */) +int update_stages(const char *path, struct diff_filespec *o, + struct diff_filespec *a, struct diff_filespec *b, int clear) { int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE; - if ( clear ) - if (add_cacheinfo(0, null_sha1, path, 0, 0, options)) + if (clear) + if (remove_file_from_cache(path)) return -1; - if ( omode ) - if (add_cacheinfo(omode, osha, path, 1, 0, options)) + if (o) + if (add_cacheinfo(o->mode, o->sha1, path, 1, 0, options)) return -1; - if ( amode ) - if (add_cacheinfo(omode, osha, path, 2, 0, options)) + if (a) + if (add_cacheinfo(a->mode, a->sha1, path, 2, 0, options)) return -1; - if ( bmode ) - if (add_cacheinfo(omode, osha, path, 3, 0, options)) + if (b) + if (add_cacheinfo(b->mode, b->sha1, path, 3, 0, options)) return -1; return 0; } -/* - * TODO: there has to be a function in libgit doing this exact thing. - */ static int remove_path(const char *name) { - int ret; - char *slash; + int ret, len; + char *slash, *dirs; ret = unlink(name); - if ( ret ) + if (ret) return ret; - int len = strlen(name); - char *dirs = malloc(len+1); + len = strlen(name); + dirs = malloc(len+1); memcpy(dirs, name, len); dirs[len] = '\0'; - while ( (slash = strrchr(name, '/')) ) { + while ((slash = strrchr(name, '/'))) { *slash = '\0'; len = slash - name; - if ( rmdir(name) != 0 ) + if (rmdir(name) != 0) break; } free(dirs); @@ -636,7 +529,7 @@ static int remove_path(const char *name) /* General TODO: unC99ify the code: no declaration after code */ /* General TODO: no javaIfiCation: rename updateCache to update_cache */ /* - * TODO: once we no longer call external programs, we'd probably be better of + * 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) @@ -644,17 +537,17 @@ int remove_file(int clean, const char *path) int updateCache = index_only || clean; int updateWd = !index_only; - if ( updateCache ) { + if (updateCache) { if (!cache_dirty) read_cache_from(getenv("GIT_INDEX_FILE")); cache_dirty++; if (remove_file_from_cache(path)) return -1; } - if ( updateWd ) + if (updateWd) { unlink(path); - if ( errno != ENOENT || errno != EISDIR ) + if (errno != ENOENT || errno != EISDIR) return -1; remove_path(path); } @@ -664,55 +557,31 @@ int remove_file(int clean, const char *path) static char *unique_path(const char *path, const char *branch) { char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1); - strcpy(newpath, path); - strcat(newpath, "~"); - char *p = newpath + strlen(newpath); - strcpy(p, branch); - for ( ; *p; ++p ) - if ( '/' == *p ) - *p = '_'; int suffix = 0; struct stat st; - while ( path_list_has_path(¤tFileSet, newpath) || - path_list_has_path(¤tDirectorySet, newpath) || - lstat(newpath, &st) == 0 ) { + char *p = newpath + strlen(newpath); + strcpy(newpath, path); + strcat(newpath, "~"); + strcpy(p, branch); + for (; *p; ++p) + if ('/' == *p) + *p = '_'; + while (path_list_has_path(¤tFileSet, newpath) || + path_list_has_path(¤tDirectorySet, newpath) || + lstat(newpath, &st) == 0) sprintf(p, "_%d", suffix++); - } + path_list_insert(newpath, ¤tFileSet); return newpath; } -/* - * TODO: except for create_last, this so looks like - * safe_create_leading_directories(). - */ -static int mkdir_p(const char *path, unsigned long mode, int create_last) +static int mkdir_p(const char *path, unsigned long mode) { + /* path points to cache entries, so strdup before messing with it */ char *buf = strdup(path); - char *p; - - for ( p = buf; *p; ++p ) { - if ( *p != '/' ) - continue; - *p = '\0'; - if (mkdir(buf, mode)) { - int e = errno; - if ( e == EEXIST ) { - struct stat st; - if ( !stat(buf, &st) && S_ISDIR(st.st_mode) ) - goto next; /* ok */ - errno = e; - } - free(buf); - return -1; - } - next: - *p = '/'; - } + int result = safe_create_leading_directories(buf); free(buf); - if ( create_last && mkdir(path, mode) ) - return -1; - return 0; + return result; } static void flush_buffer(int fd, const char *buf, unsigned long size) @@ -732,17 +601,16 @@ static void flush_buffer(int fd, const char *buf, unsigned long size) } } -/* General TODO: reindent according to guide lines (no if ( blabla )) */ void update_file_flags(const unsigned char *sha, - unsigned mode, - const char *path, - int updateCache, - int updateWd) + unsigned mode, + const char *path, + int update_cache, + int update_wd) { - if ( index_only ) - updateWd = 0; + if (index_only) + update_wd = 0; - if ( updateWd ) { + if (update_wd) { char type[20]; void *buf; unsigned long size; @@ -750,37 +618,38 @@ void update_file_flags(const unsigned char *sha, buf = read_sha1_file(sha, type, &size); if (!buf) die("cannot read object %s '%s'", sha1_to_hex(sha), path); - if ( strcmp(type, blob_type) != 0 ) + if (strcmp(type, blob_type) != 0) die("blob expected for %s '%s'", sha1_to_hex(sha), path); - if ( S_ISREG(mode) ) { - if ( mkdir_p(path, 0777, 0 /* don't create last element */) ) + if (S_ISREG(mode)) { + int fd; + if (mkdir_p(path, 0777)) die("failed to create path %s: %s", path, strerror(errno)); unlink(path); - if ( mode & 0100 ) + if (mode & 0100) mode = 0777; else mode = 0666; - int fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); - if ( fd < 0 ) + fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); + if (fd < 0) die("failed to open %s: %s", path, strerror(errno)); flush_buffer(fd, buf, size); close(fd); - } else if ( S_ISLNK(mode) ) { - char *linkTarget = malloc(size + 1); - memcpy(linkTarget, buf, size); - linkTarget[size] = '\0'; - mkdir_p(path, 0777, 0); - symlink(linkTarget, path); + } else if (S_ISLNK(mode)) { + char *lnk = malloc(size + 1); + memcpy(lnk, buf, size); + lnk[size] = '\0'; + mkdir_p(path, 0777); + unlink(lnk); + symlink(lnk, path); } else die("do not know what to do with %06o %s '%s'", mode, sha1_to_hex(sha), path); } - if ( updateCache ) - add_cacheinfo(mode, sha, path, 0, updateWd, ADD_CACHE_OK_TO_ADD); + if (update_cache) + add_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD); } -/* TODO: is this often used? if not, do direct call */ void update_file(int clean, const unsigned char *sha, unsigned mode, @@ -819,64 +688,53 @@ static char *git_unpack_file(const unsigned char *sha1, char *path) return path; } -/* - * TODO: the signature would be much more efficient using stage_data - */ -static struct merge_file_info merge_file(const char *oPath, - const unsigned char *oSha, - unsigned oMode, - const char *aPath, - const unsigned char *aSha, - unsigned aMode, - const char *bPath, - const unsigned char *bSha, - unsigned bMode, - const char *branch1Name, - const char *branch2Name) +static struct merge_file_info merge_file(struct diff_filespec *o, + struct diff_filespec *a, struct diff_filespec *b, + const char *branch1Name, const char *branch2Name) { struct merge_file_info result; result.merge = 0; result.clean = 1; - if ( (S_IFMT & aMode) != (S_IFMT & bMode) ) { + if ((S_IFMT & a->mode) != (S_IFMT & b->mode)) { result.clean = 0; - if ( S_ISREG(aMode) ) { - result.mode = aMode; - memcpy(result.sha, aSha, 20); + if (S_ISREG(a->mode)) { + result.mode = a->mode; + memcpy(result.sha, a->sha1, 20); } else { - result.mode = bMode; - memcpy(result.sha, bSha, 20); + result.mode = b->mode; + memcpy(result.sha, b->sha1, 20); } } else { - if ( memcmp(aSha, oSha, 20) != 0 && memcmp(bSha, oSha, 20) != 0 ) + if (!sha_eq(a->sha1, o->sha1) && !sha_eq(b->sha1, o->sha1)) result.merge = 1; - result.mode = aMode == oMode ? bMode: aMode; + result.mode = a->mode == o->mode ? b->mode: a->mode; - if ( memcmp(aSha, oSha, 20) == 0 ) - memcpy(result.sha, bSha, 20); - else if ( memcmp(bSha, oSha, 20) == 0 ) - memcpy(result.sha, aSha, 20); - else if ( S_ISREG(aMode) ) { - - int code = 1; + if (sha_eq(a->sha1, o->sha1)) + memcpy(result.sha, b->sha1, 20); + else if (sha_eq(b->sha1, o->sha1)) + memcpy(result.sha, a->sha1, 20); + else if (S_ISREG(a->mode)) { + int code = 1, fd; + struct stat st; char orig[PATH_MAX]; char src1[PATH_MAX]; char src2[PATH_MAX]; - - git_unpack_file(oSha, orig); - git_unpack_file(aSha, src1); - git_unpack_file(bSha, src2); - const char *argv[] = { "merge", "-L", NULL, "-L", NULL, "-L", NULL, src1, orig, src2, NULL }; char *la, *lb, *lo; - argv[2] = la = strdup(mkpath("%s/%s", branch1Name, aPath)); - argv[6] = lb = strdup(mkpath("%s/%s", branch2Name, bPath)); - argv[4] = lo = strdup(mkpath("orig/%s", oPath)); + + git_unpack_file(o->sha1, orig); + git_unpack_file(a->sha1, src1); + git_unpack_file(b->sha1, src2); + + argv[2] = la = strdup(mkpath("%s/%s", branch1Name, a->path)); + argv[6] = lb = strdup(mkpath("%s/%s", branch2Name, b->path)); + argv[4] = lo = strdup(mkpath("orig/%s", o->path)); #if 0 printf("%s %s %s %s %s %s %s %s %s %s\n", @@ -888,17 +746,15 @@ static struct merge_file_info merge_file(const char *oPath, free(la); free(lb); free(lo); - if ( code && code < -256 ) { + if (code && code < -256) { die("Failed to execute 'merge'. merge(1) is used as the " "file-level merge tool. Is 'merge' in your path?"); } - struct stat st; - int fd = open(src1, O_RDONLY); + fd = open(src1, O_RDONLY); if (fd < 0 || fstat(fd, &st) < 0 || index_fd(result.sha, fd, &st, 1, "blob")) die("Unable to add %s to database", src1); - close(fd); unlink(orig); unlink(src1); @@ -906,12 +762,12 @@ static struct merge_file_info merge_file(const char *oPath, result.clean = WEXITSTATUS(code) == 0; } else { - if ( !(S_ISLNK(aMode) || S_ISLNK(bMode)) ) + if (!(S_ISLNK(a->mode) || S_ISLNK(b->mode))) die("cannot merge modes?"); - memcpy(result.sha, aSha, 20); + memcpy(result.sha, a->sha1, 20); - if ( memcmp(aSha, bSha, 20) != 0 ) + if (!sha_eq(a->sha1, b->sha1)) result.clean = 0; } } @@ -942,17 +798,9 @@ static void conflict_rename_rename(struct rename *ren1, ren2_dst, branch1, dstName2); remove_file(0, ren2_dst); } - update_stages(dstName1, - NULL, 0, - ren1->pair->two->sha1, ren1->pair->two->mode, - NULL, 0, - 1 /* clear */); - update_stages(dstName2, - NULL, 0, - NULL, 0, - ren2->pair->two->sha1, ren2->pair->two->mode, - 1 /* clear */); - while ( delp-- ) + update_stages(dstName1, NULL, ren1->pair->two, NULL, 1); + update_stages(dstName2, NULL, NULL, ren2->pair->two, 1); + while (delp--) free(del[delp]); } @@ -989,66 +837,73 @@ static int process_renames(struct path_list *renamesA, const char *branchNameA, const char *branchNameB) { - int cleanMerge = 1, i; - struct path_list srcNames = {NULL, 0, 0, 0}, byDstA = {NULL, 0, 0, 0}, byDstB = {NULL, 0, 0, 0}; + int cleanMerge = 1, i, j; + struct path_list byDstA = {NULL, 0, 0, 0}, byDstB = {NULL, 0, 0, 0}; const struct rename *sre; - /* - * TODO: think about a saner way to do this. - * Since both renamesA and renamesB are sorted, it should - * be much more efficient to traverse both simultaneously, - * only byDstA and byDstB should be needed. - */ - debug("processRenames...\n"); for (i = 0; i < renamesA->nr; i++) { sre = renamesA->items[i].util; - path_list_insert(sre->pair->one->path, &srcNames); path_list_insert(sre->pair->two->path, &byDstA)->util = sre->dst_entry; } for (i = 0; i < renamesB->nr; i++) { sre = renamesB->items[i].util; - path_list_insert(sre->pair->one->path, &srcNames); path_list_insert(sre->pair->two->path, &byDstB)->util = sre->dst_entry; } - for (i = 0; i < srcNames.nr; i++) { - char *src = srcNames.items[i].path; + for (i = 0, j = 0; i < renamesA->nr || j < renamesB->nr;) { + int compare; + char *src; struct path_list *renames1, *renames2, *renames2Dst; - struct rename *ren1, *ren2; + struct rename *ren1 = NULL, *ren2 = NULL; const char *branchName1, *branchName2; - ren1 = find_rename_bysrc(renamesA, src); - ren2 = find_rename_bysrc(renamesB, src); + const char *ren1_src, *ren1_dst; + + if (i >= renamesA->nr) { + compare = 1; + ren2 = renamesB->items[j++].util; + } else if (j >= renamesB->nr) { + compare = -1; + ren1 = renamesA->items[i++].util; + } else { + compare = strcmp(renamesA->items[i].path, + renamesB->items[j].path); + ren1 = renamesA->items[i++].util; + ren2 = renamesB->items[j++].util; + } + /* TODO: refactor, so that 1/2 are not needed */ - if ( ren1 ) { + if (ren1) { renames1 = renamesA; renames2 = renamesB; renames2Dst = &byDstB; branchName1 = branchNameA; branchName2 = branchNameB; } else { + struct rename *tmp; renames1 = renamesB; renames2 = renamesA; renames2Dst = &byDstA; branchName1 = branchNameB; branchName2 = branchNameA; - struct rename *tmp = ren2; + tmp = ren2; ren2 = ren1; ren1 = tmp; } + src = ren1->pair->one->path; ren1->dst_entry->processed = 1; ren1->src_entry->processed = 1; - if ( ren1->processed ) + if (ren1->processed) continue; ren1->processed = 1; - const char *ren1_src = ren1->pair->one->path; - const char *ren1_dst = ren1->pair->two->path; + ren1_src = ren1->pair->one->path; + ren1_dst = ren1->pair->two->path; - if ( ren2 ) { + if (ren2) { const char *ren2_src = ren2->pair->one->path; const char *ren2_dst = ren2->pair->two->path; /* Renamed in 1 and renamed in 2 */ @@ -1067,57 +922,48 @@ static int process_renames(struct path_list *renamesA, } else { remove_file(1, ren1_src); struct merge_file_info mfi; - mfi = merge_file(ren1_src, - ren1->pair->one->sha1, - ren1->pair->one->mode, - ren1_dst, - ren1->pair->two->sha1, - ren1->pair->two->mode, - ren2_dst, - ren2->pair->two->sha1, - ren2->pair->two->mode, + mfi = merge_file(ren1->pair->one, + ren1->pair->two, + ren2->pair->two, branchName1, branchName2); - if ( mfi.merge || !mfi.clean ) + if (mfi.merge || !mfi.clean) output("Renaming %s->%s", src, ren1_dst); - if ( mfi.merge ) + if (mfi.merge) output("Auto-merging %s", ren1_dst); - if ( !mfi.clean ) { + if (!mfi.clean) { output("CONFLICT (content): merge conflict in %s", ren1_dst); cleanMerge = 0; - if ( !index_only ) + if (!index_only) update_stages(ren1_dst, - ren1->pair->one->sha1, - ren1->pair->one->mode, - ren1->pair->two->sha1, - ren1->pair->two->mode, - ren2->pair->two->sha1, - ren2->pair->two->mode, + ren1->pair->one, + ren1->pair->two, + ren2->pair->two, 1 /* clear */); } update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst); } } else { /* Renamed in 1, maybe changed in 2 */ + struct path_list_item *item; + /* we only use sha1 and mode of these */ + struct diff_filespec src_other, dst_other; + int tryMerge, stage = renamesA == renames1 ? 3: 2; + remove_file(1, ren1_src); - unsigned char srcShaOtherBranch[20], dstShaOtherBranch[20]; - unsigned srcModeOtherBranch, dstModeOtherBranch; + memcpy(src_other.sha1, + ren1->src_entry->stages[stage].sha, 20); + src_other.mode = ren1->src_entry->stages[stage].mode; + memcpy(dst_other.sha1, + ren1->dst_entry->stages[stage].sha, 20); + dst_other.mode = ren1->dst_entry->stages[stage].mode; - int stage = renamesA == renames1 ? 3: 2; - - memcpy(srcShaOtherBranch, ren1->src_entry->stages[stage].sha, 20); - srcModeOtherBranch = ren1->src_entry->stages[stage].mode; - - memcpy(dstShaOtherBranch, ren1->dst_entry->stages[stage].sha, 20); - dstModeOtherBranch = ren1->dst_entry->stages[stage].mode; - - int tryMerge = 0; - char *newPath; + tryMerge = 0; if (path_list_has_path(¤tDirectorySet, ren1_dst)) { cleanMerge = 0; @@ -1126,14 +972,15 @@ static int process_renames(struct path_list *renamesA, ren1_src, ren1_dst, branchName1, ren1_dst, branchName2); conflict_rename_dir(ren1, branchName1); - } else if ( memcmp(srcShaOtherBranch, null_sha1, 20) == 0 ) { + } else if (sha_eq(src_other.sha1, null_sha1)) { cleanMerge = 0; output("CONFLICT (rename/delete): Rename %s->%s in %s " "and deleted in %s", ren1_src, ren1_dst, branchName1, branchName2); update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst); - } else if ( memcmp(dstShaOtherBranch, null_sha1, 20) != 0 ) { + } else if (!sha_eq(dst_other.sha1, null_sha1)) { + const char *newPath; cleanMerge = 0; tryMerge = 1; output("CONFLICT (rename/add): Rename %s->%s in %s. " @@ -1142,8 +989,9 @@ static int process_renames(struct path_list *renamesA, ren1_dst, branchName2); newPath = unique_path(ren1_dst, branchName2); output("Adding as %s instead", newPath); - update_file(0, dstShaOtherBranch, dstModeOtherBranch, newPath); - } else if ( (ren2 = find_rename_bysrc(renames2Dst, ren1_dst)) ) { + update_file(0, dst_other.sha1, dst_other.mode, newPath); + } else if ((item = path_list_lookup(ren1_dst, renames2Dst))) { + ren2 = item->util; cleanMerge = 0; ren2->processed = 1; output("CONFLICT (rename/rename): Rename %s->%s in %s. " @@ -1154,53 +1002,41 @@ static int process_renames(struct path_list *renamesA, } else tryMerge = 1; - if ( tryMerge ) { - const char *oname = ren1_src; - const char *aname = ren1_dst; - const char *bname = ren1_src; - unsigned char osha[20], asha[20], bsha[20]; - unsigned omode = ren1->pair->one->mode; - unsigned amode = ren1->pair->two->mode; - unsigned bmode = srcModeOtherBranch; - memcpy(osha, ren1->pair->one->sha1, 20); - memcpy(asha, ren1->pair->two->sha1, 20); - memcpy(bsha, srcShaOtherBranch, 20); - const char *aBranch = branchName1; - const char *bBranch = branchName2; - - if ( renamesA != renames1 ) { - memswp(&aname, &bname, sizeof(aname)); - memswp(asha, bsha, 20); - memswp(&aBranch, &bBranch, sizeof(aBranch)); - } + if (tryMerge) { + struct diff_filespec *o, *a, *b; struct merge_file_info mfi; - mfi = merge_file(oname, osha, omode, - aname, asha, amode, - bname, bsha, bmode, - aBranch, bBranch); + src_other.path = (char *)ren1_src; - if ( mfi.merge || !mfi.clean ) + o = ren1->pair->one; + if (renamesA == renames1) { + a = ren1->pair->two; + b = &src_other; + } else { + b = ren1->pair->two; + a = &src_other; + } + mfi = merge_file(o, a, b, + branchNameA, branchNameB); + + if (mfi.merge || !mfi.clean) output("Renaming %s => %s", ren1_src, ren1_dst); - if ( mfi.merge ) + if (mfi.merge) output("Auto-merging %s", ren1_dst); - if ( !mfi.clean ) { + if (!mfi.clean) { output("CONFLICT (rename/modify): Merge conflict in %s", ren1_dst); cleanMerge = 0; - if ( !index_only ) + if (!index_only) update_stages(ren1_dst, - osha, omode, - asha, amode, - bsha, bmode, - 1 /* clear */); + o, a, b, 1); } update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst); } } } - path_list_clear(&srcNames, 0); - debug(" processRenames done\n"); + path_list_clear(&byDstA, 0); + path_list_clear(&byDstB, 0); if (cache_dirty) flush_cache(); @@ -1229,20 +1065,20 @@ static int process_entry(const char *path, struct stage_data *entry, unsigned aMode = entry->stages[2].mode; unsigned bMode = entry->stages[3].mode; - if ( oSha && (!aSha || !bSha) ) { + if (oSha && (!aSha || !bSha)) { /* Case A: Deleted in one */ - if ( (!aSha && !bSha) || - (sha_eq(aSha, oSha) && !bSha) || - (!aSha && sha_eq(bSha, oSha)) ) { + if ((!aSha && !bSha) || + (sha_eq(aSha, oSha) && !bSha) || + (!aSha && sha_eq(bSha, oSha))) { /* Deleted in both or deleted in one and * unchanged in the other */ - if ( aSha ) + if (aSha) output("Removing %s", path); remove_file(1, path); } else { /* Deleted in one and changed in the other */ cleanMerge = 0; - if ( !aSha ) { + if (!aSha) { output("CONFLICT (delete/modify): %s deleted in %s " "and modified in %s. Version %s of %s left in tree.", path, branch1Name, @@ -1257,8 +1093,8 @@ static int process_entry(const char *path, struct stage_data *entry, } } - } else if ( (!oSha && aSha && !bSha) || - (!oSha && !aSha && bSha) ) { + } else if ((!oSha && aSha && !bSha) || + (!oSha && !aSha && bSha)) { /* Case B: Added in one. */ const char *addBranch; const char *otherBranch; @@ -1266,7 +1102,7 @@ static int process_entry(const char *path, struct stage_data *entry, const unsigned char *sha; const char *conf; - if ( aSha ) { + if (aSha) { addBranch = branch1Name; otherBranch = branch2Name; mode = aMode; @@ -1279,9 +1115,9 @@ static int process_entry(const char *path, struct stage_data *entry, sha = bSha; conf = "directory/file"; } - if ( path_list_has_path(¤tDirectorySet, path) ) { - cleanMerge = 0; + if (path_list_has_path(¤tDirectorySet, path)) { const char *newPath = unique_path(path, addBranch); + cleanMerge = 0; output("CONFLICT (%s): There is a directory with name %s in %s. " "Adding %s as %s", conf, path, otherBranch, path, newPath); @@ -1291,10 +1127,10 @@ static int process_entry(const char *path, struct stage_data *entry, output("Adding %s", path); update_file(1, sha, mode, path); } - } else if ( !oSha && aSha && bSha ) { + } else if (!oSha && aSha && bSha) { /* Case C: Added in both (check for same permissions). */ - if ( sha_eq(aSha, bSha) ) { - if ( aMode != bMode ) { + if (sha_eq(aSha, bSha)) { + if (aMode != bMode) { cleanMerge = 0; output("CONFLICT: File %s added identically in both branches, " "but permissions conflict %06o->%06o", @@ -1306,9 +1142,10 @@ static int process_entry(const char *path, struct stage_data *entry, assert(0 && "This case must be handled by git-read-tree"); } } else { + const char *newPath1, *newPath2; cleanMerge = 0; - const char *newPath1 = unique_path(path, branch1Name); - const char *newPath2 = unique_path(path, branch2Name); + newPath1 = unique_path(path, branch1Name); + newPath2 = unique_path(path, branch2Name); output("CONFLICT (add/add): File %s added non-identically " "in both branches. Adding as %s and %s instead.", path, newPath1, newPath2); @@ -1317,22 +1154,30 @@ static int process_entry(const char *path, struct stage_data *entry, update_file(0, bSha, bMode, newPath2); } - } else if ( oSha && aSha && bSha ) { + } else if (oSha && aSha && bSha) { /* case D: Modified in both, but differently. */ - output("Auto-merging %s", path); struct merge_file_info mfi; - mfi = merge_file(path, oSha, oMode, - path, aSha, aMode, - path, bSha, bMode, + struct diff_filespec o, a, b; + + output("Auto-merging %s", path); + o.path = a.path = b.path = (char *)path; + memcpy(o.sha1, oSha, 20); + o.mode = oMode; + memcpy(a.sha1, aSha, 20); + a.mode = aMode; + memcpy(b.sha1, bSha, 20); + b.mode = bMode; + + mfi = merge_file(&o, &a, &b, branch1Name, branch2Name); - if ( mfi.clean ) + if (mfi.clean) update_file(1, mfi.sha, mfi.mode, path); else { cleanMerge = 0; output("CONFLICT (content): Merge conflict in %s", path); - if ( index_only ) + if (index_only) update_file(0, mfi.sha, mfi.mode, path); else update_file_flags(mfi.sha, mfi.mode, path, @@ -1347,73 +1192,68 @@ static int process_entry(const char *path, struct stage_data *entry, return cleanMerge; } -static struct merge_tree_result merge_trees(struct tree *head, - struct tree *merge, - struct tree *common, - const char *branch1Name, - const char *branch2Name) +static int merge_trees(struct tree *head, + struct tree *merge, + struct tree *common, + const char *branch1Name, + const char *branch2Name, + struct tree **result) { - int code; - struct merge_tree_result result = { NULL, 0 }; - if ( !memcmp(common->object.sha1, merge->object.sha1, 20) ) { + int code, clean; + if (sha_eq(common->object.sha1, merge->object.sha1)) { output("Already uptodate!"); - result.tree = head; - result.clean = 1; - return result; + *result = head; + return 1; } - debug("merge_trees ...\n"); code = git_merge_trees(index_only ? "-i": "-u", common, head, merge); - if ( code != 0 ) + if (code != 0) die("merging of trees %s and %s failed", sha1_to_hex(head->object.sha1), sha1_to_hex(merge->object.sha1)); - result.tree = git_write_tree(); + *result = git_write_tree(); - if ( !result.tree ) { + if (!*result) { + struct path_list *entries, *re_head, *re_merge; + int i; path_list_clear(¤tFileSet, 1); path_list_clear(¤tDirectorySet, 1); - get_files_dirs(head, ¤tFileSet, ¤tDirectorySet); - get_files_dirs(merge, ¤tFileSet, ¤tDirectorySet); + get_files_dirs(head); + get_files_dirs(merge); - struct path_list *entries = get_unmerged(); - struct path_list *re_head, *re_merge; + entries = get_unmerged(); re_head = get_renames(head, common, head, merge, entries); re_merge = get_renames(merge, common, head, merge, entries); - result.clean = process_renames(re_head, re_merge, - branch1Name, branch2Name); - debug("\tprocessing entries...\n"); - int i; + clean = process_renames(re_head, re_merge, + branch1Name, branch2Name); for (i = 0; i < entries->nr; i++) { const char *path = entries->items[i].path; struct stage_data *e = entries->items[i].util; if (e->processed) continue; if (!process_entry(path, e, branch1Name, branch2Name)) - result.clean = 0; + clean = 0; } - free_rename_entries(&re_merge); - free_rename_entries(&re_head); - free_index_entries(&entries); + path_list_clear(re_merge, 0); + path_list_clear(re_head, 0); + path_list_clear(entries, 1); - if (result.clean || index_only) - result.tree = git_write_tree(); + if (clean || index_only) + *result = git_write_tree(); else - result.tree = NULL; - debug("\t processing entries done\n"); + *result = NULL; } else { - result.clean = 1; + clean = 1; printf("merging of trees %s and %s resulted in %s\n", sha1_to_hex(head->object.sha1), sha1_to_hex(merge->object.sha1), - sha1_to_hex(result.tree->object.sha1)); + sha1_to_hex((*result)->object.sha1)); } - debug(" merge_trees done\n"); - return result; + return clean; } /* @@ -1421,76 +1261,75 @@ static struct merge_tree_result merge_trees(struct tree *head, * commit object and a flag indicating the cleaness of the merge. */ static -struct merge_result merge(struct commit *h1, +int merge(struct commit *h1, struct commit *h2, const char *branch1Name, const char *branch2Name, int callDepth /* =0 */, - struct commit *ancestor /* =None */) + struct commit *ancestor /* =None */, + struct commit **result) { - struct merge_result result = { NULL, 0 }; - const char *msg; - int msglen; struct commit_list *ca = NULL, *iter; struct commit *mergedCA; - struct merge_tree_result mtr; + struct tree *mrtree; + int clean; output("Merging:"); - msg = commit_title(h1, &msglen); - /* TODO: refactor. we always show the sha1 with the title */ - output("%s %.*s", commit_hex_sha1(h1), msglen, msg); - msg = commit_title(h2, &msglen); - output("%s %.*s", commit_hex_sha1(h2), msglen, msg); + output_commit_title(h1); + output_commit_title(h2); - if ( ancestor ) + if (ancestor) commit_list_insert(ancestor, &ca); else ca = get_merge_bases(h1, h2, 1); output("found %u common ancestor(s):", commit_list_count(ca)); - for (iter = ca; iter; iter = iter->next) { - msg = commit_title(iter->item, &msglen); - output("%s %.*s", commit_hex_sha1(iter->item), msglen, msg); - } + for (iter = ca; iter; iter = iter->next) + output_commit_title(iter->item); mergedCA = pop_commit(&ca); - /* TODO: what happens when merge with virtual commits fails? */ for (iter = ca; iter; iter = iter->next) { output_indent = callDepth + 1; - result = merge(mergedCA, iter->item, - "Temporary merge branch 1", - "Temporary merge branch 2", - callDepth + 1, - NULL); - mergedCA = result.commit; + /* + * When the merge fails, the result contains files + * with conflict markers. The cleanness flag is + * ignored, it was never acutally used, as result of + * merge_trees has always overwritten it: the commited + * "conflicts" were already resolved. + */ + merge(mergedCA, iter->item, + "Temporary merge branch 1", + "Temporary merge branch 2", + callDepth + 1, + NULL, + &mergedCA); output_indent = callDepth; - if ( !mergedCA ) + if (!mergedCA) die("merge returned no commit"); } - if ( callDepth == 0 ) { - setup_index(0); + if (callDepth == 0) { + setup_index(0 /* $GIT_DIR/index */); index_only = 0; } else { - setup_index(1); + setup_index(1 /* temporary index */); git_read_tree(h1->tree); index_only = 1; } - mtr = merge_trees(h1->tree, h2->tree, - mergedCA->tree, branch1Name, branch2Name); + clean = merge_trees(h1->tree, h2->tree, mergedCA->tree, + branch1Name, branch2Name, &mrtree); - if ( !ancestor && (mtr.clean || index_only) ) { - result.commit = make_virtual_commit(mtr.tree, "merged tree"); - commit_list_insert(h1, &result.commit->parents); - commit_list_insert(h2, &result.commit->parents->next); + if (!ancestor && (clean || index_only)) { + *result = make_virtual_commit(mrtree, "merged tree"); + commit_list_insert(h1, &(*result)->parents); + commit_list_insert(h2, &(*result)->parents->next); } else - result.commit = NULL; + *result = NULL; - result.clean = mtr.clean; - return result; + return clean; } static struct commit *get_ref(const char *ref) @@ -1512,6 +1351,9 @@ int main(int argc, char *argv[]) { static const char *bases[2]; static unsigned bases_count = 0; + int i, clean; + const char *branch1, *branch2; + struct commit *result, *h1, *h2; original_index_file = getenv("GIT_INDEX_FILE"); @@ -1523,7 +1365,6 @@ int main(int argc, char *argv[]) if (argc < 4) die("Usage: %s ... -- ...\n", argv[0]); - int i; for (i = 1; i < argc; ++i) { if (!strcmp(argv[i], "--")) break; @@ -1533,26 +1374,23 @@ int main(int argc, char *argv[]) if (argc - i != 3) /* "--" "" "" */ die("Not handling anything other than two heads merge."); - const char *branch1, *branch2; - branch1 = argv[++i]; branch2 = argv[++i]; printf("Merging %s with %s\n", branch1, branch2); - struct merge_result result; - struct commit *h1 = get_ref(branch1); - struct commit *h2 = get_ref(branch2); + h1 = get_ref(branch1); + h2 = get_ref(branch2); if (bases_count == 1) { struct commit *ancestor = get_ref(bases[0]); - result = merge(h1, h2, branch1, branch2, 0, ancestor); + clean = merge(h1, h2, branch1, branch2, 0, ancestor, &result); } else - result = merge(h1, h2, branch1, branch2, 0, NULL); + clean = merge(h1, h2, branch1, branch2, 0, NULL, &result); if (cache_dirty) flush_cache(); - return result.clean ? 0: 1; + return clean ? 0: 1; } /* From 5a753613406286962df514db21094afc99525989 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 27 Jul 2006 19:12:29 +0200 Subject: [PATCH 04/21] merge-recur: Convert variable names to lower_case Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 378 +++++++++++++++++++++++----------------------- 1 file changed, 188 insertions(+), 190 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index d78f58d8d2..5d20f9efce 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -122,8 +122,8 @@ struct stage_data unsigned processed:1; }; -static struct path_list currentFileSet = {NULL, 0, 0, 1}; -static struct path_list currentDirectorySet = {NULL, 0, 0, 1}; +static struct path_list current_file_set = {NULL, 0, 0, 1}; +static struct path_list current_directory_set = {NULL, 0, 0, 1}; static int output_indent = 0; @@ -335,9 +335,9 @@ static int save_files_dirs(const unsigned char *sha1, newpath[baselen + len] = '\0'; if (S_ISDIR(mode)) - path_list_insert(newpath, ¤tDirectorySet); + path_list_insert(newpath, ¤t_directory_set); else - path_list_insert(newpath, ¤tFileSet); + path_list_insert(newpath, ¤t_file_set); free(newpath); return READ_TREE_RECURSIVE; @@ -351,7 +351,7 @@ static int get_files_dirs(struct tree *tree) debug(" get_files_dirs done (0)\n"); return 0; } - n = currentFileSet.nr + currentDirectorySet.nr; + n = current_file_set.nr + current_directory_set.nr; debug(" get_files_dirs done (%d)\n", n); return n; } @@ -378,7 +378,7 @@ static struct stage_data *insert_stage_data(const char *path, } /* - * Create a dictionary mapping file names to CacheEntry objects. The + * Create a dictionary mapping file names to stage_data objects. The * dictionary contains one entry for every path with a non-zero stage entry. */ static struct path_list *get_unmerged(void) @@ -420,15 +420,15 @@ struct rename }; /* - * Get information of all renames which occured between 'oTree' and - * 'tree'. We need the three trees in the merge ('oTree', 'aTree' and - * 'bTree') to be able to associate the correct cache entries with - * the rename information. 'tree' is always equal to either aTree or bTree. + * Get information of all renames which occured between 'o_tree' and + * 'tree'. We need the three trees in the merge ('o_tree', 'a_tree' and + * 'b_tree') to be able to associate the correct cache entries with + * the rename information. 'tree' is always equal to either a_tree or b_tree. */ static struct path_list *get_renames(struct tree *tree, - struct tree *oTree, - struct tree *aTree, - struct tree *bTree, + struct tree *o_tree, + struct tree *a_tree, + struct tree *b_tree, struct path_list *entries) { int i; @@ -437,7 +437,7 @@ static struct path_list *get_renames(struct tree *tree, #ifdef DEBUG time_t t = time(0); - debug("getRenames ...\n"); + debug("get_renames ...\n"); #endif renames = xcalloc(1, sizeof(struct path_list)); @@ -447,7 +447,7 @@ static struct path_list *get_renames(struct tree *tree, opts.output_format = DIFF_FORMAT_NO_OUTPUT; if (diff_setup_done(&opts) < 0) die("diff setup failed"); - diff_tree_sha1(oTree->object.sha1, tree->object.sha1, "", &opts); + diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts); diffcore_std(&opts); for (i = 0; i < diff_queued_diff.nr; ++i) { struct path_list_item *item; @@ -463,14 +463,14 @@ static struct path_list *get_renames(struct tree *tree, item = path_list_lookup(re->pair->one->path, entries); if (!item) re->src_entry = insert_stage_data(re->pair->one->path, - oTree, aTree, bTree, entries); + o_tree, a_tree, b_tree, entries); else re->src_entry = item->util; item = path_list_lookup(re->pair->two->path, entries); if (!item) re->dst_entry = insert_stage_data(re->pair->two->path, - oTree, aTree, bTree, entries); + o_tree, a_tree, b_tree, entries); else re->dst_entry = item->util; item = path_list_insert(pair->one->path, renames); @@ -480,7 +480,7 @@ static struct path_list *get_renames(struct tree *tree, diff_queued_diff.nr = 0; diff_flush(&opts); #ifdef DEBUG - debug(" getRenames done in %ld\n", time(0)-t); + debug(" get_renames done in %ld\n", time(0)-t); #endif return renames; } @@ -526,25 +526,23 @@ static int remove_path(const char *name) return ret; } -/* General TODO: unC99ify the code: no declaration after code */ -/* General TODO: no javaIfiCation: rename updateCache to update_cache */ /* * 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 updateCache = index_only || clean; - int updateWd = !index_only; + int update_cache = index_only || clean; + int update_working_directory = !index_only; - if (updateCache) { + if (update_cache) { if (!cache_dirty) read_cache_from(getenv("GIT_INDEX_FILE")); cache_dirty++; if (remove_file_from_cache(path)) return -1; } - if (updateWd) + if (update_working_directory) { unlink(path); if (errno != ENOENT || errno != EISDIR) @@ -566,12 +564,12 @@ static char *unique_path(const char *path, const char *branch) for (; *p; ++p) if ('/' == *p) *p = '_'; - while (path_list_has_path(¤tFileSet, newpath) || - path_list_has_path(¤tDirectorySet, newpath) || + while (path_list_has_path(¤t_file_set, newpath) || + path_list_has_path(¤t_directory_set, newpath) || lstat(newpath, &st) == 0) sprintf(p, "_%d", suffix++); - path_list_insert(newpath, ¤tFileSet); + path_list_insert(newpath, ¤t_file_set); return newpath; } @@ -784,22 +782,22 @@ static void conflict_rename_rename(struct rename *ren1, int delp = 0; const char *ren1_dst = ren1->pair->two->path; const char *ren2_dst = ren2->pair->two->path; - const char *dstName1 = ren1_dst; - const char *dstName2 = ren2_dst; - if (path_list_has_path(¤tDirectorySet, ren1_dst)) { - dstName1 = del[delp++] = unique_path(ren1_dst, branch1); + const char *dst_name1 = ren1_dst; + const char *dst_name2 = ren2_dst; + if (path_list_has_path(¤t_directory_set, ren1_dst)) { + dst_name1 = del[delp++] = unique_path(ren1_dst, branch1); output("%s is a directory in %s adding as %s instead", - ren1_dst, branch2, dstName1); + ren1_dst, branch2, dst_name1); remove_file(0, ren1_dst); } - if (path_list_has_path(¤tDirectorySet, ren2_dst)) { - dstName2 = del[delp++] = unique_path(ren2_dst, branch2); + if (path_list_has_path(¤t_directory_set, ren2_dst)) { + dst_name2 = del[delp++] = unique_path(ren2_dst, branch2); output("%s is a directory in %s adding as %s instead", - ren2_dst, branch1, dstName2); + ren2_dst, branch1, dst_name2); remove_file(0, ren2_dst); } - update_stages(dstName1, NULL, ren1->pair->two, NULL, 1); - update_stages(dstName2, NULL, NULL, ren2->pair->two, 1); + update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1); + update_stages(dst_name2, NULL, NULL, ren2->pair->two, 1); while (delp--) free(del[delp]); } @@ -807,11 +805,11 @@ static void conflict_rename_rename(struct rename *ren1, static void conflict_rename_dir(struct rename *ren1, const char *branch1) { - char *newPath = unique_path(ren1->pair->two->path, branch1); - output("Renaming %s to %s instead", ren1->pair->one->path, newPath); + char *new_path = unique_path(ren1->pair->two->path, branch1); + output("Renaming %s to %s instead", ren1->pair->one->path, new_path); remove_file(0, ren1->pair->two->path); - update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, newPath); - free(newPath); + update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path); + free(new_path); } static void conflict_rename_rename_2(struct rename *ren1, @@ -819,74 +817,74 @@ static void conflict_rename_rename_2(struct rename *ren1, struct rename *ren2, const char *branch2) { - char *newPath1 = unique_path(ren1->pair->two->path, branch1); - char *newPath2 = unique_path(ren2->pair->two->path, branch2); + char *new_path1 = unique_path(ren1->pair->two->path, branch1); + char *new_path2 = unique_path(ren2->pair->two->path, branch2); output("Renaming %s to %s and %s to %s instead", - ren1->pair->one->path, newPath1, - ren2->pair->one->path, newPath2); + ren1->pair->one->path, new_path1, + ren2->pair->one->path, new_path2); remove_file(0, ren1->pair->two->path); - update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, newPath1); - update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, newPath2); - free(newPath2); - free(newPath1); + update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path1); + update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, new_path2); + free(new_path2); + free(new_path1); } /* General TODO: get rid of all the debug messages */ -static int process_renames(struct path_list *renamesA, - struct path_list *renamesB, - const char *branchNameA, - const char *branchNameB) +static int process_renames(struct path_list *a_renames, + struct path_list *b_renames, + const char *a_branch, + const char *b_branch) { - int cleanMerge = 1, i, j; - struct path_list byDstA = {NULL, 0, 0, 0}, byDstB = {NULL, 0, 0, 0}; + int clean_merge = 1, i, j; + struct path_list a_by_dst = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0}; const struct rename *sre; - for (i = 0; i < renamesA->nr; i++) { - sre = renamesA->items[i].util; - path_list_insert(sre->pair->two->path, &byDstA)->util + for (i = 0; i < a_renames->nr; i++) { + sre = a_renames->items[i].util; + path_list_insert(sre->pair->two->path, &a_by_dst)->util = sre->dst_entry; } - for (i = 0; i < renamesB->nr; i++) { - sre = renamesB->items[i].util; - path_list_insert(sre->pair->two->path, &byDstB)->util + for (i = 0; i < b_renames->nr; i++) { + sre = b_renames->items[i].util; + path_list_insert(sre->pair->two->path, &b_by_dst)->util = sre->dst_entry; } - for (i = 0, j = 0; i < renamesA->nr || j < renamesB->nr;) { + for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) { int compare; char *src; struct path_list *renames1, *renames2, *renames2Dst; struct rename *ren1 = NULL, *ren2 = NULL; - const char *branchName1, *branchName2; + const char *branch1, *branch2; const char *ren1_src, *ren1_dst; - if (i >= renamesA->nr) { + if (i >= a_renames->nr) { compare = 1; - ren2 = renamesB->items[j++].util; - } else if (j >= renamesB->nr) { + ren2 = b_renames->items[j++].util; + } else if (j >= b_renames->nr) { compare = -1; - ren1 = renamesA->items[i++].util; + ren1 = a_renames->items[i++].util; } else { - compare = strcmp(renamesA->items[i].path, - renamesB->items[j].path); - ren1 = renamesA->items[i++].util; - ren2 = renamesB->items[j++].util; + compare = strcmp(a_renames->items[i].path, + b_renames->items[j].path); + ren1 = a_renames->items[i++].util; + ren2 = b_renames->items[j++].util; } /* TODO: refactor, so that 1/2 are not needed */ if (ren1) { - renames1 = renamesA; - renames2 = renamesB; - renames2Dst = &byDstB; - branchName1 = branchNameA; - branchName2 = branchNameB; + renames1 = a_renames; + renames2 = b_renames; + renames2Dst = &b_by_dst; + branch1 = a_branch; + branch2 = b_branch; } else { struct rename *tmp; - renames1 = renamesB; - renames2 = renamesA; - renames2Dst = &byDstA; - branchName1 = branchNameB; - branchName2 = branchNameA; + renames1 = b_renames; + renames2 = a_renames; + renames2Dst = &a_by_dst; + branch1 = b_branch; + branch2 = a_branch; tmp = ren2; ren2 = ren1; ren1 = tmp; @@ -912,21 +910,21 @@ static int process_renames(struct path_list *renamesA, ren2->dst_entry->processed = 1; ren2->processed = 1; if (strcmp(ren1_dst, ren2_dst) != 0) { - cleanMerge = 0; + clean_merge = 0; output("CONFLICT (rename/rename): " "Rename %s->%s in branch %s " "rename %s->%s in %s", - src, ren1_dst, branchName1, - src, ren2_dst, branchName2); - conflict_rename_rename(ren1, branchName1, ren2, branchName2); + src, ren1_dst, branch1, + src, ren2_dst, branch2); + conflict_rename_rename(ren1, branch1, ren2, branch2); } else { - remove_file(1, ren1_src); struct merge_file_info mfi; + remove_file(1, ren1_src); mfi = merge_file(ren1->pair->one, ren1->pair->two, ren2->pair->two, - branchName1, - branchName2); + branch1, + branch2); if (mfi.merge || !mfi.clean) output("Renaming %s->%s", src, ren1_dst); @@ -936,7 +934,7 @@ static int process_renames(struct path_list *renamesA, if (!mfi.clean) { output("CONFLICT (content): merge conflict in %s", ren1_dst); - cleanMerge = 0; + clean_merge = 0; if (!index_only) update_stages(ren1_dst, @@ -952,7 +950,7 @@ static int process_renames(struct path_list *renamesA, struct path_list_item *item; /* we only use sha1 and mode of these */ struct diff_filespec src_other, dst_other; - int tryMerge, stage = renamesA == renames1 ? 3: 2; + int try_merge, stage = a_renames == renames1 ? 3: 2; remove_file(1, ren1_src); @@ -963,52 +961,52 @@ static int process_renames(struct path_list *renamesA, ren1->dst_entry->stages[stage].sha, 20); dst_other.mode = ren1->dst_entry->stages[stage].mode; - tryMerge = 0; + try_merge = 0; - if (path_list_has_path(¤tDirectorySet, ren1_dst)) { - cleanMerge = 0; + if (path_list_has_path(¤t_directory_set, ren1_dst)) { + clean_merge = 0; output("CONFLICT (rename/directory): Rename %s->%s in %s " " directory %s added in %s", - ren1_src, ren1_dst, branchName1, - ren1_dst, branchName2); - conflict_rename_dir(ren1, branchName1); + ren1_src, ren1_dst, branch1, + ren1_dst, branch2); + conflict_rename_dir(ren1, branch1); } else if (sha_eq(src_other.sha1, null_sha1)) { - cleanMerge = 0; + clean_merge = 0; output("CONFLICT (rename/delete): Rename %s->%s in %s " "and deleted in %s", - ren1_src, ren1_dst, branchName1, - branchName2); + ren1_src, ren1_dst, branch1, + branch2); update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst); } else if (!sha_eq(dst_other.sha1, null_sha1)) { - const char *newPath; - cleanMerge = 0; - tryMerge = 1; + const char *new_path; + clean_merge = 0; + try_merge = 1; output("CONFLICT (rename/add): Rename %s->%s in %s. " "%s added in %s", - ren1_src, ren1_dst, branchName1, - ren1_dst, branchName2); - newPath = unique_path(ren1_dst, branchName2); - output("Adding as %s instead", newPath); - update_file(0, dst_other.sha1, dst_other.mode, newPath); + ren1_src, ren1_dst, branch1, + ren1_dst, branch2); + new_path = unique_path(ren1_dst, branch2); + output("Adding as %s instead", new_path); + update_file(0, dst_other.sha1, dst_other.mode, new_path); } else if ((item = path_list_lookup(ren1_dst, renames2Dst))) { ren2 = item->util; - cleanMerge = 0; + clean_merge = 0; ren2->processed = 1; output("CONFLICT (rename/rename): Rename %s->%s in %s. " "Rename %s->%s in %s", - ren1_src, ren1_dst, branchName1, - ren2->pair->one->path, ren2->pair->two->path, branchName2); - conflict_rename_rename_2(ren1, branchName1, ren2, branchName2); + ren1_src, ren1_dst, branch1, + ren2->pair->one->path, ren2->pair->two->path, branch2); + conflict_rename_rename_2(ren1, branch1, ren2, branch2); } else - tryMerge = 1; + try_merge = 1; - if (tryMerge) { + if (try_merge) { struct diff_filespec *o, *a, *b; struct merge_file_info mfi; src_other.path = (char *)ren1_src; o = ren1->pair->one; - if (renamesA == renames1) { + if (a_renames == renames1) { a = ren1->pair->two; b = &src_other; } else { @@ -1016,7 +1014,7 @@ static int process_renames(struct path_list *renamesA, a = &src_other; } mfi = merge_file(o, a, b, - branchNameA, branchNameB); + a_branch, b_branch); if (mfi.merge || !mfi.clean) output("Renaming %s => %s", ren1_src, ren1_dst); @@ -1025,7 +1023,7 @@ static int process_renames(struct path_list *renamesA, if (!mfi.clean) { output("CONFLICT (rename/modify): Merge conflict in %s", ren1_dst); - cleanMerge = 0; + clean_merge = 0; if (!index_only) update_stages(ren1_dst, @@ -1035,12 +1033,12 @@ static int process_renames(struct path_list *renamesA, } } } - path_list_clear(&byDstA, 0); - path_list_clear(&byDstB, 0); + path_list_clear(&a_by_dst, 0); + path_list_clear(&b_by_dst, 0); if (cache_dirty) flush_cache(); - return cleanMerge; + return clean_merge; } static unsigned char *has_sha(const unsigned char *sha) @@ -1057,116 +1055,116 @@ static int process_entry(const char *path, struct stage_data *entry, printf("processing entry, clean cache: %s\n", index_only ? "yes": "no"); print_index_entry("\tpath: ", entry); */ - int cleanMerge = 1; - unsigned char *oSha = has_sha(entry->stages[1].sha); - unsigned char *aSha = has_sha(entry->stages[2].sha); - unsigned char *bSha = has_sha(entry->stages[3].sha); - unsigned oMode = entry->stages[1].mode; - unsigned aMode = entry->stages[2].mode; - unsigned bMode = entry->stages[3].mode; + int clean_merge = 1; + unsigned char *o_sha = has_sha(entry->stages[1].sha); + unsigned char *a_sha = has_sha(entry->stages[2].sha); + unsigned char *b_sha = has_sha(entry->stages[3].sha); + unsigned o_mode = entry->stages[1].mode; + unsigned a_mode = entry->stages[2].mode; + unsigned b_mode = entry->stages[3].mode; - if (oSha && (!aSha || !bSha)) { + if (o_sha && (!a_sha || !b_sha)) { /* Case A: Deleted in one */ - if ((!aSha && !bSha) || - (sha_eq(aSha, oSha) && !bSha) || - (!aSha && sha_eq(bSha, oSha))) { + if ((!a_sha && !b_sha) || + (sha_eq(a_sha, o_sha) && !b_sha) || + (!a_sha && sha_eq(b_sha, o_sha))) { /* Deleted in both or deleted in one and * unchanged in the other */ - if (aSha) + if (a_sha) output("Removing %s", path); remove_file(1, path); } else { /* Deleted in one and changed in the other */ - cleanMerge = 0; - if (!aSha) { + clean_merge = 0; + if (!a_sha) { output("CONFLICT (delete/modify): %s deleted in %s " "and modified in %s. Version %s of %s left in tree.", path, branch1Name, branch2Name, branch2Name, path); - update_file(0, bSha, bMode, path); + update_file(0, b_sha, b_mode, 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); - update_file(0, aSha, aMode, path); + update_file(0, a_sha, a_mode, path); } } - } else if ((!oSha && aSha && !bSha) || - (!oSha && !aSha && bSha)) { + } else if ((!o_sha && a_sha && !b_sha) || + (!o_sha && !a_sha && b_sha)) { /* Case B: Added in one. */ - const char *addBranch; - const char *otherBranch; + const char *add_branch; + const char *other_branch; unsigned mode; const unsigned char *sha; const char *conf; - if (aSha) { - addBranch = branch1Name; - otherBranch = branch2Name; - mode = aMode; - sha = aSha; + if (a_sha) { + add_branch = branch1Name; + other_branch = branch2Name; + mode = a_mode; + sha = a_sha; conf = "file/directory"; } else { - addBranch = branch2Name; - otherBranch = branch1Name; - mode = bMode; - sha = bSha; + add_branch = branch2Name; + other_branch = branch1Name; + mode = b_mode; + sha = b_sha; conf = "directory/file"; } - if (path_list_has_path(¤tDirectorySet, path)) { - const char *newPath = unique_path(path, addBranch); - cleanMerge = 0; + if (path_list_has_path(¤t_directory_set, path)) { + const char *new_path = unique_path(path, add_branch); + clean_merge = 0; output("CONFLICT (%s): There is a directory with name %s in %s. " "Adding %s as %s", - conf, path, otherBranch, path, newPath); + conf, path, other_branch, path, new_path); remove_file(0, path); - update_file(0, sha, mode, newPath); + update_file(0, sha, mode, new_path); } else { output("Adding %s", path); update_file(1, sha, mode, path); } - } else if (!oSha && aSha && bSha) { + } else if (!o_sha && a_sha && b_sha) { /* Case C: Added in both (check for same permissions). */ - if (sha_eq(aSha, bSha)) { - if (aMode != bMode) { - cleanMerge = 0; + if (sha_eq(a_sha, b_sha)) { + if (a_mode != b_mode) { + clean_merge = 0; output("CONFLICT: File %s added identically in both branches, " "but permissions conflict %06o->%06o", - path, aMode, bMode); - output("CONFLICT: adding with permission: %06o", aMode); - update_file(0, aSha, aMode, path); + path, a_mode, b_mode); + output("CONFLICT: adding with permission: %06o", a_mode); + update_file(0, a_sha, a_mode, path); } else { /* This case is handled by git-read-tree */ assert(0 && "This case must be handled by git-read-tree"); } } else { - const char *newPath1, *newPath2; - cleanMerge = 0; - newPath1 = unique_path(path, branch1Name); - newPath2 = unique_path(path, branch2Name); + const char *new_path1, *new_path2; + clean_merge = 0; + new_path1 = unique_path(path, branch1Name); + new_path2 = unique_path(path, branch2Name); output("CONFLICT (add/add): File %s added non-identically " "in both branches. Adding as %s and %s instead.", - path, newPath1, newPath2); + path, new_path1, new_path2); remove_file(0, path); - update_file(0, aSha, aMode, newPath1); - update_file(0, bSha, bMode, newPath2); + update_file(0, a_sha, a_mode, new_path1); + update_file(0, b_sha, b_mode, new_path2); } - } else if (oSha && aSha && bSha) { + } else if (o_sha && a_sha && b_sha) { /* case D: Modified in both, but differently. */ struct merge_file_info mfi; struct diff_filespec o, a, b; output("Auto-merging %s", path); o.path = a.path = b.path = (char *)path; - memcpy(o.sha1, oSha, 20); - o.mode = oMode; - memcpy(a.sha1, aSha, 20); - a.mode = aMode; - memcpy(b.sha1, bSha, 20); - b.mode = bMode; + memcpy(o.sha1, o_sha, 20); + o.mode = o_mode; + memcpy(a.sha1, a_sha, 20); + a.mode = a_mode; + memcpy(b.sha1, b_sha, 20); + b.mode = b_mode; mfi = merge_file(&o, &a, &b, branch1Name, branch2Name); @@ -1174,14 +1172,14 @@ static int process_entry(const char *path, struct stage_data *entry, if (mfi.clean) update_file(1, mfi.sha, mfi.mode, path); else { - cleanMerge = 0; + clean_merge = 0; output("CONFLICT (content): Merge conflict in %s", path); if (index_only) update_file(0, mfi.sha, mfi.mode, path); else update_file_flags(mfi.sha, mfi.mode, path, - 0 /* updateCache */, 1 /* updateWd */); + 0 /* update_cache */, 1 /* update_working_directory */); } } else die("Fatal merge failure, shouldn't happen."); @@ -1189,7 +1187,7 @@ static int process_entry(const char *path, struct stage_data *entry, if (cache_dirty) flush_cache(); - return cleanMerge; + return clean_merge; } static int merge_trees(struct tree *head, @@ -1218,8 +1216,8 @@ static int merge_trees(struct tree *head, if (!*result) { struct path_list *entries, *re_head, *re_merge; int i; - path_list_clear(¤tFileSet, 1); - path_list_clear(¤tDirectorySet, 1); + path_list_clear(¤t_file_set, 1); + path_list_clear(¤t_directory_set, 1); get_files_dirs(head); get_files_dirs(merge); @@ -1265,12 +1263,12 @@ int merge(struct commit *h1, struct commit *h2, const char *branch1Name, const char *branch2Name, - int callDepth /* =0 */, + int call_depth /* =0 */, struct commit *ancestor /* =None */, struct commit **result) { struct commit_list *ca = NULL, *iter; - struct commit *mergedCA; + struct commit *merged_common_ancestors; struct tree *mrtree; int clean; @@ -1287,10 +1285,10 @@ int merge(struct commit *h1, for (iter = ca; iter; iter = iter->next) output_commit_title(iter->item); - mergedCA = pop_commit(&ca); + merged_common_ancestors = pop_commit(&ca); for (iter = ca; iter; iter = iter->next) { - output_indent = callDepth + 1; + output_indent = call_depth + 1; /* * When the merge fails, the result contains files * with conflict markers. The cleanness flag is @@ -1298,19 +1296,19 @@ int merge(struct commit *h1, * merge_trees has always overwritten it: the commited * "conflicts" were already resolved. */ - merge(mergedCA, iter->item, + merge(merged_common_ancestors, iter->item, "Temporary merge branch 1", "Temporary merge branch 2", - callDepth + 1, + call_depth + 1, NULL, - &mergedCA); - output_indent = callDepth; + &merged_common_ancestors); + output_indent = call_depth; - if (!mergedCA) + if (!merged_common_ancestors) die("merge returned no commit"); } - if (callDepth == 0) { + if (call_depth == 0) { setup_index(0 /* $GIT_DIR/index */); index_only = 0; } else { @@ -1319,7 +1317,7 @@ int merge(struct commit *h1, index_only = 1; } - clean = merge_trees(h1->tree, h2->tree, mergedCA->tree, + clean = merge_trees(h1->tree, h2->tree, merged_common_ancestors->tree, branch1Name, branch2Name, &mrtree); if (!ancestor && (clean || index_only)) { From bd669986f7ecfb0215e5d5fa29ec8449a6225bc1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 27 Jul 2006 19:12:51 +0200 Subject: [PATCH 05/21] merge-recur: Get rid of debug code Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 65 +---------------------------------------------- 1 file changed, 1 insertion(+), 64 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index 5d20f9efce..6f109f16ec 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -22,56 +22,6 @@ #include "path-list.h" -/*#define DEBUG*/ - -#ifdef DEBUG -#define debug(...) fprintf(stderr, __VA_ARGS__) -#else -#define debug(...) do { ; /* nothing */ } while (0) -#endif - -#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(void) { - int i; - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; - show_ce_entry("", ce); - } - fprintf(stderr, "---\n"); - if (0) ls_files(); /* avoid "unused" warning */ -} -#endif - /* * A virtual commit has * - (const char *)commit->util set to the name, and @@ -346,13 +296,9 @@ static int save_files_dirs(const unsigned char *sha1, static int get_files_dirs(struct tree *tree) { int n; - debug("get_files_dirs ...\n"); - if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs) != 0) { - debug(" get_files_dirs done (0)\n"); + if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs) != 0) return 0; - } n = current_file_set.nr + current_directory_set.nr; - debug(" get_files_dirs done (%d)\n", n); return n; } @@ -434,11 +380,6 @@ static struct path_list *get_renames(struct tree *tree, int i; struct path_list *renames; struct diff_options opts; -#ifdef DEBUG - time_t t = time(0); - - debug("get_renames ...\n"); -#endif renames = xcalloc(1, sizeof(struct path_list)); diff_setup(&opts); @@ -479,9 +420,6 @@ static struct path_list *get_renames(struct tree *tree, opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_queued_diff.nr = 0; diff_flush(&opts); -#ifdef DEBUG - debug(" get_renames done in %ld\n", time(0)-t); -#endif return renames; } @@ -829,7 +767,6 @@ static void conflict_rename_rename_2(struct rename *ren1, free(new_path1); } -/* General TODO: get rid of all the debug messages */ static int process_renames(struct path_list *a_renames, struct path_list *b_renames, const char *a_branch, From 5d3afe05d9466a8472be315793dfc08594b1e9c4 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 27 Jul 2006 19:13:08 +0200 Subject: [PATCH 06/21] merge-recur: Remove dead code Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index 6f109f16ec..79c022e098 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -197,11 +197,6 @@ static int index_only = 0; */ static int git_read_tree(const struct tree *tree) { -#if 0 - fprintf(stderr, "GIT_INDEX_FILE='%s' git-read-tree %s\n", - getenv("GIT_INDEX_FILE"), - sha1_to_hex(tree->object.sha1)); -#endif int rc; const char *argv[] = { "git-read-tree", NULL, NULL, }; if (cache_dirty) @@ -219,14 +214,6 @@ static int git_merge_trees(const char *update_arg, struct tree *head, struct tree *merge) { -#if 0 - fprintf(stderr, "GIT_INDEX_FILE='%s' git-read-tree %s -m %s %s %s\n", - getenv("GIT_INDEX_FILE"), - update_arg, - sha1_to_hex(common->object.sha1), - sha1_to_hex(head->object.sha1), - sha1_to_hex(merge->object.sha1)); -#endif int rc; const char *argv[] = { "git-read-tree", NULL, "-m", NULL, NULL, NULL, @@ -247,10 +234,6 @@ static int git_merge_trees(const char *update_arg, */ static struct tree *git_write_tree(void) { -#if 0 - fprintf(stderr, "GIT_INDEX_FILE='%s' git-write-tree\n", - getenv("GIT_INDEX_FILE")); -#endif FILE *fp; int rc; char buf[41]; @@ -672,11 +655,6 @@ static struct merge_file_info merge_file(struct diff_filespec *o, argv[6] = lb = strdup(mkpath("%s/%s", branch2Name, b->path)); argv[4] = lo = strdup(mkpath("orig/%s", o->path)); -#if 0 - printf("%s %s %s %s %s %s %s %s %s %s\n", - argv[0], argv[1], argv[2], argv[3], argv[4], - argv[5], argv[6], argv[7], argv[8], argv[9]); -#endif code = run_command_v(10, argv); free(la); From 7b3f5daabc9fcfffc632a6a963cf5466745b7dbd Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 27 Jul 2006 19:13:24 +0200 Subject: [PATCH 07/21] merge-recur: Fix compiler warning with -pedantic Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/merge-recursive.c b/merge-recursive.c index 79c022e098..1a21ff3025 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -642,7 +642,7 @@ static struct merge_file_info merge_file(struct diff_filespec *o, char src2[PATH_MAX]; const char *argv[] = { "merge", "-L", NULL, "-L", NULL, "-L", NULL, - src1, orig, src2, + NULL, NULL, NULL, NULL }; char *la, *lb, *lo; @@ -654,6 +654,9 @@ static struct merge_file_info merge_file(struct diff_filespec *o, argv[2] = la = strdup(mkpath("%s/%s", branch1Name, a->path)); argv[6] = lb = strdup(mkpath("%s/%s", branch2Name, b->path)); argv[4] = lo = strdup(mkpath("orig/%s", o->path)); + argv[7] = src1; + argv[8] = orig; + argv[9] = src2, code = run_command_v(10, argv); From c1d20846f4a7112c44653dfe25411ccd19297790 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 27 Jul 2006 19:13:47 +0200 Subject: [PATCH 08/21] merge-recur: Cleanup last mixedCase variables... Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index 1a21ff3025..eda4feea0f 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -609,7 +609,7 @@ static char *git_unpack_file(const unsigned char *sha1, char *path) static struct merge_file_info merge_file(struct diff_filespec *o, struct diff_filespec *a, struct diff_filespec *b, - const char *branch1Name, const char *branch2Name) + const char *branch1, const char *branch2) { struct merge_file_info result; result.merge = 0; @@ -651,8 +651,8 @@ static struct merge_file_info merge_file(struct diff_filespec *o, git_unpack_file(a->sha1, src1); git_unpack_file(b->sha1, src2); - argv[2] = la = strdup(mkpath("%s/%s", branch1Name, a->path)); - argv[6] = lb = strdup(mkpath("%s/%s", branch2Name, b->path)); + argv[2] = la = strdup(mkpath("%s/%s", branch1, a->path)); + argv[6] = lb = strdup(mkpath("%s/%s", branch2, b->path)); argv[4] = lo = strdup(mkpath("orig/%s", o->path)); argv[7] = src1; argv[8] = orig; @@ -966,8 +966,8 @@ static unsigned char *has_sha(const unsigned char *sha) /* Per entry merge function */ static int process_entry(const char *path, struct stage_data *entry, - const char *branch1Name, - const char *branch2Name) + const char *branch1, + const char *branch2) { /* printf("processing entry, clean cache: %s\n", index_only ? "yes": "no"); @@ -997,14 +997,14 @@ static int process_entry(const char *path, struct stage_data *entry, if (!a_sha) { output("CONFLICT (delete/modify): %s deleted in %s " "and modified in %s. Version %s of %s left in tree.", - path, branch1Name, - branch2Name, branch2Name, path); + path, branch1, + branch2, branch2, path); update_file(0, b_sha, b_mode, 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); + path, branch2, + branch1, branch1, path); update_file(0, a_sha, a_mode, path); } } @@ -1019,14 +1019,14 @@ static int process_entry(const char *path, struct stage_data *entry, const char *conf; if (a_sha) { - add_branch = branch1Name; - other_branch = branch2Name; + add_branch = branch1; + other_branch = branch2; mode = a_mode; sha = a_sha; conf = "file/directory"; } else { - add_branch = branch2Name; - other_branch = branch1Name; + add_branch = branch2; + other_branch = branch1; mode = b_mode; sha = b_sha; conf = "directory/file"; @@ -1060,8 +1060,8 @@ static int process_entry(const char *path, struct stage_data *entry, } else { const char *new_path1, *new_path2; clean_merge = 0; - new_path1 = unique_path(path, branch1Name); - new_path2 = unique_path(path, branch2Name); + new_path1 = unique_path(path, branch1); + new_path2 = unique_path(path, branch2); output("CONFLICT (add/add): File %s added non-identically " "in both branches. Adding as %s and %s instead.", path, new_path1, new_path2); @@ -1085,7 +1085,7 @@ static int process_entry(const char *path, struct stage_data *entry, b.mode = b_mode; mfi = merge_file(&o, &a, &b, - branch1Name, branch2Name); + branch1, branch2); if (mfi.clean) update_file(1, mfi.sha, mfi.mode, path); @@ -1111,8 +1111,8 @@ static int process_entry(const char *path, struct stage_data *entry, static int merge_trees(struct tree *head, struct tree *merge, struct tree *common, - const char *branch1Name, - const char *branch2Name, + const char *branch1, + const char *branch2, struct tree **result) { int code, clean; @@ -1143,13 +1143,13 @@ static int merge_trees(struct tree *head, re_head = get_renames(head, common, head, merge, entries); re_merge = get_renames(merge, common, head, merge, entries); clean = process_renames(re_head, re_merge, - branch1Name, branch2Name); + branch1, branch2); for (i = 0; i < entries->nr; i++) { const char *path = entries->items[i].path; struct stage_data *e = entries->items[i].util; if (e->processed) continue; - if (!process_entry(path, e, branch1Name, branch2Name)) + if (!process_entry(path, e, branch1, branch2)) clean = 0; } @@ -1179,8 +1179,8 @@ static int merge_trees(struct tree *head, static int merge(struct commit *h1, struct commit *h2, - const char *branch1Name, - const char *branch2Name, + const char *branch1, + const char *branch2, int call_depth /* =0 */, struct commit *ancestor /* =None */, struct commit **result) @@ -1236,7 +1236,7 @@ int merge(struct commit *h1, } clean = merge_trees(h1->tree, h2->tree, merged_common_ancestors->tree, - branch1Name, branch2Name, &mrtree); + branch1, branch2, &mrtree); if (!ancestor && (clean || index_only)) { *result = make_virtual_commit(mrtree, "merged tree"); From 3058e9339f05379462b7e14d832d091cc3d9b2b9 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 27 Jul 2006 19:14:17 +0200 Subject: [PATCH 09/21] merge-recur: Explain why sha_eq() and struct stage_data cannot go There were two TODOs to remove sha_eq() and to convert users of struct stage_data to active_cache users, but this is not possible. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index eda4feea0f..6a796f24c7 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -47,8 +47,8 @@ static struct commit *make_virtual_commit(struct tree *tree, const char *comment } /* - * TODO: we should not have to copy the SHA1s around, but rather reference - * them. That way, sha_eq() is just sha1 == sha2. + * Since we use get_tree_entry(), which does not put the read object into + * the object pool, we cannot rely on a == b. */ static int sha_eq(const unsigned char *a, const unsigned char *b) { @@ -58,9 +58,8 @@ static int sha_eq(const unsigned char *a, const unsigned char *b) } /* - * TODO: check if we can just reuse the active_cache structure: it is already - * sorted (by name, stage). - * Only problem: do not write it when flushing the cache. + * Since we want to write the index eventually, we cannot reuse the index + * for these (temporary) data. */ struct stage_data { From a060b803b49c04cd6e3b0d859f131349dab6b26f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 27 Jul 2006 22:02:45 -0700 Subject: [PATCH 10/21] Makefile: git-merge-recur depends on xdiff libraries. Tighten dependencies to allow parallel build. Signed-off-by: Junio C Hamano --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a749aa4f0e..12ffd45ca9 100644 --- a/Makefile +++ b/Makefile @@ -617,7 +617,7 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS) $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) merge-recursive.o path-list.o: path-list.h -git-merge-recur$X: merge-recursive.o path-list.o $(LIB_FILE) +git-merge-recur$X: merge-recursive.o path-list.o $(GITLIBS) $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) From f59aac47f3839367d0da04019b0fc2bd61345225 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 30 Jul 2006 18:35:21 +0200 Subject: [PATCH 11/21] merge-recur: fix thinko in unique_path() This could result in a nasty infinite loop, or in bogus names (it used the strlen() of the newly allocated buffer instead of the original buffer). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index 6a796f24c7..5375a1ba30 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -477,9 +477,9 @@ static char *unique_path(const char *path, const char *branch) char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1); int suffix = 0; struct stat st; - char *p = newpath + strlen(newpath); + char *p = newpath + strlen(path); strcpy(newpath, path); - strcat(newpath, "~"); + *(p++) = '~'; strcpy(p, branch); for (; *p; ++p) if ('/' == *p) From 7a85b848ad77c72c539c5887ead99a89140541da Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 30 Jul 2006 20:27:10 +0200 Subject: [PATCH 12/21] merge-recur: use the unpack_trees() interface instead of exec()ing read-tree Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 80 ++++++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index 5375a1ba30..10bce705fa 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -19,7 +19,7 @@ #include "diffcore.h" #include "run-command.h" #include "tag.h" - +#include "unpack-trees.h" #include "path-list.h" /* @@ -124,7 +124,7 @@ static int flush_cache(void) 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); + 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")); @@ -191,41 +191,59 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, */ static int index_only = 0; -/* - * TODO: this can be streamlined by refactoring builtin-read-tree.c - */ -static int git_read_tree(const struct tree *tree) +static int git_read_tree(struct tree *tree) { int rc; - const char *argv[] = { "git-read-tree", NULL, NULL, }; + struct object_list *trees = NULL; + struct unpack_trees_options opts; + if (cache_dirty) die("read-tree with dirty cache"); - argv[1] = sha1_to_hex(tree->object.sha1); - rc = run_command_v(2, argv); - return rc < 0 ? -1: rc; + + memset(&opts, 0, sizeof(opts)); + object_list_append(&tree->object, &trees); + rc = unpack_trees(trees, &opts); + cache_tree_free(&active_cache_tree); + + if (rc == 0) + cache_dirty = 1; + + return rc; } -/* - * TODO: this can be streamlined by refactoring builtin-read-tree.c - */ -static int git_merge_trees(const char *update_arg, +static int git_merge_trees(int index_only, struct tree *common, struct tree *head, struct tree *merge) { int rc; - const char *argv[] = { - "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); - argv[5] = sha1_to_hex(merge->object.sha1); - rc = run_command_v(6, argv); - return rc < 0 ? -1: rc; + struct object_list *trees = NULL; + struct unpack_trees_options opts; + + if (!cache_dirty) { + read_cache_from(getenv("GIT_INDEX_FILE")); + cache_dirty = 1; + } + + memset(&opts, 0, sizeof(opts)); + if (index_only) + opts.index_only = 1; + else + opts.update = 1; + opts.merge = 1; + opts.head_idx = 2; + opts.fn = threeway_merge; + + object_list_append(&common->object, &trees); + object_list_append(&head->object, &trees); + object_list_append(&merge->object, &trees); + + rc = unpack_trees(trees, &opts); + cache_tree_free(&active_cache_tree); + + cache_dirty = 1; + + return rc; } /* @@ -239,8 +257,14 @@ static struct tree *git_write_tree(void) unsigned char sha1[20]; int ch; unsigned i = 0; - if (cache_dirty) + if (cache_dirty) { + 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') @@ -1121,7 +1145,7 @@ static int merge_trees(struct tree *head, return 1; } - code = git_merge_trees(index_only ? "-i": "-u", common, head, merge); + code = git_merge_trees(index_only, common, head, merge); if (code != 0) die("merging of trees %s and %s failed", From c1f3089e4bb5a3e8e8f92915800f2d5a2d4dd246 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 31 Jul 2006 12:42:35 +0200 Subject: [PATCH 13/21] merge-recur: virtual commits shall never be parsed It would not make sense to parse a virtual commit, therefore set the "parsed" flag to 1. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- merge-recursive.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/merge-recursive.c b/merge-recursive.c index 10bce705fa..74a329faa5 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -43,6 +43,8 @@ static struct commit *make_virtual_commit(struct tree *tree, const char *comment commit->tree = tree; commit->util = (void*)comment; *(int*)commit->object.sha1 = virtual_id++; + /* avoid warnings */ + commit->object.parsed = 1; return commit; } From c8b87ef8b0da6b08a19ed4f98eb58f46748af1e9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 3 Aug 2006 16:17:51 -0700 Subject: [PATCH 14/21] .gitignore: git-merge-recur is a built file. Also it is OK to omit the documentation for it at least for now. Signed-off-by: Junio C Hamano --- .gitignore | 1 + Makefile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fb0fa3f16a..2299e36ba9 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,7 @@ git-merge-tree git-merge-octopus git-merge-one-file git-merge-ours +git-merge-recur git-merge-recursive git-merge-resolve git-merge-stupid diff --git a/Makefile b/Makefile index b7a095f0dd..81d26a0881 100644 --- a/Makefile +++ b/Makefile @@ -802,7 +802,7 @@ check-docs:: do \ case "$$v" in \ git-merge-octopus | git-merge-ours | git-merge-recursive | \ - git-merge-resolve | git-merge-stupid | \ + git-merge-resolve | git-merge-stupid | git-merge-recur | \ git-ssh-pull | git-ssh-push ) continue ;; \ esac ; \ test -f "Documentation/$$v.txt" || \ From 3d234d0afacd355eb30b330a56bd2efa73bd44d5 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 4 Aug 2006 18:21:41 +0200 Subject: [PATCH 15/21] merge-recursive: fix rename handling To handle renames properly, we iterate through all file names of both heads, the current one, and the one to be merged. Only that there was a bug, where it was checked if the file name was present in both heads, but the result of the check was never used. Instead, the merge proceeded as if both heads contained that file. 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 74a329faa5..f5c0080a51 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -810,8 +810,10 @@ static int process_renames(struct path_list *a_renames, } else { compare = strcmp(a_renames->items[i].path, b_renames->items[j].path); - ren1 = a_renames->items[i++].util; - ren2 = b_renames->items[j++].util; + if (compare <= 0) + ren1 = a_renames->items[i++].util; + if (compare >= 0) + ren2 = b_renames->items[j++].util; } /* TODO: refactor, so that 1/2 are not needed */ From 5b982f84ee6eb3027e5bdf2e917a0efa48aded6a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 9 Aug 2006 15:04:16 +0200 Subject: [PATCH 16/21] 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 17/21] 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 18/21] 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 19/21] 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 20/21] 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(); } From 4147d801db66df9b127ffe315601f467aa9d1c48 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 10 Aug 2006 16:47:21 +0200 Subject: [PATCH 21/21] discard_cache(): discard index, even if no file was mmap()ed Since add_cacheinfo() can be called without a mapped index file, discard_cache() _has_ to discard the entries, even when cache_mmap == NULL. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- read-cache.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/read-cache.c b/read-cache.c index c375e912a9..fc2af2cb04 100644 --- a/read-cache.c +++ b/read-cache.c @@ -840,14 +840,14 @@ int discard_cache() { int ret; + active_nr = active_cache_changed = 0; + index_file_timestamp = 0; + cache_tree_free(&active_cache_tree); if (cache_mmap == NULL) return 0; ret = munmap(cache_mmap, cache_mmap_size); cache_mmap = NULL; cache_mmap_size = 0; - active_nr = active_cache_changed = 0; - index_file_timestamp = 0; - cache_tree_free(&active_cache_tree); /* no need to throw away allocated active_cache */ return ret;