#include "cache.h" #include "tree-walk.h" static const char unresolve_usage[] = "git-unresolve ..."; static struct cache_file cache_file; static unsigned char head_sha1[20]; static unsigned char merge_head_sha1[20]; static struct cache_entry *read_one_ent(const char *which, unsigned char *ent, const char *path, int namelen, int stage) { unsigned mode; unsigned char sha1[20]; int size; struct cache_entry *ce; if (get_tree_entry(ent, path, sha1, &mode)) { error("%s: not in %s branch.", path, which); return NULL; } if (mode == S_IFDIR) { error("%s: not a blob in %s branch.", path, which); return NULL; } size = cache_entry_size(namelen); ce = xcalloc(1, size); memcpy(ce->sha1, sha1, 20); memcpy(ce->name, path, namelen); ce->ce_flags = create_ce_flags(namelen, stage); ce->ce_mode = create_ce_mode(mode); return ce; } static int unresolve_one(const char *path) { int namelen = strlen(path); int pos; int ret = 0; struct cache_entry *ce_2 = NULL, *ce_3 = NULL; /* See if there is such entry in the index. */ pos = cache_name_pos(path, namelen); if (pos < 0) { /* If there isn't, either it is unmerged, or * resolved as "removed" by mistake. We do not * want to do anything in the former case. */ pos = -pos-1; if (pos < active_nr) { struct cache_entry *ce = active_cache[pos]; if (ce_namelen(ce) == namelen && !memcmp(ce->name, path, namelen)) { fprintf(stderr, "%s: skipping still unmerged path.\n", path); goto free_return; } } } /* Grab blobs from given path from HEAD and MERGE_HEAD, * stuff HEAD version in stage #2, * stuff MERGE_HEAD version in stage #3. */ ce_2 = read_one_ent("our", head_sha1, path, namelen, 2); ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3); if (!ce_2 || !ce_3) { ret = -1; goto free_return; } if (!memcmp(ce_2->sha1, ce_3->sha1, 20) && ce_2->ce_mode == ce_3->ce_mode) { fprintf(stderr, "%s: identical in both, skipping.\n", path); goto free_return; } remove_file_from_cache(path); if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) { error("%s: cannot add our version to the index.", path); ret = -1; goto free_return; } if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD)) return 0; error("%s: cannot add their version to the index.", path); ret = -1; free_return: free(ce_2); free(ce_3); return ret; } static void read_head_pointers(void) { if (read_ref(git_path("HEAD"), head_sha1)) die("Cannot read HEAD -- no initial commit yet?"); if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) { fprintf(stderr, "Not in the middle of a merge.\n"); exit(0); } } int main(int ac, char **av) { int i; int err = 0; int newfd; if (ac < 2) usage(unresolve_usage); git_config(git_default_config); /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we * are not doing a merge, so exit with success status. */ read_head_pointers(); /* Otherwise we would need to update the cache. */ newfd= hold_index_file_for_update(&cache_file, get_index_file()); if (newfd < 0) die("unable to create new cachefile"); if (read_cache() < 0) die("cache corrupted"); for (i = 1; i < ac; i++) { char *arg = av[i]; err |= unresolve_one(arg); } if (err) die("Error encountered; index not updated."); if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_index_file(&cache_file)) die("Unable to write new cachefile"); } return 0; }