static void find_non_local_tags(struct transport *transport, struct ref **head, struct ref ***tail) { struct string_list existing_refs = { NULL, 0, 0, 0 }; struct string_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 (!string_list_has_string(&existing_refs, ref_name) && !string_list_has_string(&new_refs, ref_name) && (has_sha1_file(ref->old_sha1) || will_fetch(head, ref->old_sha1))) { string_list_insert(ref_name, &new_refs); rm = alloc_ref(ref_name); rm->peer_ref = alloc_ref(ref_name); hashcpy(rm->old_sha1, ref_sha1); **tail = rm; *tail = &rm->next; } free(ref_name); } string_list_clear(&existing_refs, 0); string_list_clear(&new_refs, 0); }
const char *find_descendant_ref(const char *dirname, const struct string_list *extras, const struct string_list *skip) { int pos; if (!extras) return NULL; /* * Look at the place where dirname would be inserted into * extras. If there is an entry at that position that starts * with dirname (remember, dirname includes the trailing * slash) and is not in skip, then we have a conflict. */ for (pos = string_list_find_insert_index(extras, dirname, 0); pos < extras->nr; pos++) { const char *extra_refname = extras->items[pos].string; if (!starts_with(extra_refname, dirname)) break; if (!skip || !string_list_has_string(skip, extra_refname)) return extra_refname; } return NULL; }
static char *unique_path(struct merge_options *o, 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 (string_list_has_string(&o->current_file_set, newpath) || string_list_has_string(&o->current_directory_set, newpath) || lstat(newpath, &st) == 0) sprintf(p, "_%d", suffix++); string_list_insert(newpath, &o->current_file_set); return newpath; }
static void conflict_rename_rename(struct merge_options *o, 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 (string_list_has_string(&o->current_directory_set, ren1_dst)) { dst_name1 = del[delp++] = unique_path(o, ren1_dst, branch1); output(o, 1, "%s is a directory in %s adding as %s instead", ren1_dst, branch2, dst_name1); remove_file(o, 0, ren1_dst, 0); } if (string_list_has_string(&o->current_directory_set, ren2_dst)) { dst_name2 = del[delp++] = unique_path(o, ren2_dst, branch2); output(o, 1, "%s is a directory in %s adding as %s instead", ren2_dst, branch1, dst_name2); remove_file(o, 0, ren2_dst, 0); } if (o->call_depth) { remove_file_from_cache(dst_name1); remove_file_from_cache(dst_name2); /* * Uncomment to leave the conflicting names in the resulting tree * * update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, dst_name1); * update_file(o, 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 do_plain_rerere(struct repository *r, struct string_list *rr, int fd) { struct string_list conflict = STRING_LIST_INIT_DUP; struct string_list update = STRING_LIST_INIT_DUP; int i; find_conflict(r, &conflict); /* * MERGE_RR records paths with conflicts immediately after * merge failed. Some of the conflicted paths might have been * hand resolved in the working tree since then, but the * initial run would catch all and register their preimages. */ for (i = 0; i < conflict.nr; i++) { struct rerere_id *id; unsigned char hash[GIT_MAX_RAWSZ]; const char *path = conflict.items[i].string; int ret; /* * Ask handle_file() to scan and assign a * conflict ID. No need to write anything out * yet. */ ret = handle_file(r->index, path, hash, NULL); if (ret != 0 && string_list_has_string(rr, path)) { remove_variant(string_list_lookup(rr, path)->util); string_list_remove(rr, path, 1); } if (ret < 1) continue; id = new_rerere_id(hash); string_list_insert(rr, path)->util = id; /* Ensure that the directory exists. */ mkdir_in_gitdir(rerere_path(id, NULL)); } for (i = 0; i < rr->nr; i++) do_rerere_one_path(r->index, &rr->items[i], &update); if (update.nr) update_paths(r, &update); return write_rr(rr, fd); }
int is_transport_allowed(const char *type, int from_user) { const struct string_list *whitelist = protocol_whitelist(); if (whitelist) return string_list_has_string(whitelist, type); switch (get_protocol_config(type)) { case PROTOCOL_ALLOW_ALWAYS: return 1; case PROTOCOL_ALLOW_NEVER: return 0; case PROTOCOL_ALLOW_USER_ONLY: if (from_user < 0) from_user = git_env_bool("GIT_PROTOCOL_FROM_USER", 1); return from_user; } BUG("invalid protocol_allow_config type"); }
static int warn_if_dangling_symref(const char *refname, const struct object_id *oid, int flags, void *cb_data) { struct warn_if_dangling_data *d = cb_data; const char *resolves_to; struct object_id junk; if (!(flags & REF_ISSYMREF)) return 0; resolves_to = resolve_ref_unsafe(refname, 0, junk.hash, NULL); if (!resolves_to || (d->refname ? strcmp(resolves_to, d->refname) : !string_list_has_string(d->refnames, resolves_to))) { return 0; } fprintf(d->fp, d->msg_fmt, refname); fputc('\n', d->fp); return 0; }
/* * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input, * and * (1) strip "^{}" at the end of line if any; * (2) ignore if match is provided and does not head-match refname; * (3) warn if refname is not a well-formed refname and skip; * (4) ignore if refname is a ref that exists in the local repository; * (5) otherwise output the line. */ static int exclude_existing(const char *match) { static struct string_list existing_refs = STRING_LIST_INIT_DUP; char buf[1024]; int matchlen = match ? strlen(match) : 0; for_each_ref(add_existing, &existing_refs); while (fgets(buf, sizeof(buf), stdin)) { char *ref; int len = strlen(buf); if (len > 0 && buf[len - 1] == '\n') buf[--len] = '\0'; if (3 <= len && !strcmp(buf + len - 3, "^{}")) { len -= 3; buf[len] = '\0'; } for (ref = buf + len; buf < ref; ref--) if (isspace(ref[-1])) break; if (match) { int reflen = buf + len - ref; if (reflen < matchlen) continue; if (strncmp(ref, match, matchlen)) continue; } if (check_refname_format(ref, 0)) { warning("ref '%s' ignored", ref); continue; } if (!string_list_has_string(&existing_refs, ref)) { printf("%s\n", buf); } } return 0; }
static int do_plain_rerere(struct string_list *rr, int fd) { struct string_list conflict = STRING_LIST_INIT_DUP; struct string_list update = STRING_LIST_INIT_DUP; int i; find_conflict(&conflict); /* * MERGE_RR records paths with conflicts immediately after merge * failed. Some of the conflicted paths might have been hand resolved * in the working tree since then, but the initial run would catch all * and register their preimages. */ for (i = 0; i < conflict.nr; i++) { const char *path = conflict.items[i].string; if (!string_list_has_string(rr, path)) { unsigned char sha1[20]; char *hex; int ret; ret = handle_file(path, sha1, NULL); if (ret < 1) continue; hex = xstrdup(sha1_to_hex(sha1)); string_list_insert(rr, path)->util = hex; if (mkdir_in_gitdir(git_path("rr-cache/%s", hex))) continue; handle_file(path, NULL, rerere_path(hex, "preimage")); fprintf(stderr, "Recorded preimage for '%s'\n", path); } } /* * Now some of the paths that had conflicts earlier might have been * hand resolved. Others may be similar to a conflict already that * was resolved before. */ for (i = 0; i < rr->nr; i++) { int ret; const char *path = rr->items[i].string; const char *name = (const char *)rr->items[i].util; if (has_rerere_resolution(name)) { if (!merge(name, path)) { const char *msg; if (rerere_autoupdate) { string_list_insert(&update, path); msg = "Staged '%s' using previous resolution.\n"; } else msg = "Resolved '%s' using previous resolution.\n"; fprintf(stderr, msg, path); goto mark_resolved; } } /* Let's see if we have resolved it. */ ret = handle_file(path, NULL, NULL); if (ret) continue; fprintf(stderr, "Recorded resolution for '%s'.\n", path); copy_file(rerere_path(name, "postimage"), path, 0666); mark_resolved: rr->items[i].util = NULL; } if (update.nr) update_paths(&update); return write_rr(rr, fd); }
static int do_plain_rerere(struct string_list *rr, int fd) { struct string_list conflict = STRING_LIST_INIT_DUP; struct string_list update = STRING_LIST_INIT_DUP; int i; find_conflict(&conflict); /* * MERGE_RR records paths with conflicts immediately after * merge failed. Some of the conflicted paths might have been * hand resolved in the working tree since then, but the * initial run would catch all and register their preimages. */ for (i = 0; i < conflict.nr; i++) { struct rerere_id *id; unsigned char sha1[20]; const char *path = conflict.items[i].string; int ret; if (string_list_has_string(rr, path)) continue; /* * Ask handle_file() to scan and assign a * conflict ID. No need to write anything out * yet. */ ret = handle_file(path, sha1, NULL); if (ret < 1) continue; id = new_rerere_id(sha1); string_list_insert(rr, path)->util = id; /* * If the directory does not exist, create * it. mkdir_in_gitdir() will fail with * EEXIST if there already is one. * * NEEDSWORK: make sure "gc" does not remove * preimage without removing the directory. */ if (mkdir_in_gitdir(rerere_path(id, NULL))) continue; /* * We are the first to encounter this * conflict. Ask handle_file() to write the * normalized contents to the "preimage" file. */ handle_file(path, NULL, rerere_path(id, "preimage")); fprintf(stderr, "Recorded preimage for '%s'\n", path); } for (i = 0; i < rr->nr; i++) do_rerere_one_path(&rr->items[i], &update); if (update.nr) update_paths(&update); return write_rr(rr, fd); }
int cmd_mv(int argc, const char **argv, const char *prefix) { int i, newfd, gitmodules_modified = 0; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; struct option builtin_mv_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT__DRY_RUN(&show_only, N_("dry run")), OPT__FORCE(&force, N_("force move/rename even if target exists")), OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")), OPT_END(), }; const char **source, **destination, **dest_path, **submodule_gitfile; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; gitmodules_config(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_mv_options, builtin_mv_usage, 0); if (--argc < 1) usage_with_options(builtin_mv_usage, builtin_mv_options); newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) die(_("index file corrupt")); source = internal_copy_pathspec(prefix, argv, argc, 0); modes = xcalloc(argc, sizeof(enum update_mode)); /* * Keep trailing slash, needed to let * "git mv file no-such-dir/" error out. */ dest_path = internal_copy_pathspec(prefix, argv + argc, 1, KEEP_TRAILING_SLASH); submodule_gitfile = xcalloc(argc, sizeof(char *)); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME); } else { if (argc != 1) die("destination '%s' is not a directory", dest_path[0]); destination = dest_path; } /* Checking */ for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; int length, src_is_dir; const char *bad = NULL; if (show_only) printf(_("Checking rename of '%s' to '%s'\n"), src, dst); length = strlen(src); if (lstat(src, &st) < 0) bad = _("bad source"); else if (!strncmp(src, dst, length) && (dst[length] == 0 || dst[length] == '/')) { bad = _("can not move directory into itself"); } else if ((src_is_dir = S_ISDIR(st.st_mode)) && lstat(dst, &st) == 0) bad = _("cannot move directory over file"); else if (src_is_dir) { int first = cache_name_pos(src, length); if (first >= 0) { struct strbuf submodule_dotgit = STRBUF_INIT; if (!S_ISGITLINK(active_cache[first]->ce_mode)) die (_("Huh? Directory %s is in index and no submodule?"), src); if (!is_staging_gitmodules_ok()) die (_("Please, stage your changes to .gitmodules or stash them to proceed")); strbuf_addf(&submodule_dotgit, "%s/.git", src); submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf); if (submodule_gitfile[i]) submodule_gitfile[i] = xstrdup(submodule_gitfile[i]); else submodule_gitfile[i] = SUBMODULE_WITH_GITDIR; strbuf_release(&submodule_dotgit); } else { const char *src_w_slash = add_slash(src); int last, len_w_slash = length + 1; modes[i] = WORKING_DIRECTORY; first = cache_name_pos(src_w_slash, len_w_slash); if (first >= 0) die (_("Huh? %.*s is in index?"), len_w_slash, src_w_slash); first = -1 - first; for (last = first; last < active_nr; last++) { const char *path = active_cache[last]->name; if (strncmp(path, src_w_slash, len_w_slash)) break; } free((char *)src_w_slash); if (last - first < 1) bad = _("source directory is empty"); else { int j, dst_len; if (last - first > 0) { source = xrealloc(source, (argc + last - first) * sizeof(char *)); destination = xrealloc(destination, (argc + last - first) * sizeof(char *)); modes = xrealloc(modes, (argc + last - first) * sizeof(enum update_mode)); submodule_gitfile = xrealloc(submodule_gitfile, (argc + last - first) * sizeof(char *)); } dst = add_slash(dst); dst_len = strlen(dst); for (j = 0; j < last - first; j++) { const char *path = active_cache[first + j]->name; source[argc + j] = path; destination[argc + j] = prefix_path(dst, dst_len, path + length + 1); modes[argc + j] = INDEX; submodule_gitfile[argc + j] = NULL; } argc += last - first; } } } else if (cache_name_pos(src, length) < 0) bad = _("not under version control"); else if (lstat(dst, &st) == 0) { bad = _("destination exists"); if (force) { /* * only files can overwrite each other: * check both source and destination */ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { if (verbose) warning(_("overwriting '%s'"), dst); bad = NULL; } else bad = _("Cannot overwrite"); } } else if (string_list_has_string(&src_for_dst, dst)) bad = _("multiple sources for the same target"); else if (is_dir_sep(dst[strlen(dst) - 1])) bad = _("destination directory does not exist"); else string_list_insert(&src_for_dst, dst); if (bad) { if (ignore_errors) { if (--argc > 0) { memmove(source + i, source + i + 1, (argc - i) * sizeof(char *)); memmove(destination + i, destination + i + 1, (argc - i) * sizeof(char *)); memmove(modes + i, modes + i + 1, (argc - i) * sizeof(enum update_mode)); memmove(submodule_gitfile + i, submodule_gitfile + i + 1, (argc - i) * sizeof(char *)); i--; } } else die (_("%s, source=%s, destination=%s"), bad, src, dst); } } for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; int pos; if (show_only || verbose) printf(_("Renaming %s to %s\n"), src, dst); if (!show_only && mode != INDEX) { if (rename(src, dst) < 0 && !ignore_errors) die_errno (_("renaming '%s' failed"), src); if (submodule_gitfile[i]) { if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); if (!update_path_in_gitmodules(src, dst)) gitmodules_modified = 1; } } if (mode == WORKING_DIRECTORY) continue; pos = cache_name_pos(src, strlen(src)); assert(pos >= 0); if (!show_only) rename_cache_entry_at(pos, dst); } if (gitmodules_modified) stage_updated_gitmodules(); if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) die(_("Unable to write new index file")); } return 0; }
static void find_non_local_tags(struct transport *transport, struct ref **head, struct ref ***tail) { struct string_list existing_refs = { NULL, 0, 0, 0 }; struct string_list remote_refs = { NULL, 0, 0, 0 }; struct tag_data data = {head, tail}; const struct ref *ref; struct string_list_item *item = NULL; 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; /* * The peeled ref always follows the matching base * ref, so if we see a peeled ref that we don't want * to fetch then we can mark the ref entry in the list * as one to ignore by setting util to NULL. */ if (!suffixcmp(ref->name, "^{}")) { if (item && !has_sha1_file(ref->old_sha1) && !will_fetch(head, ref->old_sha1) && !has_sha1_file(item->util) && !will_fetch(head, item->util)) item->util = NULL; item = NULL; continue; } /* * If item is non-NULL here, then we previously saw a * ref not followed by a peeled reference, so we need * to check if it is a lightweight tag that we want to * fetch. */ if (item && !has_sha1_file(item->util) && !will_fetch(head, item->util)) item->util = NULL; item = NULL; /* skip duplicates and refs that we already have */ if (string_list_has_string(&remote_refs, ref->name) || string_list_has_string(&existing_refs, ref->name)) continue; item = string_list_insert(ref->name, &remote_refs); item->util = (void *)ref->old_sha1; } string_list_clear(&existing_refs, 0); /* * We may have a final lightweight tag that needs to be * checked to see if it needs fetching. */ if (item && !has_sha1_file(item->util) && !will_fetch(head, item->util)) item->util = NULL; /* * For all the tags in the remote_refs string list, call * add_to_tail to add them to the list of refs to be fetched */ for_each_string_list(add_to_tail, &remote_refs, &data); string_list_clear(&remote_refs, 0); }
static int process_renames(struct merge_options *o, struct string_list *a_renames, struct string_list *b_renames) { int clean_merge = 1, i, j; struct string_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; string_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; string_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 string_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].string, b_renames->items[j].string); 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 = o->branch1; branch2 = o->branch2; } else { struct rename *tmp; renames1 = b_renames; renames2 = a_renames; renames2Dst = &a_by_dst; branch1 = o->branch2; branch2 = o->branch1; 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(o, 1, "CONFLICT (rename/rename): " "Rename \"%s\"->\"%s\" in branch \"%s\" " "rename \"%s\"->\"%s\" in \"%s\"%s", src, ren1_dst, branch1, src, ren2_dst, branch2, o->call_depth ? " (left unresolved)": ""); if (o->call_depth) { remove_file_from_cache(src); update_file(o, 0, ren1->pair->one->sha1, ren1->pair->one->mode, src); } conflict_rename_rename(o, ren1, branch1, ren2, branch2); } else { struct merge_file_info mfi; remove_file(o, 1, ren1_src, 1); mfi = merge_file(o, ren1->pair->one, ren1->pair->two, ren2->pair->two, branch1, branch2); if (mfi.merge || !mfi.clean) output(o, 1, "Renaming %s->%s", src, ren1_dst); if (mfi.merge) output(o, 2, "Auto-merging %s", ren1_dst); if (!mfi.clean) { output(o, 1, "CONFLICT (content): merge conflict in %s", ren1_dst); clean_merge = 0; if (!o->call_depth) update_stages(ren1_dst, ren1->pair->one, ren1->pair->two, ren2->pair->two, 1 /* clear */); } update_file(o, mfi.clean, mfi.sha, mfi.mode, ren1_dst); } } else { /* Renamed in 1, maybe changed in 2 */ struct string_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(o, 1, ren1_src, o->call_depth || 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 (string_list_has_string(&o->current_directory_set, ren1_dst)) { clean_merge = 0; output(o, 1, "CONFLICT (rename/directory): Rename %s->%s in %s " " directory %s added in %s", ren1_src, ren1_dst, branch1, ren1_dst, branch2); conflict_rename_dir(o, ren1, branch1); } else if (sha_eq(src_other.sha1, null_sha1)) { clean_merge = 0; output(o, 1, "CONFLICT (rename/delete): Rename %s->%s in %s " "and deleted in %s", ren1_src, ren1_dst, branch1, branch2); update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst); update_stages(ren1_dst, NULL, branch1 == o->branch1 ? ren1->pair->two : NULL, branch1 == o->branch1 ? NULL : ren1->pair->two, 1); } else if (!sha_eq(dst_other.sha1, null_sha1)) { const char *new_path; clean_merge = 0; try_merge = 1; output(o, 1, "CONFLICT (rename/add): Rename %s->%s in %s. " "%s added in %s", ren1_src, ren1_dst, branch1, ren1_dst, branch2); new_path = unique_path(o, ren1_dst, branch2); output(o, 1, "Adding as %s instead", new_path); update_file(o, 0, dst_other.sha1, dst_other.mode, new_path); } else if ((item = string_list_lookup(ren1_dst, renames2Dst))) { ren2 = item->util; clean_merge = 0; ren2->processed = 1; output(o, 1, "CONFLICT (rename/rename): " "Rename %s->%s in %s. " "Rename %s->%s in %s", ren1_src, ren1_dst, branch1, ren2->pair->one->path, ren2->pair->two->path, branch2); conflict_rename_rename_2(o, ren1, branch1, ren2, branch2); } else try_merge = 1; if (try_merge) { struct diff_filespec *one, *a, *b; struct merge_file_info mfi; src_other.path = (char *)ren1_src; one = 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, one, a, b, o->branch1, o->branch2); 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(o, 3, "Skipped %s (merged same as existing)", ren1_dst); else { if (mfi.merge || !mfi.clean) output(o, 1, "Renaming %s => %s", ren1_src, ren1_dst); if (mfi.merge) output(o, 2, "Auto-merging %s", ren1_dst); if (!mfi.clean) { output(o, 1, "CONFLICT (rename/modify): Merge conflict in %s", ren1_dst); clean_merge = 0; if (!o->call_depth) update_stages(ren1_dst, one, a, b, 1); } update_file(o, mfi.clean, mfi.sha, mfi.mode, ren1_dst); } } } } string_list_clear(&a_by_dst, 0); string_list_clear(&b_by_dst, 0); return clean_merge; }
/* Per entry merge function */ static int process_entry(struct merge_options *o, const char *path, struct stage_data *entry) { /* 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(o, 2, "Removing %s", path); /* do not touch working file if it did not exist */ remove_file(o, 1, path, !a_sha); } else { /* Deleted in one and changed in the other */ clean_merge = 0; if (!a_sha) { output(o, 1, "CONFLICT (delete/modify): %s deleted in %s " "and modified in %s. Version %s of %s left in tree.", path, o->branch1, o->branch2, o->branch2, path); update_file(o, 0, b_sha, b_mode, path); } else { output(o, 1, "CONFLICT (delete/modify): %s deleted in %s " "and modified in %s. Version %s of %s left in tree.", path, o->branch2, o->branch1, o->branch1, path); update_file(o, 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 = o->branch1; other_branch = o->branch2; mode = a_mode; sha = a_sha; conf = "file/directory"; } else { add_branch = o->branch2; other_branch = o->branch1; mode = b_mode; sha = b_sha; conf = "directory/file"; } if (string_list_has_string(&o->current_directory_set, path)) { const char *new_path = unique_path(o, path, add_branch); clean_merge = 0; output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. " "Adding %s as %s", conf, path, other_branch, path, new_path); remove_file(o, 0, path, 0); update_file(o, 0, sha, mode, new_path); } else { output(o, 2, "Adding %s", path); update_file(o, 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 one, a, b; if (!o_sha) { reason = "add/add"; o_sha = (unsigned char *)null_sha1; } output(o, 2, "Auto-merging %s", path); one.path = a.path = b.path = (char *)path; hashcpy(one.sha1, o_sha); one.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, &one, &a, &b, o->branch1, o->branch2); clean_merge = mfi.clean; if (mfi.clean) update_file(o, 1, mfi.sha, mfi.mode, path); else if (S_ISGITLINK(mfi.mode)) output(o, 1, "CONFLICT (submodule): Merge conflict in %s " "- needs %s", path, sha1_to_hex(b.sha1)); else { output(o, 1, "CONFLICT (%s): Merge conflict in %s", reason, path); if (o->call_depth) update_file(o, 0, mfi.sha, mfi.mode, path); else update_file_flags(o, 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(o, 1, path, !a_mode); } else die("Fatal merge failure, shouldn't happen."); return clean_merge; }
int is_transport_allowed(const char *type) { const struct string_list *allowed = protocol_whitelist(); return !allowed || string_list_has_string(allowed, type); }
int cmd_mv(int argc, const char **argv, const char *prefix) { int i, newfd; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; struct option builtin_mv_options[] = { OPT__DRY_RUN(&show_only), OPT_BOOLEAN('f', "force", &force, "force move/rename even if target exists"), OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"), OPT_END(), }; const char **source, **destination, **dest_path; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_mv_options, builtin_mv_usage, 0); if (--argc < 1) usage_with_options(builtin_mv_usage, builtin_mv_options); newfd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) die("index file corrupt"); source = copy_pathspec(prefix, argv, argc, 0); modes = xcalloc(argc, sizeof(enum update_mode)); dest_path = copy_pathspec(prefix, argv + argc, 1, 0); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ destination = copy_pathspec(dest_path[0], argv, argc, 1); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); destination = copy_pathspec(dest_path[0], argv, argc, 1); } else { if (argc != 1) usage_with_options(builtin_mv_usage, builtin_mv_options); destination = dest_path; } /* Checking */ for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; int length, src_is_dir; const char *bad = NULL; if (show_only) printf("Checking rename of '%s' to '%s'\n", src, dst); length = strlen(src); if (lstat(src, &st) < 0) bad = "bad source"; else if (!strncmp(src, dst, length) && (dst[length] == 0 || dst[length] == '/')) { bad = "can not move directory into itself"; } else if ((src_is_dir = S_ISDIR(st.st_mode)) && lstat(dst, &st) == 0) bad = "cannot move directory over file"; else if (src_is_dir) { const char *src_w_slash = add_slash(src); int len_w_slash = length + 1; int first, last; modes[i] = WORKING_DIRECTORY; first = cache_name_pos(src_w_slash, len_w_slash); if (first >= 0) die ("Huh? %.*s is in index?", len_w_slash, src_w_slash); first = -1 - first; for (last = first; last < active_nr; last++) { const char *path = active_cache[last]->name; if (strncmp(path, src_w_slash, len_w_slash)) break; } free((char *)src_w_slash); if (last - first < 1) bad = "source directory is empty"; else { int j, dst_len; if (last - first > 0) { source = xrealloc(source, (argc + last - first) * sizeof(char *)); destination = xrealloc(destination, (argc + last - first) * sizeof(char *)); modes = xrealloc(modes, (argc + last - first) * sizeof(enum update_mode)); } dst = add_slash(dst); dst_len = strlen(dst); for (j = 0; j < last - first; j++) { const char *path = active_cache[first + j]->name; source[argc + j] = path; destination[argc + j] = prefix_path(dst, dst_len, path + length + 1); modes[argc + j] = INDEX; } argc += last - first; } } else if (cache_name_pos(src, length) < 0) bad = "not under version control"; else if (lstat(dst, &st) == 0) { bad = "destination exists"; if (force) { /* * only files can overwrite each other: * check both source and destination */ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { warning("%s; will overwrite!", bad); bad = NULL; } else bad = "Cannot overwrite"; } } else if (string_list_has_string(&src_for_dst, dst)) bad = "multiple sources for the same target"; else string_list_insert(&src_for_dst, dst); if (bad) { if (ignore_errors) { if (--argc > 0) { memmove(source + i, source + i + 1, (argc - i) * sizeof(char *)); memmove(destination + i, destination + i + 1, (argc - i) * sizeof(char *)); i--; } } else die ("%s, source=%s, destination=%s", bad, src, dst); } } for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; int pos; if (show_only || verbose) printf("Renaming %s to %s\n", src, dst); if (!show_only && mode != INDEX && rename(src, dst) < 0 && !ignore_errors) die_errno ("renaming '%s' failed", src); if (mode == WORKING_DIRECTORY) continue; pos = cache_name_pos(src, strlen(src)); assert(pos >= 0); if (!show_only) rename_cache_entry_at(pos, dst); } if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) die("Unable to write new index file"); } return 0; }
int cmd_mv(int argc, const char **argv, const char *prefix) { int i, flags, gitmodules_modified = 0; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; struct option builtin_mv_options[] = { OPT__VERBOSE(&verbose, N_("be verbose")), OPT__DRY_RUN(&show_only, N_("dry run")), OPT__FORCE(&force, N_("force move/rename even if target exists")), OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")), OPT_END(), }; const char **source, **destination, **dest_path, **submodule_gitfile; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; struct string_list src_for_dst = STRING_LIST_INIT_NODUP; gitmodules_config(); git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_mv_options, builtin_mv_usage, 0); if (--argc < 1) usage_with_options(builtin_mv_usage, builtin_mv_options); hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); if (read_cache() < 0) die(_("index file corrupt")); source = internal_prefix_pathspec(prefix, argv, argc, 0); modes = xcalloc(argc, sizeof(enum update_mode)); /* * Keep trailing slash, needed to let * "git mv file no-such-dir/" error out, except in the case * "git mv directory no-such-dir/". */ flags = KEEP_TRAILING_SLASH; if (argc == 1 && is_directory(argv[0]) && !is_directory(argv[1])) flags = 0; dest_path = internal_prefix_pathspec(prefix, argv + argc, 1, flags); submodule_gitfile = xcalloc(argc, sizeof(char *)); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); destination = internal_prefix_pathspec(dest_path[0], argv, argc, DUP_BASENAME); } else { if (argc != 1) die(_("destination '%s' is not a directory"), dest_path[0]); destination = dest_path; } /* Checking */ for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; int length, src_is_dir; const char *bad = NULL; if (show_only) printf(_("Checking rename of '%s' to '%s'\n"), src, dst); length = strlen(src); if (lstat(src, &st) < 0) bad = _("bad source"); else if (!strncmp(src, dst, length) && (dst[length] == 0 || dst[length] == '/')) { bad = _("can not move directory into itself"); } else if ((src_is_dir = S_ISDIR(st.st_mode)) && lstat(dst, &st) == 0) bad = _("cannot move directory over file"); else if (src_is_dir) { int first = cache_name_pos(src, length), last; if (first >= 0) prepare_move_submodule(src, first, submodule_gitfile + i); else if (index_range_of_same_dir(src, length, &first, &last) < 1) bad = _("source directory is empty"); else { /* last - first >= 1 */ int j, dst_len, n; modes[i] = WORKING_DIRECTORY; n = argc + last - first; REALLOC_ARRAY(source, n); REALLOC_ARRAY(destination, n); REALLOC_ARRAY(modes, n); REALLOC_ARRAY(submodule_gitfile, n); dst = add_slash(dst); dst_len = strlen(dst); for (j = 0; j < last - first; j++) { const char *path = active_cache[first + j]->name; source[argc + j] = path; destination[argc + j] = prefix_path(dst, dst_len, path + length + 1); modes[argc + j] = INDEX; submodule_gitfile[argc + j] = NULL; } argc += last - first; } } else if (cache_name_pos(src, length) < 0) bad = _("not under version control"); else if (lstat(dst, &st) == 0 && (!ignore_case || strcasecmp(src, dst))) { bad = _("destination exists"); if (force) { /* * only files can overwrite each other: * check both source and destination */ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { if (verbose) warning(_("overwriting '%s'"), dst); bad = NULL; } else bad = _("Cannot overwrite"); } } else if (string_list_has_string(&src_for_dst, dst)) bad = _("multiple sources for the same target"); else if (is_dir_sep(dst[strlen(dst) - 1])) bad = _("destination directory does not exist"); else string_list_insert(&src_for_dst, dst); if (!bad) continue; if (!ignore_errors) die(_("%s, source=%s, destination=%s"), bad, src, dst); if (--argc > 0) { int n = argc - i; memmove(source + i, source + i + 1, n * sizeof(char *)); memmove(destination + i, destination + i + 1, n * sizeof(char *)); memmove(modes + i, modes + i + 1, n * sizeof(enum update_mode)); memmove(submodule_gitfile + i, submodule_gitfile + i + 1, n * sizeof(char *)); i--; } } for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; int pos; if (show_only || verbose) printf(_("Renaming %s to %s\n"), src, dst); if (show_only) continue; if (mode != INDEX && rename(src, dst) < 0) { if (ignore_errors) continue; die_errno(_("renaming '%s' failed"), src); } if (submodule_gitfile[i]) { if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); if (!update_path_in_gitmodules(src, dst)) gitmodules_modified = 1; } if (mode == WORKING_DIRECTORY) continue; pos = cache_name_pos(src, strlen(src)); assert(pos >= 0); if (!show_only) rename_cache_entry_at(pos, dst); } if (gitmodules_modified) stage_updated_gitmodules(); if (active_cache_changed && write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("Unable to write new index file")); return 0; }
static void show_filemodify(struct diff_queue_struct *q, struct diff_options *options, void *data) { int i; struct string_list *changed = data; /* * Handle files below a directory first, in case they are all deleted * and the directory changes to a file or symlink. */ QSORT(q->queue, q->nr, depth_first); for (i = 0; i < q->nr; i++) { struct diff_filespec *ospec = q->queue[i]->one; struct diff_filespec *spec = q->queue[i]->two; switch (q->queue[i]->status) { case DIFF_STATUS_DELETED: printf("D "); print_path(spec->path); string_list_insert(changed, spec->path); putchar('\n'); break; case DIFF_STATUS_COPIED: case DIFF_STATUS_RENAMED: /* * If a change in the file corresponding to ospec->path * has been observed, we cannot trust its contents * because the diff is calculated based on the prior * contents, not the current contents. So, declare a * copy or rename only if there was no change observed. */ if (!string_list_has_string(changed, ospec->path)) { printf("%c ", q->queue[i]->status); print_path(ospec->path); putchar(' '); print_path(spec->path); string_list_insert(changed, spec->path); putchar('\n'); if (!oidcmp(&ospec->oid, &spec->oid) && ospec->mode == spec->mode) break; } /* fallthrough */ case DIFF_STATUS_TYPE_CHANGED: case DIFF_STATUS_MODIFIED: case DIFF_STATUS_ADDED: /* * Links refer to objects in another repositories; * output the SHA-1 verbatim. */ if (no_data || S_ISGITLINK(spec->mode)) printf("M %06o %s ", spec->mode, sha1_to_hex(anonymize ? anonymize_sha1(&spec->oid) : spec->oid.hash)); else { struct object *object = lookup_object(spec->oid.hash); printf("M %06o :%d ", spec->mode, get_object_mark(object)); } print_path(spec->path); string_list_insert(changed, spec->path); putchar('\n'); break; default: die("Unexpected comparison status '%c' for %s, %s", q->queue[i]->status, ospec->path ? ospec->path : "none", spec->path ? spec->path : "none"); } } }