static int commit_index_files(void) { int err = 0; switch (commit_style) { case COMMIT_AS_IS: break; /* nothing to do */ case COMMIT_NORMAL: err = commit_lock_file(&index_lock); break; case COMMIT_PARTIAL: err = commit_lock_file(&index_lock); rollback_lock_file(&false_lock); break; } return err; }
static void update_paths(struct string_list *update) { int i; hold_locked_index(&index_lock, 1); for (i = 0; i < update->nr; i++) { struct string_list_item *item = &update->items[i]; if (add_file_to_cache(item->string, 0)) exit(128); } if (active_cache_changed) { if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) die("Unable to write new index file"); } else rollback_lock_file(&index_lock); }
static void refresh_index_quietly(void) { struct lock_file *lock_file; int fd; lock_file = xcalloc(1, sizeof(struct lock_file)); fd = hold_locked_index(lock_file, 0); if (fd < 0) return; discard_cache(); read_cache(); refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED); if (active_cache_changed && !write_cache(fd, active_cache, active_nr)) commit_locked_index(lock_file); rollback_lock_file(lock_file); }
void rerere_gc(struct string_list *rr) { struct string_list to_remove = STRING_LIST_INIT_DUP; DIR *dir; struct dirent *e; int i, cutoff; time_t now = time(NULL), then; int cutoff_noresolve = 15; int cutoff_resolve = 60; if (setup_rerere(rr, 0) < 0) return; git_config_get_int("gc.rerereresolved", &cutoff_resolve); git_config_get_int("gc.rerereunresolved", &cutoff_noresolve); git_config(git_default_config, NULL); dir = opendir(git_path("rr-cache")); if (!dir) die_errno("unable to open rr-cache directory"); /* Collect stale conflict IDs ... */ while ((e = readdir(dir))) { if (is_dot_or_dotdot(e->d_name)) continue; then = rerere_last_used_at(e->d_name); if (then) { cutoff = cutoff_resolve; } else { then = rerere_created_at(e->d_name); if (!then) continue; cutoff = cutoff_noresolve; } if (then < now - cutoff * 86400) string_list_append(&to_remove, e->d_name); } closedir(dir); /* ... and then remove them one-by-one */ for (i = 0; i < to_remove.nr; i++) unlink_rr_item(dirname_to_id(to_remove.items[i].string)); string_list_clear(&to_remove, 0); rollback_lock_file(&write_lock); }
int commit_lock_file_to(struct lock_file *lk, const char *path) { if (!lk->active) die("BUG: attempt to commit unlocked object to \"%s\"", path); if (close_lock_file(lk)) return -1; if (rename(lk->filename.buf, path)) { int save_errno = errno; rollback_lock_file(lk); errno = save_errno; return -1; } lk->active = 0; strbuf_reset(&lk->filename); return 0; }
static void update_paths(struct string_list *update) { int i; hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); for (i = 0; i < update->nr; i++) { struct string_list_item *item = &update->items[i]; if (add_file_to_cache(item->string, 0)) exit(128); fprintf(stderr, "Staged '%s' using previous resolution.\n", item->string); } if (active_cache_changed) { if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) die("Unable to write new index file"); } else rollback_lock_file(&index_lock); }
static int update_paths(struct string_list *update) { int i; int fd = hold_locked_index(&index_lock, 0); int status = 0; if (fd < 0) return -1; for (i = 0; i < update->nr; i++) { struct string_list_item *item = &update->items[i]; if (add_file_to_cache(item->string, ADD_CACHE_IGNORE_ERRORS)) status = -1; } if (!status && active_cache_changed) { if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) die("Unable to write new index file"); } else if (fd >= 0) rollback_lock_file(&index_lock); return status; }
static int update_shallow_ref(struct command *cmd, struct shallow_info *si) { static struct lock_file shallow_lock; struct sha1_array extra = SHA1_ARRAY_INIT; const char *alt_file; uint32_t mask = 1 << (cmd->index % 32); int i; trace_printf_key(&trace_shallow, "shallow: update_shallow_ref %s\n", cmd->ref_name); for (i = 0; i < si->shallow->nr; i++) if (si->used_shallow[i] && (si->used_shallow[i][cmd->index / 32] & mask) && !delayed_reachability_test(si, i)) sha1_array_append(&extra, si->shallow->sha1[i]); setup_alternate_shallow(&shallow_lock, &alt_file, &extra); if (check_shallow_connected(command_singleton_iterator, 0, cmd, alt_file)) { rollback_lock_file(&shallow_lock); sha1_array_clear(&extra); return -1; } commit_lock_file(&shallow_lock); /* * Make sure setup_alternate_shallow() for the next ref does * not lose these new roots.. */ for (i = 0; i < extra.nr; i++) register_shallow(extra.sha1[i]); si->shallow_ref[cmd->index] = 0; sha1_array_clear(&extra); return 0; }
static int try_merge_strategy(const char *strategy, struct commit_list *common, const char *head_arg) { const char **args; int i = 0, ret; struct commit_list *j; struct strbuf buf = STRBUF_INIT; int index_fd; struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); index_fd = hold_locked_index(lock, 1); refresh_cache(REFRESH_QUIET); if (active_cache_changed && (write_cache(index_fd, active_cache, active_nr) || commit_locked_index(lock))) return error("Unable to write index."); rollback_lock_file(lock); if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) { int clean; struct commit *result; struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); int index_fd; struct commit_list *reversed = NULL; struct merge_options o; if (remoteheads->next) { error("Not handling anything other than two heads merge."); return 2; } init_merge_options(&o); if (!strcmp(strategy, "subtree")) o.subtree_merge = 1; o.branch1 = head_arg; o.branch2 = remoteheads->item->util; for (j = common; j; j = j->next) commit_list_insert(j->item, &reversed); index_fd = hold_locked_index(lock, 1); clean = merge_recursive(&o, lookup_commit(head), remoteheads->item, reversed, &result); if (active_cache_changed && (write_cache(index_fd, active_cache, active_nr) || commit_locked_index(lock))) die ("unable to write %s", get_index_file()); rollback_lock_file(lock); return clean ? 0 : 1; } else { args = xmalloc((4 + commit_list_count(common) + commit_list_count(remoteheads)) * sizeof(char *)); strbuf_addf(&buf, "merge-%s", strategy); args[i++] = buf.buf; for (j = common; j; j = j->next) args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1)); args[i++] = "--"; args[i++] = head_arg; for (j = remoteheads; j; j = j->next) args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1)); args[i] = NULL; ret = run_command_v_opt(args, RUN_GIT_CMD); strbuf_release(&buf); i = 1; for (j = common; j; j = j->next) free((void *)args[i++]); i += 2; for (j = remoteheads; j; j = j->next) free((void *)args[i++]); free(args); discard_cache(); if (read_cache() < 0) die("failed to read the cache"); return ret; } }
struct ref *fetch_pack(struct fetch_pack_args *args, int fd[], struct child_process *conn, const struct ref *ref, const char *dest, struct string_list *sought, char **pack_lockfile) { struct stat st; struct ref *ref_cpy; fetch_pack_setup(); if (args->depth > 0) { if (stat(git_path("shallow"), &st)) st.st_mtime = 0; } if (sought->nr) { sort_string_list(sought); string_list_remove_duplicates(sought, 0); } if (!ref) { packet_flush(fd[1]); die("no matching remote head"); } ref_cpy = do_fetch_pack(args, fd, ref, sought, pack_lockfile); if (args->depth > 0) { static struct lock_file lock; struct cache_time mtime; struct strbuf sb = STRBUF_INIT; char *shallow = git_path("shallow"); int fd; mtime.sec = st.st_mtime; mtime.nsec = ST_MTIME_NSEC(st); if (stat(shallow, &st)) { if (mtime.sec) die("shallow file was removed during fetch"); } else if (st.st_mtime != mtime.sec #ifdef USE_NSEC || ST_MTIME_NSEC(st) != mtime.nsec #endif ) die("shallow file was changed during fetch"); fd = hold_lock_file_for_update(&lock, shallow, LOCK_DIE_ON_ERROR); if (!write_shallow_commits(&sb, 0) || write_in_full(fd, sb.buf, sb.len) != sb.len) { unlink_or_warn(shallow); rollback_lock_file(&lock); } else { commit_lock_file(&lock); } strbuf_release(&sb); } reprepare_packed_git(); return ref_cpy; }
struct ref *fetch_pack(struct fetch_pack_args *args, int fd[], struct child_process *conn, const struct ref *ref, const char *dest, struct ref **sought, int nr_sought, struct oid_array *shallow, char **pack_lockfile, enum protocol_version version) { struct ref *ref_cpy; struct shallow_info si; fetch_pack_setup(); if (nr_sought) nr_sought = remove_duplicates_in_refs(sought, nr_sought); if (args->no_dependents && !args->filter_options.choice) { /* * The protocol does not support requesting that only the * wanted objects be sent, so approximate this by setting a * "blob:none" filter if no filter is already set. This works * for all object types: note that wanted blobs will still be * sent because they are directly specified as a "want". * * NEEDSWORK: Add an option in the protocol to request that * only the wanted objects be sent, and implement it. */ parse_list_objects_filter(&args->filter_options, "blob:none"); } if (version != protocol_v2 && !ref) { packet_flush(fd[1]); die(_("no matching remote head")); } prepare_shallow_info(&si, shallow); if (version == protocol_v2) ref_cpy = do_fetch_pack_v2(args, fd, ref, sought, nr_sought, pack_lockfile); else ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, &si, pack_lockfile); reprepare_packed_git(the_repository); if (!args->cloning && args->deepen) { struct check_connected_options opt = CHECK_CONNECTED_INIT; struct ref *iterator = ref_cpy; opt.shallow_file = alternate_shallow_file; if (args->deepen) opt.is_deepening_fetch = 1; if (check_connected(iterate_ref_map, &iterator, &opt)) { error(_("remote did not send all necessary objects")); free_refs(ref_cpy); ref_cpy = NULL; rollback_lock_file(&shallow_lock); goto cleanup; } args->connectivity_checked = 1; } update_shallow(args, sought, nr_sought, &si); cleanup: clear_shallow_info(&si); return ref_cpy; }
int cmd_update_index(int argc, const char **argv, const char *prefix) { int newfd, entries, has_errors = 0, nul_term_line = 0; enum uc_mode untracked_cache = UC_UNSPECIFIED; int read_from_stdin = 0; int prefix_length = prefix ? strlen(prefix) : 0; int preferred_index_format = 0; char set_executable_bit = 0; struct refresh_params refresh_args = {0, &has_errors}; int lock_error = 0; int split_index = -1; int force_write = 0; int fsmonitor = -1; struct lock_file lock_file = LOCK_INIT; struct parse_opt_ctx_t ctx; strbuf_getline_fn getline_fn; int parseopt_state = PARSE_OPT_UNKNOWN; struct option options[] = { OPT_BIT('q', NULL, &refresh_args.flags, N_("continue refresh even when index needs update"), REFRESH_QUIET), OPT_BIT(0, "ignore-submodules", &refresh_args.flags, N_("refresh: ignore submodules"), REFRESH_IGNORE_SUBMODULES), OPT_SET_INT(0, "add", &allow_add, N_("do not ignore new files"), 1), OPT_SET_INT(0, "replace", &allow_replace, N_("let files replace directories and vice-versa"), 1), OPT_SET_INT(0, "remove", &allow_remove, N_("notice files missing from worktree"), 1), OPT_BIT(0, "unmerged", &refresh_args.flags, N_("refresh even if index contains unmerged entries"), REFRESH_UNMERGED), {OPTION_CALLBACK, 0, "refresh", &refresh_args, NULL, N_("refresh stat information"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, refresh_callback}, {OPTION_CALLBACK, 0, "really-refresh", &refresh_args, NULL, N_("like --refresh, but ignore assume-unchanged setting"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, really_refresh_callback}, {OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL, N_("<mode>,<object>,<path>"), N_("add the specified entry to the index"), PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */ PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, (parse_opt_cb *) cacheinfo_callback}, {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, "(+|-)x", N_("override the executable bit of the listed files"), PARSE_OPT_NONEG, chmod_callback}, {OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL, N_("mark files as \"not changing\""), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, {OPTION_SET_INT, 0, "no-assume-unchanged", &mark_valid_only, NULL, N_("clear assumed-unchanged bit"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, {OPTION_SET_INT, 0, "skip-worktree", &mark_skip_worktree_only, NULL, N_("mark files as \"index-only\""), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL, N_("clear skip-worktree bit"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, OPT_SET_INT(0, "info-only", &info_only, N_("add to index only; do not add content to object database"), 1), OPT_SET_INT(0, "force-remove", &force_remove, N_("remove named paths even if present in worktree"), 1), OPT_BOOL('z', NULL, &nul_term_line, N_("with --stdin: input lines are terminated by null bytes")), {OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL, N_("read list of paths to be updated from standard input"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) stdin_callback}, {OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &nul_term_line, NULL, N_("add entries from standard input to the index"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) stdin_cacheinfo_callback}, {OPTION_LOWLEVEL_CALLBACK, 0, "unresolve", &has_errors, NULL, N_("repopulate stages #2 and #3 for the listed paths"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) unresolve_callback}, {OPTION_LOWLEVEL_CALLBACK, 'g', "again", &has_errors, NULL, N_("only update entries that differ from HEAD"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) reupdate_callback}, OPT_BIT(0, "ignore-missing", &refresh_args.flags, N_("ignore files missing from worktree"), REFRESH_IGNORE_MISSING), OPT_SET_INT(0, "verbose", &verbose, N_("report actions to standard output"), 1), {OPTION_CALLBACK, 0, "clear-resolve-undo", NULL, NULL, N_("(for porcelains) forget saved unresolved conflicts"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, resolve_undo_clear_callback}, OPT_INTEGER(0, "index-version", &preferred_index_format, N_("write index in this format")), OPT_BOOL(0, "split-index", &split_index, N_("enable or disable split index")), OPT_BOOL(0, "untracked-cache", &untracked_cache, N_("enable/disable untracked cache")), OPT_SET_INT(0, "test-untracked-cache", &untracked_cache, N_("test if the filesystem supports untracked cache"), UC_TEST), OPT_SET_INT(0, "force-untracked-cache", &untracked_cache, N_("enable untracked cache without testing the filesystem"), UC_FORCE), OPT_SET_INT(0, "force-write-index", &force_write, N_("write out the index even if is not flagged as changed"), 1), OPT_BOOL(0, "fsmonitor", &fsmonitor, N_("enable or disable file system monitor")), {OPTION_SET_INT, 0, "fsmonitor-valid", &mark_fsmonitor_only, NULL, N_("mark files as fsmonitor valid"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, {OPTION_SET_INT, 0, "no-fsmonitor-valid", &mark_fsmonitor_only, NULL, N_("clear fsmonitor valid bit"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, OPT_END() }; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(update_index_usage, options); git_config(git_default_config, NULL); /* we will diagnose later if it turns out that we need to update it */ newfd = hold_locked_index(&lock_file, 0); if (newfd < 0) lock_error = errno; entries = read_cache(); if (entries < 0) die("cache corrupted"); /* * Custom copy of parse_options() because we want to handle * filename arguments as they come. */ parse_options_start(&ctx, argc, argv, prefix, options, PARSE_OPT_STOP_AT_NON_OPTION); while (ctx.argc) { if (parseopt_state != PARSE_OPT_DONE) parseopt_state = parse_options_step(&ctx, options, update_index_usage); if (!ctx.argc) break; switch (parseopt_state) { case PARSE_OPT_HELP: case PARSE_OPT_ERROR: exit(129); case PARSE_OPT_NON_OPTION: case PARSE_OPT_DONE: { const char *path = ctx.argv[0]; char *p; setup_work_tree(); p = prefix_path(prefix, prefix_length, path); update_one(p); if (set_executable_bit) chmod_path(set_executable_bit, p); free(p); ctx.argc--; ctx.argv++; break; } case PARSE_OPT_UNKNOWN: if (ctx.argv[0][1] == '-') error("unknown option '%s'", ctx.argv[0] + 2); else error("unknown switch '%c'", *ctx.opt); usage_with_options(update_index_usage, options); } } argc = parse_options_end(&ctx); getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; if (preferred_index_format) { if (preferred_index_format < INDEX_FORMAT_LB || INDEX_FORMAT_UB < preferred_index_format) die("index-version %d not in range: %d..%d", preferred_index_format, INDEX_FORMAT_LB, INDEX_FORMAT_UB); if (the_index.version != preferred_index_format) active_cache_changed |= SOMETHING_CHANGED; the_index.version = preferred_index_format; } if (read_from_stdin) { struct strbuf buf = STRBUF_INIT; struct strbuf unquoted = STRBUF_INIT; setup_work_tree(); while (getline_fn(&buf, stdin) != EOF) { char *p; if (!nul_term_line && buf.buf[0] == '"') { strbuf_reset(&unquoted); if (unquote_c_style(&unquoted, buf.buf, NULL)) die("line is badly quoted"); strbuf_swap(&buf, &unquoted); } p = prefix_path(prefix, prefix_length, buf.buf); update_one(p); if (set_executable_bit) chmod_path(set_executable_bit, p); free(p); } strbuf_release(&unquoted); strbuf_release(&buf); } if (split_index > 0) { if (git_config_get_split_index() == 0) warning(_("core.splitIndex is set to false; " "remove or change it, if you really want to " "enable split index")); if (the_index.split_index) the_index.cache_changed |= SPLIT_INDEX_ORDERED; else add_split_index(&the_index); } else if (!split_index) { if (git_config_get_split_index() == 1) warning(_("core.splitIndex is set to true; " "remove or change it, if you really want to " "disable split index")); remove_split_index(&the_index); } switch (untracked_cache) { case UC_UNSPECIFIED: break; case UC_DISABLE: if (git_config_get_untracked_cache() == 1) warning(_("core.untrackedCache is set to true; " "remove or change it, if you really want to " "disable the untracked cache")); remove_untracked_cache(&the_index); report(_("Untracked cache disabled")); break; case UC_TEST: setup_work_tree(); return !test_if_untracked_cache_is_supported(); case UC_ENABLE: case UC_FORCE: if (git_config_get_untracked_cache() == 0) warning(_("core.untrackedCache is set to false; " "remove or change it, if you really want to " "enable the untracked cache")); add_untracked_cache(&the_index); report(_("Untracked cache enabled for '%s'"), get_git_work_tree()); break; default: BUG("bad untracked_cache value: %d", untracked_cache); } if (fsmonitor > 0) { if (git_config_get_fsmonitor() == 0) warning(_("core.fsmonitor is unset; " "set it if you really want to " "enable fsmonitor")); add_fsmonitor(&the_index); report(_("fsmonitor enabled")); } else if (!fsmonitor) { if (git_config_get_fsmonitor() == 1) warning(_("core.fsmonitor is set; " "remove it if you really want to " "disable fsmonitor")); remove_fsmonitor(&the_index); report(_("fsmonitor disabled")); } if (active_cache_changed || force_write) { if (newfd < 0) { if (refresh_args.flags & REFRESH_QUIET) exit(128); unable_to_lock_die(get_index_file(), lock_error); } if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die("Unable to write new index file"); } rollback_lock_file(&lock_file); return has_errors ? 1 : 0; }
static char *prepare_index(int argc, const char **argv, const char *prefix, const struct commit *current_head, int is_status) { int fd; struct string_list partial; struct pathspec pathspec; int refresh_flags = REFRESH_QUIET; if (is_status) refresh_flags |= REFRESH_UNMERGED; parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_FULL, prefix, argv); if (read_cache_preload(&pathspec) < 0) die(_("index file corrupt")); if (interactive) { char *old_index_env = NULL; fd = hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die(_("unable to create temporary index")); old_index_env = getenv(INDEX_ENVIRONMENT); setenv(INDEX_ENVIRONMENT, index_lock.filename, 1); if (interactive_add(argc, argv, prefix, patch_interactive) != 0) die(_("interactive add failed")); if (old_index_env && *old_index_env) setenv(INDEX_ENVIRONMENT, old_index_env, 1); else unsetenv(INDEX_ENVIRONMENT); discard_cache(); read_cache_from(index_lock.filename); commit_style = COMMIT_NORMAL; return index_lock.filename; } /* * Non partial, non as-is commit. * * (1) get the real index; * (2) update the_index as necessary; * (3) write the_index out to the real index (still locked); * (4) return the name of the locked index file. * * The caller should run hooks on the locked real index, and * (A) if all goes well, commit the real index; * (B) on failure, rollback the real index. */ if (all || (also && pathspec.nr)) { fd = hold_locked_index(&index_lock, 1); add_files_to_cache(also ? prefix : NULL, &pathspec, 0); refresh_cache_or_die(refresh_flags); update_main_cache_tree(WRITE_TREE_SILENT); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die(_("unable to write new_index file")); commit_style = COMMIT_NORMAL; return index_lock.filename; } /* * As-is commit. * * (1) return the name of the real index file. * * The caller should run hooks on the real index, * and create commit from the_index. * We still need to refresh the index here. */ if (!only && !pathspec.nr) { fd = hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); if (active_cache_changed) { update_main_cache_tree(WRITE_TREE_SILENT); if (write_cache(fd, active_cache, active_nr) || commit_locked_index(&index_lock)) die(_("unable to write new_index file")); } else { rollback_lock_file(&index_lock); } commit_style = COMMIT_AS_IS; return get_index_file(); } /* * A partial commit. * * (0) find the set of affected paths; * (1) get lock on the real index file; * (2) update the_index with the given paths; * (3) write the_index out to the real index (still locked); * (4) get lock on the false index file; * (5) reset the_index from HEAD; * (6) update the_index the same way as (2); * (7) write the_index out to the false index file; * (8) return the name of the false index file (still locked); * * The caller should run hooks on the locked false index, and * create commit from it. Then * (A) if all goes well, commit the real index; * (B) on failure, rollback the real index; * In either case, rollback the false index. */ commit_style = COMMIT_PARTIAL; if (whence != FROM_COMMIT) { if (whence == FROM_MERGE) die(_("cannot do a partial commit during a merge.")); else if (whence == FROM_CHERRY_PICK) die(_("cannot do a partial commit during a cherry-pick.")); } memset(&partial, 0, sizeof(partial)); partial.strdup_strings = 1; if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec)) exit(1); discard_cache(); if (read_cache() < 0) die(_("cannot read the index")); fd = hold_locked_index(&index_lock, 1); add_remove_files(&partial); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die(_("unable to write new_index file")); fd = hold_lock_file_for_update(&false_lock, git_path("next-index-%"PRIuMAX, (uintmax_t) getpid()), LOCK_DIE_ON_ERROR); create_base_index(current_head); add_remove_files(&partial); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&false_lock)) die(_("unable to write temporary index file")); discard_cache(); read_cache_from(false_lock.filename); return false_lock.filename; }
/* * If value==NULL, unset in (remove from) config, * if value_regex!=NULL, disregard key/value pairs where value does not match. * if multi_replace==0, nothing, or only one matching key/value is replaced, * else all matching key/values (regardless how many) are removed, * before the new pair is written. * * Returns 0 on success. * * This function does this: * * - it locks the config file by creating ".git/config.lock" * * - it then parses the config using store_aux() as validator to find * the position on the key/value pair to replace. If it is to be unset, * it must be found exactly once. * * - the config file is mmap()ed and the part before the match (if any) is * written to the lock file, then the changed part and the rest. * * - the config file is removed and the lock file rename()d to it. * */ int git_config_set_multivar(const char *key, const char *value, const char *value_regex, int multi_replace) { int fd = -1, in_fd; int ret; char *config_filename; struct lock_file *lock = NULL; if (config_exclusive_filename) config_filename = xstrdup(config_exclusive_filename); else config_filename = git_pathdup("config"); /* parse-key returns negative; flip the sign to feed exit(3) */ ret = 0 - git_config_parse_key(key, &store.key, &store.baselen); if (ret) goto out_free; store.multi_replace = multi_replace; /* * The lock serves a purpose in addition to locking: the new * contents of .git/config will be written into it. */ lock = xcalloc(sizeof(struct lock_file), 1); fd = hold_lock_file_for_update(lock, config_filename, 0); if (fd < 0) { error("could not lock config file %s: %s", config_filename, strerror(errno)); free(store.key); ret = CONFIG_NO_LOCK; goto out_free; } /* * If .git/config does not exist yet, write a minimal version. */ in_fd = open(config_filename, O_RDONLY); if ( in_fd < 0 ) { free(store.key); if ( ENOENT != errno ) { error("opening %s: %s", config_filename, strerror(errno)); ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */ goto out_free; } /* if nothing to unset, error out */ if (value == NULL) { ret = CONFIG_NOTHING_SET; goto out_free; } store.key = (char *)key; if (!store_write_section(fd, key) || !store_write_pair(fd, key, value)) goto write_err_out; } else { struct stat st; char *contents; size_t contents_sz, copy_begin, copy_end; int i, new_line = 0; if (value_regex == NULL) store.value_regex = NULL; else { if (value_regex[0] == '!') { store.do_not_match = 1; value_regex++; } else store.do_not_match = 0; store.value_regex = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(store.value_regex, value_regex, REG_EXTENDED)) { error("invalid pattern: %s", value_regex); free(store.value_regex); ret = CONFIG_INVALID_PATTERN; goto out_free; } } store.offset[0] = 0; store.state = START; store.seen = 0; /* * After this, store.offset will contain the *end* offset * of the last match, or remain at 0 if no match was found. * As a side effect, we make sure to transform only a valid * existing config file. */ if (git_config_from_file(store_aux, config_filename, NULL)) { error("invalid config file %s", config_filename); free(store.key); if (store.value_regex != NULL) { regfree(store.value_regex); free(store.value_regex); } ret = CONFIG_INVALID_FILE; goto out_free; } free(store.key); if (store.value_regex != NULL) { regfree(store.value_regex); free(store.value_regex); } /* if nothing to unset, or too many matches, error out */ if ((store.seen == 0 && value == NULL) || (store.seen > 1 && multi_replace == 0)) { ret = CONFIG_NOTHING_SET; goto out_free; } fstat(in_fd, &st); contents_sz = xsize_t(st.st_size); contents = xmmap(NULL, contents_sz, PROT_READ, MAP_PRIVATE, in_fd, 0); close(in_fd); if (store.seen == 0) store.seen = 1; for (i = 0, copy_begin = 0; i < store.seen; i++) { if (store.offset[i] == 0) { store.offset[i] = copy_end = contents_sz; } else if (store.state != KEY_SEEN) { copy_end = store.offset[i]; } else copy_end = find_beginning_of_line( contents, contents_sz, store.offset[i]-2, &new_line); if (copy_end > 0 && contents[copy_end-1] != '\n') new_line = 1; /* write the first part of the config */ if (copy_end > copy_begin) { if (write_in_full(fd, contents + copy_begin, copy_end - copy_begin) < copy_end - copy_begin) goto write_err_out; if (new_line && write_str_in_full(fd, "\n") != 1) goto write_err_out; } copy_begin = store.offset[i]; } /* write the pair (value == NULL means unset) */ if (value != NULL) { if (store.state == START) { if (!store_write_section(fd, key)) goto write_err_out; } if (!store_write_pair(fd, key, value)) goto write_err_out; } /* write the rest of the config */ if (copy_begin < contents_sz) if (write_in_full(fd, contents + copy_begin, contents_sz - copy_begin) < contents_sz - copy_begin) goto write_err_out; munmap(contents, contents_sz); } if (commit_lock_file(lock) < 0) { error("could not commit config file %s", config_filename); ret = CONFIG_NO_WRITE; goto out_free; } /* * lock is committed, so don't try to roll it back below. * NOTE: Since lockfile.c keeps a linked list of all created * lock_file structures, it isn't safe to free(lock). It's * better to just leave it hanging around. */ lock = NULL; ret = 0; out_free: if (lock) rollback_lock_file(lock); free(config_filename); return ret; write_err_out: ret = write_error(lock->filename); goto out_free; }
static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, int argc, const char **argv) { char tmpdir[PATH_MAX]; struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT; struct strbuf rpath = STRBUF_INIT, buf = STRBUF_INIT; struct strbuf ldir = STRBUF_INIT, rdir = STRBUF_INIT; struct strbuf wtdir = STRBUF_INIT; char *lbase_dir, *rbase_dir; size_t ldir_len, rdir_len, wtdir_len; const char *workdir, *tmp; int ret = 0, i; FILE *fp; struct hashmap working_tree_dups, submodules, symlinks2; struct hashmap_iter iter; struct pair_entry *entry; struct index_state wtindex; struct checkout lstate, rstate; int rc, flags = RUN_GIT_CMD, err = 0; struct child_process child = CHILD_PROCESS_INIT; const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL }; struct hashmap wt_modified, tmp_modified; int indices_loaded = 0; workdir = get_git_work_tree(); /* Setup temp directories */ tmp = getenv("TMPDIR"); xsnprintf(tmpdir, sizeof(tmpdir), "%s/git-difftool.XXXXXX", tmp ? tmp : "/tmp"); if (!mkdtemp(tmpdir)) return error("could not create '%s'", tmpdir); strbuf_addf(&ldir, "%s/left/", tmpdir); strbuf_addf(&rdir, "%s/right/", tmpdir); strbuf_addstr(&wtdir, workdir); if (!wtdir.len || !is_dir_sep(wtdir.buf[wtdir.len - 1])) strbuf_addch(&wtdir, '/'); mkdir(ldir.buf, 0700); mkdir(rdir.buf, 0700); memset(&wtindex, 0, sizeof(wtindex)); memset(&lstate, 0, sizeof(lstate)); lstate.base_dir = lbase_dir = xstrdup(ldir.buf); lstate.base_dir_len = ldir.len; lstate.force = 1; memset(&rstate, 0, sizeof(rstate)); rstate.base_dir = rbase_dir = xstrdup(rdir.buf); rstate.base_dir_len = rdir.len; rstate.force = 1; ldir_len = ldir.len; rdir_len = rdir.len; wtdir_len = wtdir.len; hashmap_init(&working_tree_dups, (hashmap_cmp_fn)working_tree_entry_cmp, NULL, 0); hashmap_init(&submodules, (hashmap_cmp_fn)pair_cmp, NULL, 0); hashmap_init(&symlinks2, (hashmap_cmp_fn)pair_cmp, NULL, 0); child.no_stdin = 1; child.git_cmd = 1; child.use_shell = 0; child.clean_on_exit = 1; child.dir = prefix; child.out = -1; argv_array_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z", NULL); for (i = 0; i < argc; i++) argv_array_push(&child.args, argv[i]); if (start_command(&child)) die("could not obtain raw diff"); fp = xfdopen(child.out, "r"); /* Build index info for left and right sides of the diff */ i = 0; while (!strbuf_getline_nul(&info, fp)) { int lmode, rmode; struct object_id loid, roid; char status; const char *src_path, *dst_path; if (starts_with(info.buf, "::")) die(N_("combined diff formats('-c' and '--cc') are " "not supported in\n" "directory diff mode('-d' and '--dir-diff').")); if (parse_index_info(info.buf, &lmode, &rmode, &loid, &roid, &status)) break; if (strbuf_getline_nul(&lpath, fp)) break; src_path = lpath.buf; i++; if (status != 'C' && status != 'R') { dst_path = src_path; } else { if (strbuf_getline_nul(&rpath, fp)) break; dst_path = rpath.buf; } if (S_ISGITLINK(lmode) || S_ISGITLINK(rmode)) { strbuf_reset(&buf); strbuf_addf(&buf, "Subproject commit %s", oid_to_hex(&loid)); add_left_or_right(&submodules, src_path, buf.buf, 0); strbuf_reset(&buf); strbuf_addf(&buf, "Subproject commit %s", oid_to_hex(&roid)); if (!oidcmp(&loid, &roid)) strbuf_addstr(&buf, "-dirty"); add_left_or_right(&submodules, dst_path, buf.buf, 1); continue; } if (S_ISLNK(lmode)) { char *content = get_symlink(&loid, src_path); add_left_or_right(&symlinks2, src_path, content, 0); free(content); } if (S_ISLNK(rmode)) { char *content = get_symlink(&roid, dst_path); add_left_or_right(&symlinks2, dst_path, content, 1); free(content); } if (lmode && status != 'C') { if (checkout_path(lmode, &loid, src_path, &lstate)) { ret = error("could not write '%s'", src_path); goto finish; } } if (rmode && !S_ISLNK(rmode)) { struct working_tree_entry *entry; /* Avoid duplicate working_tree entries */ FLEX_ALLOC_STR(entry, path, dst_path); hashmap_entry_init(entry, strhash(dst_path)); if (hashmap_get(&working_tree_dups, entry, NULL)) { free(entry); continue; } hashmap_add(&working_tree_dups, entry); if (!use_wt_file(workdir, dst_path, &roid)) { if (checkout_path(rmode, &roid, dst_path, &rstate)) { ret = error("could not write '%s'", dst_path); goto finish; } } else if (!is_null_oid(&roid)) { /* * Changes in the working tree need special * treatment since they are not part of the * index. */ struct cache_entry *ce2 = make_cache_entry(rmode, roid.hash, dst_path, 0, 0); add_index_entry(&wtindex, ce2, ADD_CACHE_JUST_APPEND); add_path(&rdir, rdir_len, dst_path); if (ensure_leading_directories(rdir.buf)) { ret = error("could not create " "directory for '%s'", dst_path); goto finish; } add_path(&wtdir, wtdir_len, dst_path); if (symlinks) { if (symlink(wtdir.buf, rdir.buf)) { ret = error_errno("could not symlink '%s' to '%s'", wtdir.buf, rdir.buf); goto finish; } } else { struct stat st; if (stat(wtdir.buf, &st)) st.st_mode = 0644; if (copy_file(rdir.buf, wtdir.buf, st.st_mode)) { ret = error("could not copy '%s' to '%s'", wtdir.buf, rdir.buf); goto finish; } } } } } fclose(fp); fp = NULL; if (finish_command(&child)) { ret = error("error occurred running diff --raw"); goto finish; } if (!i) goto finish; /* * Changes to submodules require special treatment.This loop writes a * temporary file to both the left and right directories to show the * change in the recorded SHA1 for the submodule. */ hashmap_iter_init(&submodules, &iter); while ((entry = hashmap_iter_next(&iter))) { if (*entry->left) { add_path(&ldir, ldir_len, entry->path); ensure_leading_directories(ldir.buf); write_file(ldir.buf, "%s", entry->left); } if (*entry->right) { add_path(&rdir, rdir_len, entry->path); ensure_leading_directories(rdir.buf); write_file(rdir.buf, "%s", entry->right); } } /* * Symbolic links require special treatment.The standard "git diff" * shows only the link itself, not the contents of the link target. * This loop replicates that behavior. */ hashmap_iter_init(&symlinks2, &iter); while ((entry = hashmap_iter_next(&iter))) { if (*entry->left) { add_path(&ldir, ldir_len, entry->path); ensure_leading_directories(ldir.buf); write_file(ldir.buf, "%s", entry->left); } if (*entry->right) { add_path(&rdir, rdir_len, entry->path); ensure_leading_directories(rdir.buf); write_file(rdir.buf, "%s", entry->right); } } strbuf_release(&buf); strbuf_setlen(&ldir, ldir_len); helper_argv[1] = ldir.buf; strbuf_setlen(&rdir, rdir_len); helper_argv[2] = rdir.buf; if (extcmd) { helper_argv[0] = extcmd; flags = 0; } else setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1); rc = run_command_v_opt(helper_argv, flags); /* * If the diff includes working copy files and those * files were modified during the diff, then the changes * should be copied back to the working tree. * Do not copy back files when symlinks are used and the * external tool did not replace the original link with a file. * * These hashes are loaded lazily since they aren't needed * in the common case of --symlinks and the difftool updating * files through the symlink. */ hashmap_init(&wt_modified, (hashmap_cmp_fn)path_entry_cmp, NULL, wtindex.cache_nr); hashmap_init(&tmp_modified, (hashmap_cmp_fn)path_entry_cmp, NULL, wtindex.cache_nr); for (i = 0; i < wtindex.cache_nr; i++) { struct hashmap_entry dummy; const char *name = wtindex.cache[i]->name; struct stat st; add_path(&rdir, rdir_len, name); if (lstat(rdir.buf, &st)) continue; if ((symlinks && S_ISLNK(st.st_mode)) || !S_ISREG(st.st_mode)) continue; if (!indices_loaded) { static struct lock_file lock; strbuf_reset(&buf); strbuf_addf(&buf, "%s/wtindex", tmpdir); if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 || write_locked_index(&wtindex, &lock, COMMIT_LOCK)) { ret = error("could not write %s", buf.buf); rollback_lock_file(&lock); goto finish; } changed_files(&wt_modified, buf.buf, workdir); strbuf_setlen(&rdir, rdir_len); changed_files(&tmp_modified, buf.buf, rdir.buf); add_path(&rdir, rdir_len, name); indices_loaded = 1; } hashmap_entry_init(&dummy, strhash(name)); if (hashmap_get(&tmp_modified, &dummy, name)) { add_path(&wtdir, wtdir_len, name); if (hashmap_get(&wt_modified, &dummy, name)) { warning(_("both files modified: '%s' and '%s'."), wtdir.buf, rdir.buf); warning(_("working tree file has been left.")); warning("%s", ""); err = 1; } else if (unlink(wtdir.buf) || copy_file(wtdir.buf, rdir.buf, st.st_mode)) warning_errno(_("could not copy '%s' to '%s'"), rdir.buf, wtdir.buf); } } if (err) { warning(_("temporary files exist in '%s'."), tmpdir); warning(_("you may want to cleanup or recover these.")); exit(1); } else exit_cleanup(tmpdir, rc); finish: if (fp) fclose(fp); free(lbase_dir); free(rbase_dir); strbuf_release(&ldir); strbuf_release(&rdir); strbuf_release(&wtdir); strbuf_release(&buf); return ret; }
static int try_merge_strategy(const char *strategy, struct commit_list *common, struct commit_list *remoteheads, struct commit *head, const char *head_arg) { int index_fd; struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); index_fd = hold_locked_index(lock, 1); refresh_cache(REFRESH_QUIET); if (active_cache_changed && (write_cache(index_fd, active_cache, active_nr) || commit_locked_index(lock))) return error(_("Unable to write index.")); rollback_lock_file(lock); if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) { int clean, x; struct commit *result; struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); int index_fd; struct commit_list *reversed = NULL; struct merge_options o; struct commit_list *j; if (remoteheads->next) { error(_("Not handling anything other than two heads merge.")); return 2; } init_merge_options(&o); if (!strcmp(strategy, "subtree")) o.subtree_shift = ""; o.renormalize = option_renormalize; o.show_rename_progress = show_progress == -1 ? isatty(2) : show_progress; for (x = 0; x < xopts_nr; x++) if (parse_merge_opt(&o, xopts[x])) die(_("Unknown option for merge-recursive: -X%s"), xopts[x]); o.branch1 = head_arg; o.branch2 = merge_remote_util(remoteheads->item)->name; for (j = common; j; j = j->next) commit_list_insert(j->item, &reversed); index_fd = hold_locked_index(lock, 1); clean = merge_recursive(&o, head, remoteheads->item, reversed, &result); if (active_cache_changed && (write_cache(index_fd, active_cache, active_nr) || commit_locked_index(lock))) die (_("unable to write %s"), get_index_file()); rollback_lock_file(lock); return clean ? 0 : 1; } else { return try_merge_command(strategy, xopts_nr, xopts, common, head_arg, remoteheads); } }
int cmd_status(int argc, const char **argv, const char *prefix) { struct wt_status s; int fd; unsigned char sha1[20]; static struct option builtin_status_options[] = { OPT__VERBOSE(&verbose, "be verbose"), OPT_SET_INT('s', "short", &status_format, "show status concisely", STATUS_FORMAT_SHORT), OPT_BOOLEAN('b', "branch", &status_show_branch, "show branch information"), OPT_SET_INT(0, "porcelain", &status_format, "show porcelain output format", STATUS_FORMAT_PORCELAIN), OPT_BOOLEAN('z', "null", &null_termination, "terminate entries with NUL"), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, OPT_BOOLEAN(0, "ignored", &show_ignored_in_status, "show ignored files"), { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when", "ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, OPT_END(), }; if (null_termination && status_format == STATUS_FORMAT_LONG) status_format = STATUS_FORMAT_PORCELAIN; wt_status_prepare(&s); gitmodules_config(); git_config(git_status_config, &s); in_merge = file_exists(git_path("MERGE_HEAD")); argc = parse_options(argc, argv, prefix, builtin_status_options, builtin_status_usage, 0); handle_untracked_files_arg(&s); if (show_ignored_in_status) s.show_ignored_files = 1; if (*argv) s.pathspec = get_pathspec(prefix, argv); read_cache_preload(s.pathspec); refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL); fd = hold_locked_index(&index_lock, 0); if (0 <= fd) { if (active_cache_changed && !write_cache(fd, active_cache, active_nr)) commit_locked_index(&index_lock); else rollback_lock_file(&index_lock); } s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0; s.in_merge = in_merge; s.ignore_submodule_arg = ignore_submodule_arg; wt_status_collect(&s); if (s.relative_paths) s.prefix = prefix; if (s.use_color == -1) s.use_color = git_use_color_default; if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; switch (status_format) { case STATUS_FORMAT_SHORT: wt_shortstatus_print(&s, null_termination, status_show_branch); break; case STATUS_FORMAT_PORCELAIN: wt_porcelain_print(&s, null_termination); break; case STATUS_FORMAT_LONG: s.verbose = verbose; s.ignore_submodule_arg = ignore_submodule_arg; wt_status_print(&s); break; } return 0; }
int cmd_update_index(int argc, const char **argv, const char *prefix) { int i, newfd, entries, has_errors = 0, line_termination = '\n'; int allow_options = 1; int read_from_stdin = 0; int prefix_length = prefix ? strlen(prefix) : 0; char set_executable_bit = 0; unsigned int refresh_flags = 0; int lock_error = 0; struct lock_file *lock_file; git_config(git_default_config, NULL); /* We can't free this memory, it becomes part of a linked list parsed atexit() */ lock_file = xcalloc(1, sizeof(struct lock_file)); newfd = hold_locked_index(lock_file, 0); if (newfd < 0) lock_error = errno; entries = read_cache(); if (entries < 0) die("cache corrupted"); for (i = 1 ; i < argc; i++) { const char *path = argv[i]; const char *p; if (allow_options && *path == '-') { if (!strcmp(path, "--")) { allow_options = 0; continue; } if (!strcmp(path, "-q")) { refresh_flags |= REFRESH_QUIET; continue; } if (!strcmp(path, "--ignore-submodules")) { refresh_flags |= REFRESH_IGNORE_SUBMODULES; continue; } if (!strcmp(path, "--add")) { allow_add = 1; continue; } if (!strcmp(path, "--replace")) { allow_replace = 1; continue; } if (!strcmp(path, "--remove")) { allow_remove = 1; continue; } if (!strcmp(path, "--unmerged")) { refresh_flags |= REFRESH_UNMERGED; continue; } if (!strcmp(path, "--refresh")) { setup_work_tree(); has_errors |= refresh_cache(refresh_flags); continue; } if (!strcmp(path, "--really-refresh")) { setup_work_tree(); has_errors |= refresh_cache(REFRESH_REALLY | refresh_flags); continue; } if (!strcmp(path, "--cacheinfo")) { unsigned char sha1[20]; unsigned int mode; if (i+3 >= argc) die("git update-index: --cacheinfo <mode> <sha1> <path>"); if (strtoul_ui(argv[i+1], 8, &mode) || get_sha1_hex(argv[i+2], sha1) || add_cacheinfo(mode, sha1, argv[i+3], 0)) die("git update-index: --cacheinfo" " cannot add %s", argv[i+3]); i += 3; continue; } if (!strcmp(path, "--chmod=-x") || !strcmp(path, "--chmod=+x")) { if (argc <= i+1) die("git update-index: %s <path>", path); set_executable_bit = path[8]; continue; } if (!strcmp(path, "--assume-unchanged")) { mark_valid_only = MARK_VALID; continue; } if (!strcmp(path, "--no-assume-unchanged")) { mark_valid_only = UNMARK_VALID; continue; } if (!strcmp(path, "--info-only")) { info_only = 1; continue; } if (!strcmp(path, "--force-remove")) { force_remove = 1; continue; } if (!strcmp(path, "-z")) { line_termination = 0; continue; } if (!strcmp(path, "--stdin")) { if (i != argc - 1) die("--stdin must be at the end"); read_from_stdin = 1; break; } if (!strcmp(path, "--index-info")) { if (i != argc - 1) die("--index-info must be at the end"); allow_add = allow_replace = allow_remove = 1; read_index_info(line_termination); break; } if (!strcmp(path, "--unresolve")) { has_errors = do_unresolve(argc - i, argv + i, prefix, prefix_length); if (has_errors) active_cache_changed = 0; goto finish; } if (!strcmp(path, "--again") || !strcmp(path, "-g")) { setup_work_tree(); has_errors = do_reupdate(argc - i, argv + i, prefix, prefix_length); if (has_errors) active_cache_changed = 0; goto finish; } if (!strcmp(path, "--ignore-missing")) { refresh_flags |= REFRESH_IGNORE_MISSING; continue; } if (!strcmp(path, "--verbose")) { verbose = 1; continue; } if (!strcmp(path, "-h") || !strcmp(path, "--help")) usage(update_index_usage); die("unknown option %s", path); } setup_work_tree(); p = prefix_path(prefix, prefix_length, path); update_one(p, NULL, 0); if (set_executable_bit) chmod_path(set_executable_bit, p); if (p < path || p > path + strlen(path)) free((char*)p); } if (read_from_stdin) { struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT; setup_work_tree(); while (strbuf_getline(&buf, stdin, line_termination) != EOF) { const char *p; if (line_termination && buf.buf[0] == '"') { strbuf_reset(&nbuf); if (unquote_c_style(&nbuf, buf.buf, NULL)) die("line is badly quoted"); strbuf_swap(&buf, &nbuf); } p = prefix_path(prefix, prefix_length, buf.buf); update_one(p, NULL, 0); if (set_executable_bit) chmod_path(set_executable_bit, p); if (p < buf.buf || p > buf.buf + buf.len) free((char *)p); } strbuf_release(&nbuf); strbuf_release(&buf); } finish: if (active_cache_changed) { if (newfd < 0) { if (refresh_flags & REFRESH_QUIET) exit(128); unable_to_lock_index_die(get_index_file(), lock_error); } if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(lock_file)) die("Unable to write new index file"); } rollback_lock_file(lock_file); return has_errors ? 1 : 0; }
static char *prepare_index(int argc, const char **argv, const char *prefix, int is_status) { int fd; struct string_list partial; const char **pathspec = NULL; int refresh_flags = REFRESH_QUIET; if (is_status) refresh_flags |= REFRESH_UNMERGED; if (interactive) { if (interactive_add(argc, argv, prefix) != 0) die("interactive add failed"); if (read_cache_preload(NULL) < 0) die("index file corrupt"); commit_style = COMMIT_AS_IS; return get_index_file(); } if (*argv) pathspec = get_pathspec(prefix, argv); if (read_cache_preload(pathspec) < 0) die("index file corrupt"); /* * Non partial, non as-is commit. * * (1) get the real index; * (2) update the_index as necessary; * (3) write the_index out to the real index (still locked); * (4) return the name of the locked index file. * * The caller should run hooks on the locked real index, and * (A) if all goes well, commit the real index; * (B) on failure, rollback the real index. */ if (all || (also && pathspec && *pathspec)) { fd = hold_locked_index(&index_lock, 1); add_files_to_cache(also ? prefix : NULL, pathspec, 0); refresh_cache_or_die(refresh_flags); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die("unable to write new_index file"); commit_style = COMMIT_NORMAL; return index_lock.filename; } /* * As-is commit. * * (1) return the name of the real index file. * * The caller should run hooks on the real index, * and create commit from the_index. * We still need to refresh the index here. */ if (!pathspec || !*pathspec) { fd = hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); if (active_cache_changed) { if (write_cache(fd, active_cache, active_nr) || commit_locked_index(&index_lock)) die("unable to write new_index file"); } else { rollback_lock_file(&index_lock); } commit_style = COMMIT_AS_IS; return get_index_file(); } /* * A partial commit. * * (0) find the set of affected paths; * (1) get lock on the real index file; * (2) update the_index with the given paths; * (3) write the_index out to the real index (still locked); * (4) get lock on the false index file; * (5) reset the_index from HEAD; * (6) update the_index the same way as (2); * (7) write the_index out to the false index file; * (8) return the name of the false index file (still locked); * * The caller should run hooks on the locked false index, and * create commit from it. Then * (A) if all goes well, commit the real index; * (B) on failure, rollback the real index; * In either case, rollback the false index. */ commit_style = COMMIT_PARTIAL; if (in_merge) die("cannot do a partial commit during a merge."); memset(&partial, 0, sizeof(partial)); partial.strdup_strings = 1; if (list_paths(&partial, initial_commit ? NULL : "HEAD", prefix, pathspec)) exit(1); discard_cache(); if (read_cache() < 0) die("cannot read the index"); fd = hold_locked_index(&index_lock, 1); add_remove_files(&partial); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&index_lock)) die("unable to write new_index file"); fd = hold_lock_file_for_update(&false_lock, git_path("next-index-%"PRIuMAX, (uintmax_t) getpid()), LOCK_DIE_ON_ERROR); create_base_index(); add_remove_files(&partial); refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close_lock_file(&false_lock)) die("unable to write temporary index file"); discard_cache(); read_cache_from(false_lock.filename); return false_lock.filename; }
int cmd_update_index(int argc, const char **argv, const char *prefix) { int newfd, entries, has_errors = 0, line_termination = '\n'; int read_from_stdin = 0; int prefix_length = prefix ? strlen(prefix) : 0; int preferred_index_format = 0; char set_executable_bit = 0; struct refresh_params refresh_args = {0, &has_errors}; int lock_error = 0; int split_index = -1; struct lock_file *lock_file; struct parse_opt_ctx_t ctx; int parseopt_state = PARSE_OPT_UNKNOWN; struct option options[] = { OPT_BIT('q', NULL, &refresh_args.flags, N_("continue refresh even when index needs update"), REFRESH_QUIET), OPT_BIT(0, "ignore-submodules", &refresh_args.flags, N_("refresh: ignore submodules"), REFRESH_IGNORE_SUBMODULES), OPT_SET_INT(0, "add", &allow_add, N_("do not ignore new files"), 1), OPT_SET_INT(0, "replace", &allow_replace, N_("let files replace directories and vice-versa"), 1), OPT_SET_INT(0, "remove", &allow_remove, N_("notice files missing from worktree"), 1), OPT_BIT(0, "unmerged", &refresh_args.flags, N_("refresh even if index contains unmerged entries"), REFRESH_UNMERGED), {OPTION_CALLBACK, 0, "refresh", &refresh_args, NULL, N_("refresh stat information"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, refresh_callback}, {OPTION_CALLBACK, 0, "really-refresh", &refresh_args, NULL, N_("like --refresh, but ignore assume-unchanged setting"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, really_refresh_callback}, {OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL, N_("<mode>,<object>,<path>"), N_("add the specified entry to the index"), PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */ PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, (parse_opt_cb *) cacheinfo_callback}, {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, N_("(+/-)x"), N_("override the executable bit of the listed files"), PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, chmod_callback}, {OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL, N_("mark files as \"not changing\""), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, {OPTION_SET_INT, 0, "no-assume-unchanged", &mark_valid_only, NULL, N_("clear assumed-unchanged bit"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, {OPTION_SET_INT, 0, "skip-worktree", &mark_skip_worktree_only, NULL, N_("mark files as \"index-only\""), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG}, {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL, N_("clear skip-worktree bit"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG}, OPT_SET_INT(0, "info-only", &info_only, N_("add to index only; do not add content to object database"), 1), OPT_SET_INT(0, "force-remove", &force_remove, N_("remove named paths even if present in worktree"), 1), OPT_SET_INT('z', NULL, &line_termination, N_("with --stdin: input lines are terminated by null bytes"), '\0'), {OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL, N_("read list of paths to be updated from standard input"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) stdin_callback}, {OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &line_termination, NULL, N_("add entries from standard input to the index"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) stdin_cacheinfo_callback}, {OPTION_LOWLEVEL_CALLBACK, 0, "unresolve", &has_errors, NULL, N_("repopulate stages #2 and #3 for the listed paths"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) unresolve_callback}, {OPTION_LOWLEVEL_CALLBACK, 'g', "again", &has_errors, NULL, N_("only update entries that differ from HEAD"), PARSE_OPT_NONEG | PARSE_OPT_NOARG, (parse_opt_cb *) reupdate_callback}, OPT_BIT(0, "ignore-missing", &refresh_args.flags, N_("ignore files missing from worktree"), REFRESH_IGNORE_MISSING), OPT_SET_INT(0, "verbose", &verbose, N_("report actions to standard output"), 1), {OPTION_CALLBACK, 0, "clear-resolve-undo", NULL, NULL, N_("(for porcelains) forget saved unresolved conflicts"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, resolve_undo_clear_callback}, OPT_INTEGER(0, "index-version", &preferred_index_format, N_("write index in this format")), OPT_BOOL(0, "split-index", &split_index, N_("enable or disable split index")), OPT_END() }; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(update_index_usage, options); git_config(git_default_config, NULL); /* We can't free this memory, it becomes part of a linked list parsed atexit() */ lock_file = xcalloc(1, sizeof(struct lock_file)); newfd = hold_locked_index(lock_file, 0); if (newfd < 0) lock_error = errno; entries = read_cache(); if (entries < 0) die("cache corrupted"); /* * Custom copy of parse_options() because we want to handle * filename arguments as they come. */ parse_options_start(&ctx, argc, argv, prefix, options, PARSE_OPT_STOP_AT_NON_OPTION); while (ctx.argc) { if (parseopt_state != PARSE_OPT_DONE) parseopt_state = parse_options_step(&ctx, options, update_index_usage); if (!ctx.argc) break; switch (parseopt_state) { case PARSE_OPT_HELP: exit(129); case PARSE_OPT_NON_OPTION: case PARSE_OPT_DONE: { const char *path = ctx.argv[0]; const char *p; setup_work_tree(); p = prefix_path(prefix, prefix_length, path); update_one(p); if (set_executable_bit) chmod_path(set_executable_bit, p); free((char *)p); ctx.argc--; ctx.argv++; break; } case PARSE_OPT_UNKNOWN: if (ctx.argv[0][1] == '-') error("unknown option '%s'", ctx.argv[0] + 2); else error("unknown switch '%c'", *ctx.opt); usage_with_options(update_index_usage, options); } } argc = parse_options_end(&ctx); if (preferred_index_format) { if (preferred_index_format < INDEX_FORMAT_LB || INDEX_FORMAT_UB < preferred_index_format) die("index-version %d not in range: %d..%d", preferred_index_format, INDEX_FORMAT_LB, INDEX_FORMAT_UB); if (the_index.version != preferred_index_format) active_cache_changed |= SOMETHING_CHANGED; the_index.version = preferred_index_format; } if (read_from_stdin) { struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT; setup_work_tree(); while (strbuf_getline(&buf, stdin, line_termination) != EOF) { const char *p; if (line_termination && buf.buf[0] == '"') { strbuf_reset(&nbuf); if (unquote_c_style(&nbuf, buf.buf, NULL)) die("line is badly quoted"); strbuf_swap(&buf, &nbuf); } p = prefix_path(prefix, prefix_length, buf.buf); update_one(p); if (set_executable_bit) chmod_path(set_executable_bit, p); free((char *)p); } strbuf_release(&nbuf); strbuf_release(&buf); } if (split_index > 0) { init_split_index(&the_index); the_index.cache_changed |= SPLIT_INDEX_ORDERED; } else if (!split_index && the_index.split_index) { /* * can't discard_split_index(&the_index); because that * will destroy split_index->base->cache[], which may * be shared with the_index.cache[]. So yeah we're * leaking a bit here. */ the_index.split_index = NULL; the_index.cache_changed |= SOMETHING_CHANGED; } if (active_cache_changed) { if (newfd < 0) { if (refresh_args.flags & REFRESH_QUIET) exit(128); unable_to_lock_die(get_index_file(), lock_error); } if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) die("Unable to write new index file"); } rollback_lock_file(lock_file); return has_errors ? 1 : 0; }
int checkout_fast_forward(const struct object_id *head, const struct object_id *remote, int overwrite_ignore) { struct tree *trees[MAX_UNPACK_TREES]; struct unpack_trees_options opts; struct tree_desc t[MAX_UNPACK_TREES]; int i, nr_trees = 0; struct dir_struct dir; struct lock_file lock_file = LOCK_INIT; refresh_cache(REFRESH_QUIET); if (hold_locked_index(&lock_file, LOCK_REPORT_ON_ERROR) < 0) return -1; memset(&trees, 0, sizeof(trees)); memset(&t, 0, sizeof(t)); trees[nr_trees] = parse_tree_indirect(head); if (!trees[nr_trees++]) { rollback_lock_file(&lock_file); return -1; } trees[nr_trees] = parse_tree_indirect(remote); if (!trees[nr_trees++]) { rollback_lock_file(&lock_file); return -1; } for (i = 0; i < nr_trees; i++) { parse_tree(trees[i]); init_tree_desc(t+i, trees[i]->buffer, trees[i]->size); } memset(&opts, 0, sizeof(opts)); if (overwrite_ignore) { memset(&dir, 0, sizeof(dir)); dir.flags |= DIR_SHOW_IGNORED; setup_standard_excludes(&dir); opts.dir = &dir; } opts.head_idx = 1; opts.src_index = &the_index; opts.dst_index = &the_index; opts.update = 1; opts.verbose_update = 1; opts.merge = 1; opts.fn = twoway_merge; setup_unpack_trees_porcelain(&opts, "merge"); if (unpack_trees(nr_trees, t, &opts)) { rollback_lock_file(&lock_file); clear_unpack_trees_porcelain(&opts); return -1; } clear_unpack_trees_porcelain(&opts); if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) return error(_("unable to write new index file")); return 0; }
/* return NULL on success, else hostname running the gc */ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) { static struct lock_file lock; static char locking_host[128]; char my_host[128]; struct strbuf sb = STRBUF_INIT; struct stat st; uintmax_t pid; FILE *fp; int fd, should_exit; if (pidfile) /* already locked */ return NULL; if (gethostname(my_host, sizeof(my_host))) strcpy(my_host, "unknown"); fd = hold_lock_file_for_update(&lock, git_path("gc.pid"), LOCK_DIE_ON_ERROR); if (!force) { fp = fopen(git_path("gc.pid"), "r"); memset(locking_host, 0, sizeof(locking_host)); should_exit = fp != NULL && !fstat(fileno(fp), &st) && /* * 12 hour limit is very generous as gc should * never take that long. On the other hand we * don't really need a strict limit here, * running gc --auto one day late is not a big * problem. --force can be used in manual gc * after the user verifies that no gc is * running. */ time(NULL) - st.st_mtime <= 12 * 3600 && fscanf(fp, "%"PRIuMAX" %127c", &pid, locking_host) == 2 && /* be gentle to concurrent "gc" on remote hosts */ (strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM); if (fp != NULL) fclose(fp); if (should_exit) { if (fd >= 0) rollback_lock_file(&lock); *ret_pid = pid; return locking_host; } } strbuf_addf(&sb, "%"PRIuMAX" %s", (uintmax_t) getpid(), my_host); write_in_full(fd, sb.buf, sb.len); strbuf_release(&sb); commit_lock_file(&lock); pidfile = git_pathdup("gc.pid"); sigchain_push_common(remove_pidfile_on_signal); atexit(remove_pidfile); return NULL; }
int cmd_checkout_index(int argc, const char **argv, const char *prefix) { int i; int newfd = -1; int all = 0; int read_from_stdin = 0; int prefix_length; int force = 0, quiet = 0, not_new = 0; struct option builtin_checkout_index_options[] = { OPT_BOOL('a', "all", &all, N_("check out all files in the index")), OPT__FORCE(&force, N_("force overwrite of existing files")), OPT__QUIET(&quiet, N_("no warning for existing files and files not in index")), OPT_BOOL('n', "no-create", ¬_new, N_("don't checkout new files")), { OPTION_CALLBACK, 'u', "index", &newfd, NULL, N_("update stat information in the index file"), PARSE_OPT_NOARG, option_parse_u }, { OPTION_CALLBACK, 'z', NULL, NULL, NULL, N_("paths are separated with NUL character"), PARSE_OPT_NOARG, option_parse_z }, OPT_BOOL(0, "stdin", &read_from_stdin, N_("read list of paths from the standard input")), OPT_BOOL(0, "temp", &to_tempfile, N_("write the content to temporary files")), OPT_CALLBACK(0, "prefix", NULL, N_("string"), N_("when creating files, prepend <string>"), option_parse_prefix), OPT_CALLBACK(0, "stage", NULL, NULL, N_("copy out the files from named stage"), option_parse_stage), OPT_END() }; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_checkout_index_usage, builtin_checkout_index_options); git_config(git_default_config, NULL); state.base_dir = ""; prefix_length = prefix ? strlen(prefix) : 0; if (read_cache() < 0) { die("invalid cache"); } argc = parse_options(argc, argv, prefix, builtin_checkout_index_options, builtin_checkout_index_usage, 0); state.force = force; state.quiet = quiet; state.not_new = not_new; if (state.base_dir_len || to_tempfile) { /* when --prefix is specified we do not * want to update cache. */ if (state.refresh_cache) { rollback_lock_file(&lock_file); newfd = -1; } state.refresh_cache = 0; } /* Check out named files first */ for (i = 0; i < argc; i++) { const char *arg = argv[i]; char *p; if (all) die("git checkout-index: don't mix '--all' and explicit filenames"); if (read_from_stdin) die("git checkout-index: don't mix '--stdin' and explicit filenames"); p = prefix_path(prefix, prefix_length, arg); checkout_file(p, prefix); free(p); } if (read_from_stdin) { struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT; strbuf_getline_fn getline_fn; if (all) die("git checkout-index: don't mix '--all' and '--stdin'"); getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; while (getline_fn(&buf, stdin) != EOF) { char *p; if (!nul_term_line && buf.buf[0] == '"') { strbuf_reset(&nbuf); if (unquote_c_style(&nbuf, buf.buf, NULL)) die("line is badly quoted"); strbuf_swap(&buf, &nbuf); } p = prefix_path(prefix, prefix_length, buf.buf); checkout_file(p, prefix); free(p); } strbuf_release(&nbuf); strbuf_release(&buf); } if (all) checkout_all(prefix, prefix_length); if (0 <= newfd && write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die("Unable to write new index file"); return 0; }
/* * If value==NULL, unset in (remove from) config, * if value_regex!=NULL, disregard key/value pairs where value does not match. * if multi_replace==0, nothing, or only one matching key/value is replaced, * else all matching key/values (regardless how many) are removed, * before the new pair is written. * * Returns 0 on success. * * This function does this: * * - it locks the config file by creating ".git/config.lock" * * - it then parses the config using store_aux() as validator to find * the position on the key/value pair to replace. If it is to be unset, * it must be found exactly once. * * - the config file is mmap()ed and the part before the match (if any) is * written to the lock file, then the changed part and the rest. * * - the config file is removed and the lock file rename()d to it. * */ int git_config_set_multivar(const char* key, const char* value, const char* value_regex, int multi_replace) { int i, dot; int fd = -1, in_fd; int ret; char* config_filename; struct lock_file *lock = NULL; const char* last_dot = strrchr(key, '.'); if (config_exclusive_filename) config_filename = xstrdup(config_exclusive_filename); else config_filename = git_pathdup("config"); /* * Since "key" actually contains the section name and the real * key name separated by a dot, we have to know where the dot is. */ if (last_dot == NULL) { error("key does not contain a section: %s", key); ret = 2; goto out_free; } store.baselen = last_dot - key; store.multi_replace = multi_replace; /* * Validate the key and while at it, lower case it for matching. */ store.key = xmalloc(strlen(key) + 1); dot = 0; for (i = 0; key[i]; i++) { unsigned char c = key[i]; if (c == '.') dot = 1; /* Leave the extended basename untouched.. */ if (!dot || i > store.baselen) { if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) { error("invalid key: %s", key); free(store.key); ret = 1; goto out_free; } c = tolower(c); } else if (c == '\n') { error("invalid key (newline): %s", key); free(store.key); ret = 1; goto out_free; } store.key[i] = c; } store.key[i] = 0; /* * The lock serves a purpose in addition to locking: the new * contents of .git/config will be written into it. */ lock = xcalloc(sizeof(struct lock_file), 1); fd = hold_lock_file_for_update(lock, config_filename, 0); if (fd < 0) { error("could not lock config file %s", config_filename); free(store.key); ret = -1; goto out_free; } /* * If .git/config does not exist yet, write a minimal version. */ in_fd = open(config_filename, O_RDONLY); if ( in_fd < 0 ) { free(store.key); if ( ENOENT != errno ) { error("opening %s: %s", config_filename, strerror(errno)); ret = 3; /* same as "invalid config file" */ goto out_free; } /* if nothing to unset, error out */ if (value == NULL) { ret = 5; goto out_free; } store.key = (char*)key; if (!store_write_section(fd, key) || !store_write_pair(fd, key, value)) goto write_err_out; } else { struct stat st; char* contents; size_t contents_sz, copy_begin, copy_end; int i, new_line = 0; if (value_regex == NULL) store.value_regex = NULL; else { if (value_regex[0] == '!') { store.do_not_match = 1; value_regex++; } else store.do_not_match = 0; store.value_regex = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(store.value_regex, value_regex, REG_EXTENDED)) { error("invalid pattern: %s", value_regex); free(store.value_regex); ret = 6; goto out_free; } } store.offset[0] = 0; store.state = START; store.seen = 0; /* * After this, store.offset will contain the *end* offset * of the last match, or remain at 0 if no match was found. * As a side effect, we make sure to transform only a valid * existing config file. */ if (git_config_from_file(store_aux, config_filename, NULL)) { error("invalid config file %s", config_filename); free(store.key); if (store.value_regex != NULL) { regfree(store.value_regex); free(store.value_regex); } ret = 3; goto out_free; } free(store.key); if (store.value_regex != NULL) { regfree(store.value_regex); free(store.value_regex); } /* if nothing to unset, or too many matches, error out */ if ((store.seen == 0 && value == NULL) || (store.seen > 1 && multi_replace == 0)) { ret = 5; goto out_free; } fstat(in_fd, &st); contents_sz = xsize_t(st.st_size); contents = xmmap(NULL, contents_sz, PROT_READ, MAP_PRIVATE, in_fd, 0); close(in_fd); if (store.seen == 0) store.seen = 1; for (i = 0, copy_begin = 0; i < store.seen; i++) { if (store.offset[i] == 0) { store.offset[i] = copy_end = contents_sz; } else if (store.state != KEY_SEEN) { copy_end = store.offset[i]; } else copy_end = find_beginning_of_line( contents, contents_sz, store.offset[i]-2, &new_line); if (copy_end > 0 && contents[copy_end-1] != '\n') new_line = 1; /* write the first part of the config */ if (copy_end > copy_begin) { if (write_in_full(fd, contents + copy_begin, copy_end - copy_begin) < copy_end - copy_begin) goto write_err_out; if (new_line && write_in_full(fd, "\n", 1) != 1) goto write_err_out; } copy_begin = store.offset[i]; } /* write the pair (value == NULL means unset) */ if (value != NULL) { if (store.state == START) { if (!store_write_section(fd, key)) goto write_err_out; } if (!store_write_pair(fd, key, value)) goto write_err_out; } /* write the rest of the config */ if (copy_begin < contents_sz) if (write_in_full(fd, contents + copy_begin, contents_sz - copy_begin) < contents_sz - copy_begin) goto write_err_out; munmap(contents, contents_sz); } if (commit_lock_file(lock) < 0) { error("could not commit config file %s", config_filename); ret = 4; goto out_free; } /* * lock is committed, so don't try to roll it back below. * NOTE: Since lockfile.c keeps a linked list of all created * lock_file structures, it isn't safe to free(lock). It's * better to just leave it hanging around. */ lock = NULL; ret = 0; out_free: if (lock) rollback_lock_file(lock); free(config_filename); return ret; write_err_out: ret = write_error(lock->filename); goto out_free; }
int cmd_checkout_index(int argc, const char **argv, const char *prefix) { int i; int newfd = -1; int all = 0; int read_from_stdin = 0; int prefix_length; int force = 0, quiet = 0, not_new = 0; struct option builtin_checkout_index_options[] = { OPT_BOOLEAN('a', "all", &all, "checks out all files in the index"), OPT_BOOLEAN('f', "force", &force, "forces overwrite of existing files"), OPT__QUIET(&quiet), OPT_BOOLEAN('n', "no-create", ¬_new, "don't checkout new files"), { OPTION_CALLBACK, 'u', "index", &newfd, NULL, "update stat information in the index file", PARSE_OPT_NOARG, option_parse_u }, { OPTION_CALLBACK, 'z', NULL, NULL, NULL, "paths are separated with NUL character", PARSE_OPT_NOARG, option_parse_z }, OPT_BOOLEAN(0, "stdin", &read_from_stdin, "read list of paths from the standard input"), OPT_BOOLEAN(0, "temp", &to_tempfile, "write the content to temporary files"), OPT_CALLBACK(0, "prefix", NULL, "string", "when creating files, prepend <string>", option_parse_prefix), OPT_CALLBACK(0, "stage", NULL, NULL, "copy out the files from named stage", option_parse_stage), OPT_END() }; git_config(git_default_config, NULL); state.base_dir = ""; prefix_length = prefix ? strlen(prefix) : 0; if (read_cache() < 0) { die("invalid cache"); } argc = parse_options(argc, argv, builtin_checkout_index_options, builtin_checkout_index_usage, 0); state.force = force; state.quiet = quiet; state.not_new = not_new; if (state.base_dir_len || to_tempfile) { /* when --prefix is specified we do not * want to update cache. */ if (state.refresh_cache) { rollback_lock_file(&lock_file); newfd = -1; } state.refresh_cache = 0; } /* Check out named files first */ for (i = 0; i < argc; i++) { const char *arg = argv[i]; const char *p; if (all) die("git checkout-index: don't mix '--all' and explicit filenames"); if (read_from_stdin) die("git checkout-index: don't mix '--stdin' and explicit filenames"); p = prefix_path(prefix, prefix_length, arg); checkout_file(p, prefix_length); if (p < arg || p > arg + strlen(arg)) free((char*)p); } if (read_from_stdin) { struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT; if (all) die("git checkout-index: don't mix '--all' and '--stdin'"); while (strbuf_getline(&buf, stdin, line_termination) != EOF) { const char *p; if (line_termination && buf.buf[0] == '"') { strbuf_reset(&nbuf); if (unquote_c_style(&nbuf, buf.buf, NULL)) die("line is badly quoted"); strbuf_swap(&buf, &nbuf); } p = prefix_path(prefix, prefix_length, buf.buf); checkout_file(p, prefix_length); if (p < buf.buf || p > buf.buf + buf.len) free((char *)p); } strbuf_release(&nbuf); strbuf_release(&buf); } if (all) checkout_all(prefix, prefix_length); if (0 <= newfd && (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file))) die("Unable to write new index file"); return 0; }
static void update_shallow(struct fetch_pack_args *args, struct ref **sought, int nr_sought, struct shallow_info *si) { struct sha1_array ref = SHA1_ARRAY_INIT; int *status; int i; if (args->depth > 0 && alternate_shallow_file) { if (*alternate_shallow_file == '\0') { /* --unshallow */ unlink_or_warn(git_path("shallow")); rollback_lock_file(&shallow_lock); } else commit_lock_file(&shallow_lock); return; } if (!si->shallow || !si->shallow->nr) return; if (args->cloning) { /* * remote is shallow, but this is a clone, there are * no objects in repo to worry about. Accept any * shallow points that exist in the pack (iow in repo * after get_pack() and reprepare_packed_git()) */ struct sha1_array extra = SHA1_ARRAY_INIT; unsigned char (*sha1)[20] = si->shallow->sha1; for (i = 0; i < si->shallow->nr; i++) if (has_sha1_file(sha1[i])) sha1_array_append(&extra, sha1[i]); if (extra.nr) { setup_alternate_shallow(&shallow_lock, &alternate_shallow_file, &extra); commit_lock_file(&shallow_lock); } sha1_array_clear(&extra); return; } if (!si->nr_ours && !si->nr_theirs) return; remove_nonexistent_theirs_shallow(si); if (!si->nr_ours && !si->nr_theirs) return; for (i = 0; i < nr_sought; i++) sha1_array_append(&ref, sought[i]->old_sha1); si->ref = &ref; if (args->update_shallow) { /* * remote is also shallow, .git/shallow may be updated * so all refs can be accepted. Make sure we only add * shallow roots that are actually reachable from new * refs. */ struct sha1_array extra = SHA1_ARRAY_INIT; unsigned char (*sha1)[20] = si->shallow->sha1; assign_shallow_commits_to_refs(si, NULL, NULL); if (!si->nr_ours && !si->nr_theirs) { sha1_array_clear(&ref); return; } for (i = 0; i < si->nr_ours; i++) sha1_array_append(&extra, sha1[si->ours[i]]); for (i = 0; i < si->nr_theirs; i++) sha1_array_append(&extra, sha1[si->theirs[i]]); setup_alternate_shallow(&shallow_lock, &alternate_shallow_file, &extra); commit_lock_file(&shallow_lock); sha1_array_clear(&extra); sha1_array_clear(&ref); return; } /* * remote is also shallow, check what ref is safe to update * without updating .git/shallow */ status = xcalloc(nr_sought, sizeof(*status)); assign_shallow_commits_to_refs(si, NULL, status); if (si->nr_ours || si->nr_theirs) { for (i = 0; i < nr_sought; i++) if (status[i]) sought[i]->status = REF_STATUS_REJECT_SHALLOW; } free(status); sha1_array_clear(&ref); }
/* return NULL on success, else hostname running the gc */ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) { struct lock_file lock = LOCK_INIT; char my_host[HOST_NAME_MAX + 1]; struct strbuf sb = STRBUF_INIT; struct stat st; uintmax_t pid; FILE *fp; int fd; char *pidfile_path; if (is_tempfile_active(pidfile)) /* already locked */ return NULL; if (xgethostname(my_host, sizeof(my_host))) xsnprintf(my_host, sizeof(my_host), "unknown"); pidfile_path = git_pathdup("gc.pid"); fd = hold_lock_file_for_update(&lock, pidfile_path, LOCK_DIE_ON_ERROR); if (!force) { static char locking_host[HOST_NAME_MAX + 1]; static char *scan_fmt; int should_exit; if (!scan_fmt) scan_fmt = xstrfmt("%s %%%ds", "%"SCNuMAX, HOST_NAME_MAX); fp = fopen(pidfile_path, "r"); memset(locking_host, 0, sizeof(locking_host)); should_exit = fp != NULL && !fstat(fileno(fp), &st) && /* * 12 hour limit is very generous as gc should * never take that long. On the other hand we * don't really need a strict limit here, * running gc --auto one day late is not a big * problem. --force can be used in manual gc * after the user verifies that no gc is * running. */ time(NULL) - st.st_mtime <= 12 * 3600 && fscanf(fp, scan_fmt, &pid, locking_host) == 2 && /* be gentle to concurrent "gc" on remote hosts */ (strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM); if (fp != NULL) fclose(fp); if (should_exit) { if (fd >= 0) rollback_lock_file(&lock); *ret_pid = pid; free(pidfile_path); return locking_host; } } strbuf_addf(&sb, "%"PRIuMAX" %s", (uintmax_t) getpid(), my_host); write_in_full(fd, sb.buf, sb.len); strbuf_release(&sb); commit_lock_file(&lock); pidfile = register_tempfile(pidfile_path); free(pidfile_path); return NULL; }