static void collect_submodules_from_diff(struct diff_queue_struct *q, struct diff_options *options, void *data) { int i; struct string_list *needs_pushing = data; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; if (!S_ISGITLINK(p->two->mode)) continue; if (submodule_needs_pushing(p->two->path, p->two->sha1)) string_list_insert(needs_pushing, p->two->path); } }
static int find_conflict(struct string_list *conflict) { int i; if (read_cache() < 0) return error("Could not read index"); for (i = 0; i < active_nr;) { int conflict_type; struct cache_entry *e = active_cache[i]; i = check_one_conflict(i, &conflict_type); if (conflict_type == THREE_STAGED) string_list_insert(conflict, (const char *)e->name); } return 0; }
static void insert_one_record(struct shortlog *log, const char *author, const char *oneline) { struct string_list_item *item; item = string_list_insert(&log->list, author); if (log->summary) item->util = (void *)(UTIL_TO_INT(item) + 1); else { const char *dot3 = log->common_repo_prefix; char *buffer, *p; struct strbuf subject = STRBUF_INIT; const char *eol; /* Skip any leading whitespace, including any blank lines. */ while (*oneline && isspace(*oneline)) oneline++; eol = strchr(oneline, '\n'); if (!eol) eol = oneline + strlen(oneline); if (starts_with(oneline, "[PATCH")) { char *eob = strchr(oneline, ']'); if (eob && (!eol || eob < eol)) oneline = eob + 1; } while (*oneline && isspace(*oneline) && *oneline != '\n') oneline++; format_subject(&subject, oneline, " "); buffer = strbuf_detach(&subject, NULL); if (dot3) { int dot3len = strlen(dot3); if (dot3len > 5) { while ((p = strstr(buffer, dot3)) != NULL) { int taillen = strlen(p) - dot3len; memcpy(p, "/.../", 5); memmove(p + 5, p + dot3len, taillen + 1); } } } if (item->util == NULL) item->util = xcalloc(1, sizeof(struct string_list)); string_list_append(item->util, buffer); } }
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; /* * 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 != 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(sha1); 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(&rr->items[i], &update); if (update.nr) update_paths(&update); return write_rr(rr, fd); }
/* * Scan the index and find paths that have conflicts that rerere can * handle, i.e. the ones that has both stages #2 and #3. * * NEEDSWORK: we do not record or replay a previous "resolve by * deletion" for a delete-modify conflict, as that is inherently risky * without knowing what modification is being discarded. The only * safe case, i.e. both side doing the deletion and modification that * are identical to the previous round, might want to be handled, * though. */ static int find_conflict(struct repository *r, struct string_list *conflict) { int i; if (repo_read_index(r) < 0) return error(_("index file corrupt")); for (i = 0; i < r->index->cache_nr;) { int conflict_type; const struct cache_entry *e = r->index->cache[i]; i = check_one_conflict(r->index, i, &conflict_type); if (conflict_type == THREE_STAGED) string_list_insert(conflict, (const char *)e->name); } return 0; }
/* * Returns an index_entry instance which doesn't have to correspond to * a real cache entry in Git's index. */ static struct stage_data *insert_stage_data(const char *path, struct tree *o, struct tree *a, struct tree *b, struct string_list *entries) { struct string_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); 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); item = string_list_insert(path, entries); item->util = e; return e; }
int refs_rename_ref_available(struct ref_store *refs, const char *old_refname, const char *new_refname) { struct string_list skip = STRING_LIST_INIT_NODUP; struct strbuf err = STRBUF_INIT; int ok; string_list_insert(&skip, old_refname); ok = !refs_verify_refname_available(refs, new_refname, NULL, &skip, &err); if (!ok) error("%s", err.buf); string_list_clear(&skip, 0); strbuf_release(&err); return ok; }
static int find_conflict(struct string_list *conflict) { int i; if (read_cache() < 0) return error("Could not read index"); for (i = 0; i+1 < active_nr; i++) { struct cache_entry *e2 = active_cache[i]; struct cache_entry *e3 = active_cache[i+1]; if (ce_stage(e2) == 2 && ce_stage(e3) == 3 && ce_same_name(e2, e3) && S_ISREG(e2->ce_mode) && S_ISREG(e3->ce_mode)) { string_list_insert((const char *)e2->name, conflict); i++; /* skip over both #2 and #3 */ } } return 0; }
static int rerere_forget_one_path(const char *path, struct string_list *rr) { const char *filename; struct rerere_id *id; unsigned char sha1[20]; int ret; struct string_list_item *item; /* * Recreate the original conflict from the stages in the * index and compute the conflict ID */ ret = handle_cache(path, sha1, NULL); if (ret < 1) return error("Could not parse conflict hunks in '%s'", path); /* Nuke the recorded resolution for the conflict */ id = new_rerere_id(sha1); filename = rerere_path(id, "postimage"); if (unlink(filename)) return (errno == ENOENT ? error("no remembered resolution for %s", path) : error("cannot unlink %s: %s", filename, strerror(errno))); /* * Update the preimage so that the user can resolve the * conflict in the working tree, run us again to record * the postimage. */ handle_cache(path, sha1, rerere_path(id, "preimage")); fprintf(stderr, "Updated preimage for '%s'\n", path); /* * And remember that we can record resolution for this * conflict when the user is done. */ item = string_list_insert(rr, path); free_rerere_id(item); item->util = id; fprintf(stderr, "Forgot resolution for %s\n", path); return 0; }
static int remove_duplicates_in_refs(struct ref **ref, int nr) { struct string_list names = STRING_LIST_INIT_NODUP; int src, dst; for (src = dst = 0; src < nr; src++) { struct string_list_item *item; item = string_list_insert(&names, ref[src]->name); if (item->util) continue; /* already have it */ item->util = ref[src]; if (src != dst) ref[dst] = ref[src]; dst++; } for (src = dst; src < nr; src++) ref[src] = NULL; string_list_clear(&names, 0); return dst; }
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 struct string_list *get_parameters(void) { if (!query_params) { const char *query = getenv("QUERY_STRING"); query_params = xcalloc(1, sizeof(*query_params)); while (query && *query) { char *name = url_decode_parameter_name(&query); char *value = url_decode_parameter_value(&query); struct string_list_item *i; i = string_list_lookup(query_params, name); if (!i) i = string_list_insert(query_params, name); else free(i->util); i->util = value; } } return query_params; }
static int collect_expect(const struct option *opt, const char *arg, int unset) { struct string_list *expect; struct string_list_item *item; struct strbuf label = STRBUF_INIT; const char *colon; if (!arg || unset) die("malformed --expect option"); expect = (struct string_list *)opt->value; colon = strchr(arg, ':'); if (!colon) die("malformed --expect option, lacking a colon"); strbuf_add(&label, arg, colon - arg); item = string_list_insert(expect, strbuf_detach(&label, NULL)); if (item->util) die("malformed --expect option, duplicate %s", label.buf); item->util = (void *)arg; return 0; }
int rerere_remaining(struct string_list *merge_rr) { int i; if (read_cache() < 0) return error("Could not read index"); for (i = 0; i < active_nr;) { int conflict_type; const struct cache_entry *e = active_cache[i]; i = check_one_conflict(i, &conflict_type); if (conflict_type == PUNTED) string_list_insert(merge_rr, (const char *)e->name); else if (conflict_type == RESOLVED) { struct string_list_item *it; it = string_list_lookup(merge_rr, (const char *)e->name); if (it != NULL) { free(it->util); it->util = RERERE_RESOLVED; } } } return 0; }
/* The only error case is to run out of memory in string-list */ void record_resolve_undo(struct index_state *istate, struct cache_entry *ce) { struct string_list_item *lost; struct resolve_undo_info *ui; struct string_list *resolve_undo; int stage = ce_stage(ce); if (!stage) return; if (!istate->resolve_undo) { resolve_undo = xcalloc(1, sizeof(*resolve_undo)); resolve_undo->strdup_strings = 1; istate->resolve_undo = resolve_undo; } resolve_undo = istate->resolve_undo; lost = string_list_insert(resolve_undo, ce->name); if (!lost->util) lost->util = xcalloc(1, sizeof(*ui)); ui = lost->util; hashcpy(ui->sha1[stage - 1], ce->sha1); ui->mode[stage - 1] = ce->ce_mode; }
/* * $GIT_DIR/MERGE_RR file is a collection of records, each of which is * "conflict ID", a HT and pathname, terminated with a NUL, and is * used to keep track of the set of paths that "rerere" may need to * work on (i.e. what is left by the previous invocation of "git * rerere" during the current conflict resolution session). */ static void read_rr(struct repository *r, struct string_list *rr) { struct strbuf buf = STRBUF_INIT; FILE *in = fopen_or_warn(git_path_merge_rr(r), "r"); if (!in) return; while (!strbuf_getwholeline(&buf, in, '\0')) { char *path; unsigned char hash[GIT_MAX_RAWSZ]; struct rerere_id *id; int variant; const unsigned hexsz = the_hash_algo->hexsz; /* There has to be the hash, tab, path and then NUL */ if (buf.len < hexsz + 2 || get_sha1_hex(buf.buf, hash)) die(_("corrupt MERGE_RR")); if (buf.buf[hexsz] != '.') { variant = 0; path = buf.buf + hexsz; } else { errno = 0; variant = strtol(buf.buf + hexsz + 1, &path, 10); if (errno) die(_("corrupt MERGE_RR")); } if (*(path++) != '\t') die(_("corrupt MERGE_RR")); buf.buf[hexsz] = '\0'; id = new_rerere_id_hex(buf.buf); id->variant = variant; string_list_insert(rr, path)->util = id; } strbuf_release(&buf); fclose(in); }
static void read_rr(struct string_list *rr) { unsigned char sha1[20]; char buf[PATH_MAX]; FILE *in = fopen(merge_rr_path, "r"); if (!in) return; while (fread(buf, 40, 1, in) == 1) { int i; char *name; if (get_sha1_hex(buf, sha1)) die("corrupt MERGE_RR"); buf[40] = '\0'; name = xstrdup(buf); if (fgetc(in) != '\t') die("corrupt MERGE_RR"); for (i = 0; i < sizeof(buf) && (buf[i] = fgetc(in)); i++) ; /* do nothing */ if (i == sizeof(buf)) die("filename too long"); string_list_insert(buf, rr)->util = name; } fclose(in); }
/* * $GIT_DIR/MERGE_RR file is a collection of records, each of which is * "conflict ID", a HT and pathname, terminated with a NUL, and is * used to keep track of the set of paths that "rerere" may need to * work on (i.e. what is left by the previous invocation of "git * rerere" during the current conflict resolution session). */ static void read_rr(struct string_list *rr) { struct strbuf buf = STRBUF_INIT; FILE *in = fopen_or_warn(git_path_merge_rr(), "r"); if (!in) return; while (!strbuf_getwholeline(&buf, in, '\0')) { char *path; unsigned char sha1[20]; struct rerere_id *id; int variant; /* There has to be the hash, tab, path and then NUL */ if (buf.len < 42 || get_sha1_hex(buf.buf, sha1)) die("corrupt MERGE_RR"); if (buf.buf[40] != '.') { variant = 0; path = buf.buf + 40; } else { errno = 0; variant = strtol(buf.buf + 41, &path, 10); if (errno) die("corrupt MERGE_RR"); } if (*(path++) != '\t') die("corrupt MERGE_RR"); buf.buf[40] = '\0'; id = new_rerere_id_hex(buf.buf); id->variant = variant; string_list_insert(rr, path)->util = id; } strbuf_release(&buf); fclose(in); }
/* * Take a union of paths in the index and the named tree (typically, "HEAD"), * and return the paths that match the given pattern in list. */ static int list_paths(struct string_list *list, const char *with_tree, const char *prefix, const char **pattern) { int i; char *m; for (i = 0; pattern[i]; i++) ; m = xcalloc(1, i); if (with_tree) overlay_tree_on_cache(with_tree, prefix); for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; if (ce->ce_flags & CE_UPDATE) continue; if (!pathspec_match(pattern, m, ce->name, 0)) continue; string_list_insert(ce->name, list); } return report_path_error(m, pattern, prefix ? strlen(prefix) : 0); }
static int parse_commit(struct strbuf* input, struct string_list* files, struct strbuf* message) { int err; int pos; struct strbuf line; int lineno = 0; enum { BEGIN, FILES, MESSAGE } state = BEGIN; enum { OK, FAILED } result = OK; // if there is no data, just return if (input->len == 0) { return 0; } strbuf_init(&line, 0); pos = 0; while (result == OK) { err = get_strbuf_line(input, &pos, &line); lineno++; if (err != 0) { break; } if (is_comment(&line)) { continue; } switch (state) { case BEGIN: strbuf_trim(&line); if (line.len != 0) { if (0 == strcmp(line.buf, "Files:")) { state = FILES; } else { result = FAILED; } } break; case FILES: strbuf_trim(&line); if (line.len != 0) { if (0 == strcmp(line.buf, "Commit Message:")) { state = MESSAGE; } else if (0 == strip_file_info(&line)) { string_list_insert(line.buf, files); } else { result = FAILED; } } break; case MESSAGE: strbuf_rtrim(&line); strbuf_addbuf(message, &line); strbuf_addch(message, '\n'); break; } } strbuf_trim(message); return result == OK ? 0 : 1; }
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); }
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; }
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; }
static int add_existing(const char *refname, const unsigned char *sha1, int flag, void *cbdata) { struct string_list *list = (struct string_list *)cbdata; string_list_insert(list, refname); return 0; }
int add_reflog_for_walk(struct reflog_walk_info *info, struct commit *commit, const char *name) { timestamp_t timestamp = 0; int recno = -1; struct string_list_item *item; struct complete_reflogs *reflogs; char *branch, *at = strchr(name, '@'); struct commit_reflog *commit_reflog; enum selector_type selector = SELECTOR_NONE; if (commit->object.flags & UNINTERESTING) die ("Cannot walk reflogs for %s", name); branch = xstrdup(name); if (at && at[1] == '{') { char *ep; branch[at - name] = '\0'; recno = strtoul(at + 2, &ep, 10); if (*ep != '}') { recno = -1; timestamp = approxidate(at + 2); selector = SELECTOR_DATE; } else selector = SELECTOR_INDEX; } else recno = 0; item = string_list_lookup(&info->complete_reflogs, branch); if (item) reflogs = item->util; else { if (*branch == '\0') { struct object_id oid; free(branch); branch = resolve_refdup("HEAD", 0, oid.hash, NULL); if (!branch) die ("No current branch"); } reflogs = read_complete_reflog(branch); if (!reflogs || reflogs->nr == 0) { struct object_id oid; char *b; int ret = dwim_log(branch, strlen(branch), oid.hash, &b); if (ret > 1) free(b); else if (ret == 1) { if (reflogs) { free(reflogs->ref); free(reflogs); } free(branch); branch = b; reflogs = read_complete_reflog(branch); } } if (!reflogs || reflogs->nr == 0) { if (reflogs) { free(reflogs->ref); free(reflogs); } free(branch); return -1; } string_list_insert(&info->complete_reflogs, branch)->util = reflogs; } free(branch); commit_reflog = xcalloc(1, sizeof(struct commit_reflog)); if (recno < 0) { commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp); if (commit_reflog->recno < 0) { if (reflogs) { free(reflogs->ref); free(reflogs); } free(commit_reflog); return -1; } } else commit_reflog->recno = reflogs->nr - recno - 1; commit_reflog->selector = selector; commit_reflog->reflogs = reflogs; add_commit_info(commit, commit_reflog, &info->reflogs); return 0; }
/* * The path indicated by rr_item may still have conflict for which we * have a recorded resolution, in which case replay it and optionally * update it. Or it may have been resolved by the user and we may * only have the preimage for that conflict, in which case the result * needs to be recorded as a resolution in a postimage file. */ static void do_rerere_one_path(struct string_list_item *rr_item, struct string_list *update) { const char *path = rr_item->string; struct rerere_id *id = rr_item->util; struct rerere_dir *rr_dir = id->collection; int variant; variant = id->variant; /* Has the user resolved it already? */ if (variant >= 0) { if (!handle_file(path, NULL, NULL)) { copy_file(rerere_path(id, "postimage"), path, 0666); id->collection->status[variant] |= RR_HAS_POSTIMAGE; fprintf_ln(stderr, _("Recorded resolution for '%s'."), path); free_rerere_id(rr_item); rr_item->util = NULL; return; } /* * There may be other variants that can cleanly * replay. Try them and update the variant number for * this one. */ } /* Does any existing resolution apply cleanly? */ for (variant = 0; variant < rr_dir->status_nr; variant++) { const int both = RR_HAS_PREIMAGE | RR_HAS_POSTIMAGE; struct rerere_id vid = *id; if ((rr_dir->status[variant] & both) != both) continue; vid.variant = variant; if (merge(&vid, path)) continue; /* failed to replay */ /* * If there already is a different variant that applies * cleanly, there is no point maintaining our own variant. */ if (0 <= id->variant && id->variant != variant) remove_variant(id); if (rerere_autoupdate) string_list_insert(update, path); else fprintf_ln(stderr, _("Resolved '%s' using previous resolution."), path); free_rerere_id(rr_item); rr_item->util = NULL; return; } /* None of the existing one applies; we need a new variant */ assign_variant(id); variant = id->variant; handle_file(path, NULL, rerere_path(id, "preimage")); if (id->collection->status[variant] & RR_HAS_POSTIMAGE) { const char *path = rerere_path(id, "postimage"); if (unlink(path)) die_errno(_("cannot unlink stray '%s'"), path); id->collection->status[variant] &= ~RR_HAS_POSTIMAGE; } id->collection->status[variant] |= RR_HAS_PREIMAGE; fprintf_ln(stderr, _("Recorded preimage for '%s'"), path); }
static int rerere_forget_one_path(const char *path, struct string_list *rr) { const char *filename; struct rerere_id *id; unsigned char sha1[20]; int ret; struct string_list_item *item; /* * Recreate the original conflict from the stages in the * index and compute the conflict ID */ ret = handle_cache(path, sha1, NULL); if (ret < 1) return error(_("could not parse conflict hunks in '%s'"), path); /* Nuke the recorded resolution for the conflict */ id = new_rerere_id(sha1); for (id->variant = 0; id->variant < id->collection->status_nr; id->variant++) { mmfile_t cur = { NULL, 0 }; mmbuffer_t result = {NULL, 0}; int cleanly_resolved; if (!has_rerere_resolution(id)) continue; handle_cache(path, sha1, rerere_path(id, "thisimage")); if (read_mmfile(&cur, rerere_path(id, "thisimage"))) { free(cur.ptr); error(_("failed to update conflicted state in '%s'"), path); goto fail_exit; } cleanly_resolved = !try_merge(id, path, &cur, &result); free(result.ptr); free(cur.ptr); if (cleanly_resolved) break; } if (id->collection->status_nr <= id->variant) { error(_("no remembered resolution for '%s'"), path); goto fail_exit; } filename = rerere_path(id, "postimage"); if (unlink(filename)) { if (errno == ENOENT) error(_("no remembered resolution for '%s'"), path); else error_errno(_("cannot unlink '%s'"), filename); goto fail_exit; } /* * Update the preimage so that the user can resolve the * conflict in the working tree, run us again to record * the postimage. */ handle_cache(path, sha1, rerere_path(id, "preimage")); fprintf_ln(stderr, _("Updated preimage for '%s'"), path); /* * And remember that we can record resolution for this * conflict when the user is done. */ item = string_list_insert(rr, path); free_rerere_id(item); item->util = id; fprintf(stderr, _("Forgot resolution for '%s'\n"), path); return 0; fail_exit: free(id); return -1; }
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 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); }