static void find_non_local_tags(struct transport *transport, struct ref **head, struct ref ***tail) { struct path_list existing_refs = { NULL, 0, 0, 0 }; struct path_list new_refs = { NULL, 0, 0, 1 }; char *ref_name; int ref_name_len; const unsigned char *ref_sha1; const struct ref *tag_ref; struct ref *rm = NULL; const struct ref *ref; for_each_ref(add_existing, &existing_refs); for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) { if (prefixcmp(ref->name, "refs/tags")) continue; ref_name = xstrdup(ref->name); ref_name_len = strlen(ref_name); ref_sha1 = ref->old_sha1; if (!strcmp(ref_name + ref_name_len - 3, "^{}")) { ref_name[ref_name_len - 3] = 0; tag_ref = transport_get_remote_refs(transport); while (tag_ref) { if (!strcmp(tag_ref->name, ref_name)) { ref_sha1 = tag_ref->old_sha1; break; } tag_ref = tag_ref->next; } } if (!path_list_has_path(&existing_refs, ref_name) && !path_list_has_path(&new_refs, ref_name) && (has_sha1_file(ref->old_sha1) || will_fetch(head, ref->old_sha1))) { path_list_insert(ref_name, &new_refs); rm = alloc_ref_from_str(ref_name); rm->peer_ref = alloc_ref_from_str(ref_name); hashcpy(rm->old_sha1, ref_sha1); **tail = rm; *tail = &rm->next; } free(ref_name); } path_list_clear(&existing_refs, 0); path_list_clear(&new_refs, 0); }
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(path); strcpy(newpath, path); *(p++) = '~'; strcpy(p, branch); for (; *p; ++p) if ('/' == *p) *p = '_'; 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, ¤t_file_set); return newpath; }
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 *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(1, "%s is a directory in %s added as %s instead", ren1_dst, branch2, dst_name1); remove_file(0, ren1_dst, 0); } if (path_list_has_path(¤t_directory_set, ren2_dst)) { dst_name2 = del[delp++] = unique_path(ren2_dst, branch2); output(1, "%s is a directory in %s added as %s instead", ren2_dst, branch1, dst_name2); remove_file(0, ren2_dst, 0); } if (index_only) { remove_file_from_cache(dst_name1); remove_file_from_cache(dst_name2); /* * Uncomment to leave the conflicting names in the resulting tree * * update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, dst_name1); * update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, dst_name2); */ } else { 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]); }
static int process_renames(struct path_list *a_renames, struct path_list *b_renames, const char *a_branch, const char *b_branch) { 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 < 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 < 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 < 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 *branch1, *branch2; const char *ren1_src, *ren1_dst; if (i >= a_renames->nr) { compare = 1; ren2 = b_renames->items[j++].util; } else if (j >= b_renames->nr) { compare = -1; ren1 = a_renames->items[i++].util; } else { compare = strcmp(a_renames->items[i].path, b_renames->items[j].path); 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 */ if (ren1) { renames1 = a_renames; renames2 = b_renames; renames2Dst = &b_by_dst; branch1 = a_branch; branch2 = b_branch; } else { struct rename *tmp; renames1 = b_renames; renames2 = a_renames; renames2Dst = &a_by_dst; branch1 = b_branch; branch2 = a_branch; tmp = ren2; ren2 = ren1; ren1 = tmp; } src = ren1->pair->one->path; ren1->dst_entry->processed = 1; ren1->src_entry->processed = 1; if (ren1->processed) continue; ren1->processed = 1; ren1_src = ren1->pair->one->path; 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) { clean_merge = 0; output(1, "CONFLICT (rename/rename): " "Rename \"%s\"->\"%s\" in branch \"%s\" " "rename \"%s\"->\"%s\" in \"%s\"%s", src, ren1_dst, branch1, src, ren2_dst, branch2, index_only ? " (left unresolved)": ""); if (index_only) { remove_file_from_cache(src); update_file(0, ren1->pair->one->sha1, ren1->pair->one->mode, src); } conflict_rename_rename(ren1, branch1, ren2, branch2); } else { struct merge_file_info mfi; remove_file(1, ren1_src, 1); mfi = merge_file(ren1->pair->one, ren1->pair->two, ren2->pair->two, branch1, branch2); if (mfi.merge || !mfi.clean) output(1, "Renamed %s->%s", src, ren1_dst); if (mfi.merge) output(2, "Auto-merged %s", ren1_dst); if (!mfi.clean) { output(1, "CONFLICT (content): merge conflict in %s", ren1_dst); clean_merge = 0; if (!index_only) update_stages(ren1_dst, 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 try_merge, stage = a_renames == renames1 ? 3: 2; remove_file(1, ren1_src, index_only || stage == 3); hashcpy(src_other.sha1, ren1->src_entry->stages[stage].sha); src_other.mode = ren1->src_entry->stages[stage].mode; hashcpy(dst_other.sha1, ren1->dst_entry->stages[stage].sha); dst_other.mode = ren1->dst_entry->stages[stage].mode; try_merge = 0; if (path_list_has_path(¤t_directory_set, ren1_dst)) { clean_merge = 0; output(1, "CONFLICT (rename/directory): Renamed %s->%s in %s " " directory %s added in %s", ren1_src, ren1_dst, branch1, ren1_dst, branch2); conflict_rename_dir(ren1, branch1); } else if (sha_eq(src_other.sha1, null_sha1)) { clean_merge = 0; output(1, "CONFLICT (rename/delete): Renamed %s->%s in %s " "and deleted in %s", 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 *new_path; clean_merge = 0; try_merge = 1; output(1, "CONFLICT (rename/add): Renamed %s->%s in %s. " "%s added in %s", ren1_src, ren1_dst, branch1, ren1_dst, branch2); new_path = unique_path(ren1_dst, branch2); output(1, "Added 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; clean_merge = 0; ren2->processed = 1; output(1, "CONFLICT (rename/rename): Renamed %s->%s in %s. " "Renamed %s->%s in %s", ren1_src, ren1_dst, branch1, ren2->pair->one->path, ren2->pair->two->path, branch2); conflict_rename_rename_2(ren1, branch1, ren2, branch2); } else try_merge = 1; 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 (a_renames == renames1) { a = ren1->pair->two; b = &src_other; } else { b = ren1->pair->two; a = &src_other; } mfi = merge_file(o, a, b, a_branch, b_branch); if (mfi.clean && sha_eq(mfi.sha, ren1->pair->two->sha1) && mfi.mode == ren1->pair->two->mode) /* * This messaged is part of * t6022 test. If you change * it update the test too. */ output(3, "Skipped %s (merged same as existing)", ren1_dst); else { if (mfi.merge || !mfi.clean) output(1, "Renamed %s => %s", ren1_src, ren1_dst); if (mfi.merge) output(2, "Auto-merged %s", ren1_dst); if (!mfi.clean) { output(1, "CONFLICT (rename/modify): Merge conflict in %s", ren1_dst); clean_merge = 0; if (!index_only) update_stages(ren1_dst, o, a, b, 1); } update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst); } } } } path_list_clear(&a_by_dst, 0); path_list_clear(&b_by_dst, 0); return clean_merge; }
/* Per entry merge function */ static int process_entry(const char *path, struct stage_data *entry, const char *branch1, const char *branch2) { /* printf("processing entry, clean cache: %s\n", index_only ? "yes": "no"); print_index_entry("\tpath: ", entry); */ int clean_merge = 1; unsigned o_mode = entry->stages[1].mode; unsigned a_mode = entry->stages[2].mode; unsigned b_mode = entry->stages[3].mode; unsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode); unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode); unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode); if (o_sha && (!a_sha || !b_sha)) { /* Case A: Deleted in one */ 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 (a_sha) output(2, "Removed %s", path); /* do not touch working file if it did not exist */ remove_file(1, path, !a_sha); } else { /* Deleted in one and changed in the other */ clean_merge = 0; if (!a_sha) { output(1, "CONFLICT (delete/modify): %s deleted in %s " "and modified in %s. Version %s of %s left in tree.", path, branch1, branch2, branch2, path); update_file(0, b_sha, b_mode, path); } else { output(1, "CONFLICT (delete/modify): %s deleted in %s " "and modified in %s. Version %s of %s left in tree.", path, branch2, branch1, branch1, path); update_file(0, a_sha, a_mode, path); } } } else if ((!o_sha && a_sha && !b_sha) || (!o_sha && !a_sha && b_sha)) { /* Case B: Added in one. */ const char *add_branch; const char *other_branch; unsigned mode; const unsigned char *sha; const char *conf; if (a_sha) { add_branch = branch1; other_branch = branch2; mode = a_mode; sha = a_sha; conf = "file/directory"; } else { add_branch = branch2; other_branch = branch1; mode = b_mode; sha = b_sha; conf = "directory/file"; } if (path_list_has_path(¤t_directory_set, path)) { const char *new_path = unique_path(path, add_branch); clean_merge = 0; output(1, "CONFLICT (%s): There is a directory with name %s in %s. " "Added %s as %s", conf, path, other_branch, path, new_path); remove_file(0, path, 0); update_file(0, sha, mode, new_path); } else { output(2, "Added %s", path); update_file(1, sha, mode, path); } } else if (a_sha && b_sha) { /* Case C: Added in both (check for same permissions) and */ /* case D: Modified in both, but differently. */ const char *reason = "content"; struct merge_file_info mfi; struct diff_filespec o, a, b; if (!o_sha) { reason = "add/add"; o_sha = (unsigned char *)null_sha1; } output(2, "Auto-merged %s", path); o.path = a.path = b.path = (char *)path; hashcpy(o.sha1, o_sha); o.mode = o_mode; hashcpy(a.sha1, a_sha); a.mode = a_mode; hashcpy(b.sha1, b_sha); b.mode = b_mode; mfi = merge_file(&o, &a, &b, branch1, branch2); clean_merge = mfi.clean; if (mfi.clean) update_file(1, mfi.sha, mfi.mode, path); else if (S_ISGITLINK(mfi.mode)) output(1, "CONFLICT (submodule): Merge conflict in %s " "- needs %s", path, sha1_to_hex(b.sha1)); else { output(1, "CONFLICT (%s): Merge conflict in %s", reason, path); if (index_only) update_file(0, mfi.sha, mfi.mode, path); else update_file_flags(mfi.sha, mfi.mode, path, 0 /* update_cache */, 1 /* update_working_directory */); } } else if (!o_sha && !a_sha && !b_sha) { /* * this entry was deleted altogether. a_mode == 0 means * we had that path and want to actively remove it. */ remove_file(1, path, !a_mode); } else die("Fatal merge failure, shouldn't happen."); return clean_merge; }