diff options
Diffstat (limited to 'builtin')
-rw-r--r-- | builtin/add.c | 2 | ||||
-rw-r--r-- | builtin/am.c | 52 | ||||
-rw-r--r-- | builtin/blame.c | 7 | ||||
-rw-r--r-- | builtin/checkout.c | 107 | ||||
-rw-r--r-- | builtin/clone.c | 108 | ||||
-rw-r--r-- | builtin/commit.c | 47 | ||||
-rw-r--r-- | builtin/config.c | 86 | ||||
-rw-r--r-- | builtin/fetch.c | 19 | ||||
-rw-r--r-- | builtin/fsck.c | 4 | ||||
-rw-r--r-- | builtin/gc.c | 34 | ||||
-rw-r--r-- | builtin/index-pack.c | 4 | ||||
-rw-r--r-- | builtin/log.c | 25 | ||||
-rw-r--r-- | builtin/ls-files.c | 2 | ||||
-rw-r--r-- | builtin/merge.c | 30 | ||||
-rw-r--r-- | builtin/notes.c | 6 | ||||
-rw-r--r-- | builtin/pull.c | 7 | ||||
-rw-r--r-- | builtin/receive-pack.c | 2 | ||||
-rw-r--r-- | builtin/reflog.c | 33 | ||||
-rw-r--r-- | builtin/remote.c | 7 | ||||
-rw-r--r-- | builtin/repack.c | 24 | ||||
-rw-r--r-- | builtin/reset.c | 2 | ||||
-rw-r--r-- | builtin/rev-list.c | 3 | ||||
-rw-r--r-- | builtin/send-pack.c | 3 | ||||
-rw-r--r-- | builtin/tag.c | 5 | ||||
-rw-r--r-- | builtin/update-ref.c | 14 | ||||
-rw-r--r-- | builtin/worktree.c | 123 |
26 files changed, 429 insertions, 327 deletions
diff --git a/builtin/add.c b/builtin/add.c index 4bd98b799e..b2a5c57f0a 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -375,7 +375,6 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (add_new_files) { int baselen; - struct pathspec empty_pathspec; /* Set up the default git porcelain excludes */ memset(&dir, 0, sizeof(dir)); @@ -384,7 +383,6 @@ int cmd_add(int argc, const char **argv, const char *prefix) setup_standard_excludes(&dir); } - memset(&empty_pathspec, 0, sizeof(empty_pathspec)); /* This picks up the paths that are not tracked */ baselen = fill_directory(&dir, &pathspec); if (pathspec.nr) diff --git a/builtin/am.c b/builtin/am.c index 486ff594d7..27165a6730 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -10,6 +10,7 @@ #include "dir.h" #include "run-command.h" #include "quote.h" +#include "tempfile.h" #include "lockfile.h" #include "cache-tree.h" #include "refs.h" @@ -1976,15 +1977,48 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset) } /** + * Merges a tree into the index. The index's stat info will take precedence + * over the merged tree's. Returns 0 on success, -1 on failure. + */ +static int merge_tree(struct tree *tree) +{ + struct lock_file *lock_file; + struct unpack_trees_options opts; + struct tree_desc t[1]; + + if (parse_tree(tree)) + return -1; + + lock_file = xcalloc(1, sizeof(struct lock_file)); + hold_locked_index(lock_file, 1); + + memset(&opts, 0, sizeof(opts)); + opts.head_idx = 1; + opts.src_index = &the_index; + opts.dst_index = &the_index; + opts.merge = 1; + opts.fn = oneway_merge; + init_tree_desc(&t[0], tree->buffer, tree->size); + + if (unpack_trees(1, t, &opts)) { + rollback_lock_file(lock_file); + return -1; + } + + if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) + die(_("unable to write new index file")); + + return 0; +} + +/** * Clean the index without touching entries that are not modified between * `head` and `remote`. */ static int clean_index(const unsigned char *head, const unsigned char *remote) { - struct lock_file *lock_file; struct tree *head_tree, *remote_tree, *index_tree; unsigned char index[GIT_SHA1_RAWSZ]; - struct pathspec pathspec; head_tree = parse_tree_indirect(head); if (!head_tree) @@ -2009,18 +2043,8 @@ static int clean_index(const unsigned char *head, const unsigned char *remote) if (fast_forward_to(index_tree, remote_tree, 0)) return -1; - memset(&pathspec, 0, sizeof(pathspec)); - - lock_file = xcalloc(1, sizeof(struct lock_file)); - hold_locked_index(lock_file, 1); - - if (read_tree(remote_tree, 0, &pathspec)) { - rollback_lock_file(lock_file); + if (merge_tree(remote_tree)) return -1; - } - - if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) - die(_("unable to write new index file")); remove_branch_state(); @@ -2181,7 +2205,7 @@ int cmd_am(int argc, const char **argv, const char *prefix) OPT_BOOL('i', "interactive", &state.interactive, N_("run interactively")), OPT_HIDDEN_BOOL('b', "binary", &binary, - N_("(historical option -- no-op")), + N_("historical option -- no-op")), OPT_BOOL('3', "3way", &state.threeway, N_("allow fall back on 3way merging if needed")), OPT__QUIET(&state.quiet, N_("be quiet")), diff --git a/builtin/blame.c b/builtin/blame.c index 04e4864f8b..4db01c195c 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -2227,20 +2227,19 @@ static struct commit_list **append_parent(struct commit_list **tail, const unsig static void append_merge_parents(struct commit_list **tail) { int merge_head; - const char *merge_head_file = git_path("MERGE_HEAD"); struct strbuf line = STRBUF_INIT; - merge_head = open(merge_head_file, O_RDONLY); + merge_head = open(git_path_merge_head(), O_RDONLY); if (merge_head < 0) { if (errno == ENOENT) return; - die("cannot open '%s' for reading", merge_head_file); + die("cannot open '%s' for reading", git_path_merge_head()); } while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) { unsigned char sha1[20]; if (line.len < 40 || get_sha1_hex(line.buf, sha1)) - die("unknown line in '%s': %s", merge_head_file, line.buf); + die("unknown line in '%s': %s", git_path_merge_head(), line.buf); tail = append_parent(tail, sha1); } close(merge_head); diff --git a/builtin/checkout.c b/builtin/checkout.c index f71844a23a..bc703c0f5e 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -18,6 +18,7 @@ #include "xdiff-interface.h" #include "ll-merge.h" #include "resolve-undo.h" +#include "submodule-config.h" #include "submodule.h" static const char * const checkout_usage[] = { @@ -48,8 +49,6 @@ struct checkout_opts { const char *prefix; struct pathspec pathspec; struct tree *source_tree; - - int new_worktree_mode; }; static int post_checkout_hook(struct commit *old, struct commit *new, @@ -282,7 +281,7 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->source_tree) read_tree_some(opts->source_tree, &opts->pathspec); - ps_matched = xcalloc(1, opts->pathspec.nr); + ps_matched = xcalloc(opts->pathspec.nr, 1); /* * Make sure all pathspecs participated in locating the paths @@ -509,7 +508,7 @@ static int merge_working_tree(const struct checkout_opts *opts, topts.dir->flags |= DIR_SHOW_IGNORED; setup_standard_excludes(topts.dir); } - tree = parse_tree_indirect(old->commit && !opts->new_worktree_mode ? + tree = parse_tree_indirect(old->commit ? old->commit->object.sha1 : EMPTY_TREE_SHA1_BIN); init_tree_desc(&trees[0], tree->buffer, tree->size); @@ -612,22 +611,20 @@ static void update_refs_for_switch(const struct checkout_opts *opts, if (opts->new_branch) { if (opts->new_orphan_branch) { if (opts->new_branch_log && !log_all_ref_updates) { - int temp; - struct strbuf log_file = STRBUF_INIT; int ret; - const char *ref_name; - - ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch); - temp = log_all_ref_updates; - log_all_ref_updates = 1; - ret = log_ref_setup(ref_name, &log_file); - log_all_ref_updates = temp; - strbuf_release(&log_file); + char *refname; + struct strbuf err = STRBUF_INIT; + + refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch); + ret = safe_create_reflog(refname, 1, &err); + free(refname); if (ret) { - fprintf(stderr, _("Can not do reflog for '%s'\n"), - opts->new_orphan_branch); + fprintf(stderr, _("Can not do reflog for '%s': %s\n"), + opts->new_orphan_branch, err.buf); + strbuf_release(&err); return; } + strbuf_release(&err); } } else @@ -832,8 +829,7 @@ static int switch_branches(const struct checkout_opts *opts, return ret; } - if (!opts->quiet && !old.path && old.commit && - new->commit != old.commit && !opts->new_worktree_mode) + if (!opts->quiet && !old.path && old.commit && new->commit != old.commit) orphaned_commit_warning(old.commit, new->commit); update_refs_for_switch(opts, &old, new); @@ -898,71 +894,6 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1) return NULL; } -static void check_linked_checkout(struct branch_info *new, const char *id) -{ - struct strbuf sb = STRBUF_INIT; - struct strbuf path = STRBUF_INIT; - struct strbuf gitdir = STRBUF_INIT; - const char *start, *end; - - if (id) - strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id); - else - strbuf_addf(&path, "%s/HEAD", get_git_common_dir()); - - if (strbuf_read_file(&sb, path.buf, 0) < 0 || - !skip_prefix(sb.buf, "ref:", &start)) - goto done; - while (isspace(*start)) - start++; - end = start; - while (*end && !isspace(*end)) - end++; - if (strncmp(start, new->path, end - start) || new->path[end - start] != '\0') - goto done; - if (id) { - strbuf_reset(&path); - strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id); - if (strbuf_read_file(&gitdir, path.buf, 0) <= 0) - goto done; - strbuf_rtrim(&gitdir); - } else - strbuf_addstr(&gitdir, get_git_common_dir()); - die(_("'%s' is already checked out at '%s'"), new->name, gitdir.buf); -done: - strbuf_release(&path); - strbuf_release(&sb); - strbuf_release(&gitdir); -} - -static void check_linked_checkouts(struct branch_info *new) -{ - struct strbuf path = STRBUF_INIT; - DIR *dir; - struct dirent *d; - - strbuf_addf(&path, "%s/worktrees", get_git_common_dir()); - if ((dir = opendir(path.buf)) == NULL) { - strbuf_release(&path); - return; - } - - /* - * $GIT_COMMON_DIR/HEAD is practically outside - * $GIT_DIR so resolve_ref_unsafe() won't work (it - * uses git_path). Parse the ref ourselves. - */ - check_linked_checkout(new, NULL); - - while ((d = readdir(dir)) != NULL) { - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) - continue; - check_linked_checkout(new, d->d_name); - } - strbuf_release(&path); - closedir(dir); -} - static int parse_branchname_arg(int argc, const char **argv, int dwim_new_local_branch_ok, struct branch_info *new, @@ -1170,14 +1101,14 @@ static int checkout_branch(struct checkout_opts *opts, die(_("Cannot switch branch to a non-commit '%s'"), new->name); - if (new->path && !opts->force_detach && !opts->new_branch) { + if (new->path && !opts->force_detach && !opts->new_branch && + !opts->ignore_other_worktrees) { unsigned char sha1[20]; int flag; char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag); if (head_ref && - (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) && - !opts->ignore_other_worktrees) - check_linked_checkouts(new); + (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path))) + die_if_checked_out(new->path); free(head_ref); } @@ -1241,8 +1172,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, checkout_usage, PARSE_OPT_KEEP_DASHDASH); - opts.new_worktree_mode = getenv("GIT_CHECKOUT_NEW_WORKTREE") != NULL; - if (conflict_style) { opts.merge = 1; /* implied */ git_xmerge_config("merge.conflictstyle", conflict_style, NULL); diff --git a/builtin/clone.c b/builtin/clone.c index 303a3a7eb6..578da85254 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -99,82 +99,140 @@ static const char *argv_submodule[] = { "submodule", "update", "--init", "--recursive", NULL }; -static char *get_repo_path(const char *repo, int *is_bundle) +static const char *get_repo_path_1(struct strbuf *path, int *is_bundle) { static char *suffix[] = { "/.git", "", ".git/.git", ".git" }; static char *bundle_suffix[] = { ".bundle", "" }; + size_t baselen = path->len; struct stat st; int i; for (i = 0; i < ARRAY_SIZE(suffix); i++) { - const char *path; - path = mkpath("%s%s", repo, suffix[i]); - if (stat(path, &st)) + strbuf_setlen(path, baselen); + strbuf_addstr(path, suffix[i]); + if (stat(path->buf, &st)) continue; - if (S_ISDIR(st.st_mode) && is_git_directory(path)) { + if (S_ISDIR(st.st_mode) && is_git_directory(path->buf)) { *is_bundle = 0; - return xstrdup(absolute_path(path)); + return path->buf; } else if (S_ISREG(st.st_mode) && st.st_size > 8) { /* Is it a "gitfile"? */ char signature[8]; - int len, fd = open(path, O_RDONLY); + const char *dst; + int len, fd = open(path->buf, O_RDONLY); if (fd < 0) continue; len = read_in_full(fd, signature, 8); close(fd); if (len != 8 || strncmp(signature, "gitdir: ", 8)) continue; - path = read_gitfile(path); - if (path) { + dst = read_gitfile(path->buf); + if (dst) { *is_bundle = 0; - return xstrdup(absolute_path(path)); + return dst; } } } for (i = 0; i < ARRAY_SIZE(bundle_suffix); i++) { - const char *path; - path = mkpath("%s%s", repo, bundle_suffix[i]); - if (!stat(path, &st) && S_ISREG(st.st_mode)) { + strbuf_setlen(path, baselen); + strbuf_addstr(path, bundle_suffix[i]); + if (!stat(path->buf, &st) && S_ISREG(st.st_mode)) { *is_bundle = 1; - return xstrdup(absolute_path(path)); + return path->buf; } } return NULL; } +static char *get_repo_path(const char *repo, int *is_bundle) +{ + struct strbuf path = STRBUF_INIT; + const char *raw; + char *canon; + + strbuf_addstr(&path, repo); + raw = get_repo_path_1(&path, is_bundle); + canon = raw ? xstrdup(absolute_path(raw)) : NULL; + strbuf_release(&path); + return canon; +} + static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) { - const char *end = repo + strlen(repo), *start; + const char *end = repo + strlen(repo), *start, *ptr; size_t len; char *dir; /* + * Skip scheme. + */ + start = strstr(repo, "://"); + if (start == NULL) + start = repo; + else + start += 3; + + /* + * Skip authentication data. The stripping does happen + * greedily, such that we strip up to the last '@' inside + * the host part. + */ + for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) { + if (*ptr == '@') + start = ptr + 1; + } + + /* * Strip trailing spaces, slashes and /.git */ - while (repo < end && (is_dir_sep(end[-1]) || isspace(end[-1]))) + while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1]))) end--; - if (end - repo > 5 && is_dir_sep(end[-5]) && + if (end - start > 5 && is_dir_sep(end[-5]) && !strncmp(end - 4, ".git", 4)) { end -= 5; - while (repo < end && is_dir_sep(end[-1])) + while (start < end && is_dir_sep(end[-1])) end--; } /* - * Find last component, but be prepared that repo could have - * the form "remote.example.com:foo.git", i.e. no slash - * in the directory part. + * Strip trailing port number if we've got only a + * hostname (that is, there is no dir separator but a + * colon). This check is required such that we do not + * strip URI's like '/foo/bar:2222.git', which should + * result in a dir '2222' being guessed due to backwards + * compatibility. */ - start = end; - while (repo < start && !is_dir_sep(start[-1]) && start[-1] != ':') - start--; + if (memchr(start, '/', end - start) == NULL + && memchr(start, ':', end - start) != NULL) { + ptr = end; + while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':') + ptr--; + if (start < ptr && ptr[-1] == ':') + end = ptr - 1; + } + + /* + * Find last component. To remain backwards compatible we + * also regard colons as path separators, such that + * cloning a repository 'foo:bar.git' would result in a + * directory 'bar' being guessed. + */ + ptr = end; + while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':') + ptr--; + start = ptr; /* * Strip .{bundle,git}. */ - strip_suffix(start, is_bundle ? ".bundle" : ".git" , &len); + len = end - start; + strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git"); + + if (!len || (len == 1 && *start == '/')) + die("No directory name could be guessed.\n" + "Please specify a directory on the command line"); if (is_bare) dir = xstrfmt("%.*s.git", (int)len, start); diff --git a/builtin/commit.c b/builtin/commit.c index 7314f33885..b37cb6c8b7 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -166,9 +166,9 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset) static void determine_whence(struct wt_status *s) { - if (file_exists(git_path("MERGE_HEAD"))) + if (file_exists(git_path_merge_head())) whence = FROM_MERGE; - else if (file_exists(git_path("CHERRY_PICK_HEAD"))) { + else if (file_exists(git_path_cherry_pick_head())) { whence = FROM_CHERRY_PICK; if (file_exists(git_path(SEQ_DIR))) sequencer_in_use = 1; @@ -324,6 +324,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix struct string_list partial; struct pathspec pathspec; int refresh_flags = REFRESH_QUIET; + const char *ret; if (is_status) refresh_flags |= REFRESH_UNMERGED; @@ -344,7 +345,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix die(_("unable to create temporary index")); old_index_env = getenv(INDEX_ENVIRONMENT); - setenv(INDEX_ENVIRONMENT, index_lock.filename.buf, 1); + setenv(INDEX_ENVIRONMENT, get_lock_file_path(&index_lock), 1); if (interactive_add(argc, argv, prefix, patch_interactive) != 0) die(_("interactive add failed")); @@ -355,7 +356,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix unsetenv(INDEX_ENVIRONMENT); discard_cache(); - read_cache_from(index_lock.filename.buf); + read_cache_from(get_lock_file_path(&index_lock)); if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) { if (reopen_lock_file(&index_lock) < 0) die(_("unable to write index file")); @@ -365,7 +366,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix warning(_("Failed to update main cache tree")); commit_style = COMMIT_NORMAL; - return index_lock.filename.buf; + return get_lock_file_path(&index_lock); } /* @@ -388,7 +389,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK)) die(_("unable to write new_index file")); commit_style = COMMIT_NORMAL; - return index_lock.filename.buf; + return get_lock_file_path(&index_lock); } /* @@ -475,9 +476,9 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix die(_("unable to write temporary index file")); discard_cache(); - read_cache_from(false_lock.filename.buf); - - return false_lock.filename.buf; + ret = get_lock_file_path(&false_lock); + read_cache_from(ret); + return ret; } static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn, @@ -725,12 +726,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix, format_commit_message(commit, "fixup! %s\n\n", &sb, &ctx); hook_arg1 = "message"; - } else if (!stat(git_path("MERGE_MSG"), &statbuf)) { - if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0) + } else if (!stat(git_path_merge_msg(), &statbuf)) { + if (strbuf_read_file(&sb, git_path_merge_msg(), 0) < 0) die_errno(_("could not read MERGE_MSG")); hook_arg1 = "merge"; - } else if (!stat(git_path("SQUASH_MSG"), &statbuf)) { - if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0) + } else if (!stat(git_path_squash_msg(), &statbuf)) { + if (strbuf_read_file(&sb, git_path_squash_msg(), 0) < 0) die_errno(_("could not read SQUASH_MSG")); hook_arg1 = "squash"; } else if (template_file) { @@ -1684,10 +1685,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (!reflog_msg) reflog_msg = "commit (merge)"; pptr = &commit_list_insert(current_head, pptr)->next; - fp = fopen(git_path("MERGE_HEAD"), "r"); + fp = fopen(git_path_merge_head(), "r"); if (fp == NULL) die_errno(_("could not open '%s' for reading"), - git_path("MERGE_HEAD")); + git_path_merge_head()); while (strbuf_getline(&m, fp, '\n') != EOF) { struct commit *parent; @@ -1698,8 +1699,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) } fclose(fp); strbuf_release(&m); - if (!stat(git_path("MERGE_MODE"), &statbuf)) { - if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0) + if (!stat(git_path_merge_mode(), &statbuf)) { + if (strbuf_read_file(&sb, git_path_merge_mode(), 0) < 0) die_errno(_("could not read MERGE_MODE")); if (!strcmp(sb.buf, "no-ff")) allow_fast_forward = 0; @@ -1775,12 +1776,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix) } ref_transaction_free(transaction); - unlink(git_path("CHERRY_PICK_HEAD")); - unlink(git_path("REVERT_HEAD")); - unlink(git_path("MERGE_HEAD")); - unlink(git_path("MERGE_MSG")); - unlink(git_path("MERGE_MODE")); - unlink(git_path("SQUASH_MSG")); + unlink(git_path_cherry_pick_head()); + unlink(git_path_revert_head()); + unlink(git_path_merge_head()); + unlink(git_path_merge_msg()); + unlink(git_path_merge_mode()); + unlink(git_path_squash_msg()); if (commit_index_files()) die (_("Repository has been updated, but unable to write\n" diff --git a/builtin/config.c b/builtin/config.c index 7188405f7e..71acc44143 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -13,6 +13,7 @@ static char *key; static regex_t *key_regexp; static regex_t *regexp; static int show_keys; +static int omit_values; static int use_key_regexp; static int do_all; static int do_not_match; @@ -78,6 +79,7 @@ static struct option builtin_config_options[] = { OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH), OPT_GROUP(N_("Other")), OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")), + OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")), OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")), OPT_END(), }; @@ -91,7 +93,7 @@ static void check_argc(int argc, int min, int max) { static int show_all_config(const char *key_, const char *value_, void *cb) { - if (value_) + if (!omit_values && value_) printf("%s%c%s%c", key_, delim, value_, term); else printf("%s%c", key_, term); @@ -106,48 +108,40 @@ struct strbuf_list { static int format_config(struct strbuf *buf, const char *key_, const char *value_) { - int must_free_vptr = 0; - int must_print_delim = 0; - char value[256]; - const char *vptr = value; - - strbuf_init(buf, 0); - - if (show_keys) { + if (show_keys) strbuf_addstr(buf, key_); - must_print_delim = 1; - } - if (types == TYPE_INT) - sprintf(value, "%"PRId64, - git_config_int64(key_, value_ ? value_ : "")); - else if (types == TYPE_BOOL) - vptr = git_config_bool(key_, value_) ? "true" : "false"; - else if (types == TYPE_BOOL_OR_INT) { - int is_bool, v; - v = git_config_bool_or_int(key_, value_, &is_bool); - if (is_bool) - vptr = v ? "true" : "false"; - else - sprintf(value, "%d", v); - } else if (types == TYPE_PATH) { - if (git_config_pathname(&vptr, key_, value_) < 0) - return -1; - must_free_vptr = 1; - } else if (value_) { - vptr = value_; - } else { - /* Just show the key name */ - vptr = ""; - must_print_delim = 0; - } + if (!omit_values) { + if (show_keys) + strbuf_addch(buf, key_delim); - if (must_print_delim) - strbuf_addch(buf, key_delim); - strbuf_addstr(buf, vptr); + if (types == TYPE_INT) + strbuf_addf(buf, "%"PRId64, + git_config_int64(key_, value_ ? value_ : "")); + else if (types == TYPE_BOOL) + strbuf_addstr(buf, git_config_bool(key_, value_) ? + "true" : "false"); + else if (types == TYPE_BOOL_OR_INT) { + int is_bool, v; + v = git_config_bool_or_int(key_, value_, &is_bool); + if (is_bool) + strbuf_addstr(buf, v ? "true" : "false"); + else + strbuf_addf(buf, "%d", v); + } else if (types == TYPE_PATH) { + const char *v; + if (git_config_pathname(&v, key_, value_) < 0) + return -1; + strbuf_addstr(buf, v); + free((char *)v); + } else if (value_) { + strbuf_addstr(buf, value_); + } else { + /* Just show the key name; back out delimiter */ + if (show_keys) + strbuf_setlen(buf, buf->len - 1); + } + } strbuf_addch(buf, term); - - if (must_free_vptr) - free((char *)vptr); return 0; } @@ -164,6 +158,7 @@ static int collect_config(const char *key_, const char *value_, void *cb) return 0; ALLOC_GROW(values->items, values->nr + 1, values->alloc); + strbuf_init(&values->items[values->nr], 0); return format_config(&values->items[values->nr++], key_, value_); } @@ -430,14 +425,11 @@ static int get_urlmatch(const char *var, const char *url) for_each_string_list_item(item, &values) { struct urlmatch_current_candidate_value *matched = item->util; - struct strbuf key = STRBUF_INIT; struct strbuf buf = STRBUF_INIT; - strbuf_addstr(&key, item->string); - format_config(&buf, key.buf, + format_config(&buf, item->string, matched->value_is_null ? NULL : matched->value.buf); fwrite(buf.buf, 1, buf.len, stdout); - strbuf_release(&key); strbuf_release(&buf); strbuf_release(&matched->value); @@ -549,7 +541,11 @@ int cmd_config(int argc, const char **argv, const char *prefix) default: usage_with_options(builtin_config_usage, builtin_config_options); } - + if (omit_values && + !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) { + error("--name-only is only applicable to --list or --get-regexp"); + usage_with_options(builtin_config_usage, builtin_config_options); + } if (actions == ACTION_LIST) { check_argc(argc, 0, 0); if (git_config_with_options(show_all_config, NULL, diff --git a/builtin/fetch.c b/builtin/fetch.c index 34b6f5ebca..9a3869f4ff 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -11,6 +11,7 @@ #include "run-command.h" #include "parse-options.h" #include "sigchain.h" +#include "submodule-config.h" #include "submodule.h" #include "connected.h" #include "argv-array.h" @@ -591,7 +592,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, const char *what, *kind; struct ref *rm; char *url; - const char *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD"); + const char *filename = dry_run ? "/dev/null" : git_path_fetch_head(); int want_status; fp = fopen(filename, "a"); @@ -834,7 +835,7 @@ static void check_not_current_branch(struct ref *ref_map) static int truncate_fetch_head(void) { - const char *filename = git_path("FETCH_HEAD"); + const char *filename = git_path_fetch_head(); FILE *fp = fopen(filename, "w"); if (!fp) @@ -988,17 +989,15 @@ static int get_remote_group(const char *key, const char *value, void *priv) { struct remote_group_data *g = priv; - if (starts_with(key, "remotes.") && - !strcmp(key + 8, g->name)) { + if (skip_prefix(key, "remotes.", &key) && !strcmp(key, g->name)) { /* split list by white space */ - int space = strcspn(value, " \t\n"); while (*value) { - if (space > 1) { + size_t wordlen = strcspn(value, " \t\n"); + + if (wordlen >= 1) string_list_append(g->list, - xstrndup(value, space)); - } - value += space + (value[space] != '\0'); - space = strcspn(value, " \t\n"); + xstrndup(value, wordlen)); + value += wordlen + (value[wordlen] != '\0'); } } diff --git a/builtin/fsck.c b/builtin/fsck.c index f4b87e9b33..079470342f 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -243,13 +243,14 @@ static void check_unreachable_object(struct object *obj) printf("dangling %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1)); if (write_lost_and_found) { - const char *filename = git_path("lost-found/%s/%s", + char *filename = git_pathdup("lost-found/%s/%s", obj->type == OBJ_COMMIT ? "commit" : "other", sha1_to_hex(obj->sha1)); FILE *f; if (safe_create_leading_directories_const(filename)) { error("Could not create lost-found"); + free(filename); return; } if (!(f = fopen(filename, "w"))) @@ -262,6 +263,7 @@ static void check_unreachable_object(struct object *obj) if (fclose(f)) die_errno("Could not finish '%s'", filename); + free(filename); } return; } diff --git a/builtin/gc.c b/builtin/gc.c index 4957c39032..0ad8d30b56 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -11,6 +11,7 @@ */ #include "builtin.h" +#include "tempfile.h" #include "lockfile.h" #include "parse-options.h" #include "run-command.h" @@ -42,20 +43,7 @@ static struct argv_array prune = ARGV_ARRAY_INIT; static struct argv_array prune_worktrees = ARGV_ARRAY_INIT; static struct argv_array rerere = ARGV_ARRAY_INIT; -static char *pidfile; - -static void remove_pidfile(void) -{ - if (pidfile) - unlink(pidfile); -} - -static void remove_pidfile_on_signal(int signo) -{ - remove_pidfile(); - sigchain_pop(signo); - raise(signo); -} +static struct tempfile pidfile; static void git_config_date_string(const char *key, const char **output) { @@ -85,7 +73,7 @@ static void gc_config(void) git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit); git_config_get_bool("gc.autodetach", &detach_auto); git_config_date_string("gc.pruneexpire", &prune_expire); - git_config_date_string("gc.pruneworktreesexpire", &prune_worktrees_expire); + git_config_date_string("gc.worktreepruneexpire", &prune_worktrees_expire); git_config(git_default_config, NULL); } @@ -199,20 +187,22 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) uintmax_t pid; FILE *fp; int fd; + char *pidfile_path; - if (pidfile) + if (is_tempfile_active(&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"), + 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[128]; int should_exit; - fp = fopen(git_path("gc.pid"), "r"); + fp = fopen(pidfile_path, "r"); memset(locking_host, 0, sizeof(locking_host)); should_exit = fp != NULL && @@ -236,6 +226,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) if (fd >= 0) rollback_lock_file(&lock); *ret_pid = pid; + free(pidfile_path); return locking_host; } } @@ -245,11 +236,8 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid) 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); - + register_tempfile(&pidfile, pidfile_path); + free(pidfile_path); return NULL; } diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 3f10840441..3431de2362 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1421,7 +1421,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, get_object_directory(), sha1_to_hex(sha1)); final_pack_name = name; } - if (move_temp_to_file(curr_pack_name, final_pack_name)) + if (finalize_object_file(curr_pack_name, final_pack_name)) die(_("cannot store pack file")); } else if (from_stdin) chmod(final_pack_name, 0444); @@ -1432,7 +1432,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name, get_object_directory(), sha1_to_hex(sha1)); final_index_name = name; } - if (move_temp_to_file(curr_index_name, final_index_name)) + if (finalize_object_file(curr_index_name, final_index_name)) die(_("cannot store index file")); } else chmod(final_index_name, 0444); diff --git a/builtin/log.c b/builtin/log.c index b50ef7510c..a491d3dea0 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -342,8 +342,7 @@ static int cmd_log_walk(struct rev_info *rev) * retain that state information if replacing rev->diffopt in this loop */ while ((commit = get_revision(rev)) != NULL) { - if (!log_tree_commit(rev, commit) && - rev->max_count >= 0) + if (!log_tree_commit(rev, commit) && rev->max_count >= 0) /* * We decremented max_count in get_revision, * but we didn't actually show the commit. @@ -504,7 +503,8 @@ static int show_tree_object(const unsigned char *sha1, return 0; } -static void show_rev_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt) +static void show_setup_revisions_tweak(struct rev_info *rev, + struct setup_revision_opt *opt) { if (rev->ignore_merges) { /* There was no "-m" on the command line */ @@ -539,7 +539,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) memset(&opt, 0, sizeof(opt)); opt.def = "HEAD"; - opt.tweak = show_rev_tweak_rev; + opt.tweak = show_setup_revisions_tweak; cmd_log_init(argc, argv, prefix, &rev, &opt); if (!rev.no_walk) @@ -626,12 +626,20 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix) return cmd_log_walk(&rev); } -static void default_follow_tweak(struct rev_info *rev, - struct setup_revision_opt *opt) +static void log_setup_revisions_tweak(struct rev_info *rev, + struct setup_revision_opt *opt) { if (DIFF_OPT_TST(&rev->diffopt, DEFAULT_FOLLOW_RENAMES) && rev->prune_data.nr == 1) DIFF_OPT_SET(&rev->diffopt, FOLLOW_RENAMES); + + /* Turn --cc/-c into -p --cc/-c when -p was not given */ + if (!rev->diffopt.output_format && rev->combine_merges) + rev->diffopt.output_format = DIFF_FORMAT_PATCH; + + /* Turn -m on when --cc/-c was given */ + if (rev->combine_merges) + rev->ignore_merges = 0; } int cmd_log(int argc, const char **argv, const char *prefix) @@ -647,7 +655,7 @@ int cmd_log(int argc, const char **argv, const char *prefix) memset(&opt, 0, sizeof(opt)); opt.def = "HEAD"; opt.revarg_opt = REVARG_COMMITTISH; - opt.tweak = default_follow_tweak; + opt.tweak = log_setup_revisions_tweak; cmd_log_init(argc, argv, prefix, &rev, &opt); return cmd_log_walk(&rev); } @@ -1455,8 +1463,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) continue; } - if (ignore_if_in_upstream && - has_commit_patch_id(commit, &ids)) + if (ignore_if_in_upstream && has_commit_patch_id(commit, &ids)) continue; nr++; diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 6fa2205734..b6a7cb0c7c 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -516,7 +516,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) /* Treat unmatching pathspec elements as errors */ if (pathspec.nr && error_unmatch) - ps_matched = xcalloc(1, pathspec.nr); + ps_matched = xcalloc(pathspec.nr, 1); if ((dir.flags & DIR_SHOW_IGNORED) && !exc_given) die("ls-files --ignored needs some exclude pattern"); diff --git a/builtin/merge.c b/builtin/merge.c index 85c54dcd5a..a0edacab20 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -231,9 +231,9 @@ static struct option builtin_merge_options[] = { /* Cleans up metadata that is uninteresting after a succeeded merge. */ static void drop_save(void) { - unlink(git_path("MERGE_HEAD")); - unlink(git_path("MERGE_MSG")); - unlink(git_path("MERGE_MODE")); + unlink(git_path_merge_head()); + unlink(git_path_merge_msg()); + unlink(git_path_merge_mode()); } static int save_state(unsigned char *stash) @@ -338,7 +338,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead struct pretty_print_context ctx = {0}; printf(_("Squash commit -- not updating HEAD\n")); - filename = git_path("SQUASH_MSG"); + filename = git_path_squash_msg(); fd = open(filename, O_WRONLY | O_CREAT, 0666); if (fd < 0) die_errno(_("Could not write to '%s'"), filename); @@ -754,7 +754,7 @@ static void add_strategies(const char *string, unsigned attr) static void write_merge_msg(struct strbuf *msg) { - const char *filename = git_path("MERGE_MSG"); + const char *filename = git_path_merge_msg(); int fd = open(filename, O_WRONLY | O_CREAT, 0666); if (fd < 0) die_errno(_("Could not open '%s' for writing"), @@ -766,7 +766,7 @@ static void write_merge_msg(struct strbuf *msg) static void read_merge_msg(struct strbuf *msg) { - const char *filename = git_path("MERGE_MSG"); + const char *filename = git_path_merge_msg(); strbuf_reset(msg); if (strbuf_read_file(msg, filename, 0) < 0) die_errno(_("Could not read from '%s'"), filename); @@ -799,10 +799,10 @@ static void prepare_to_commit(struct commit_list *remoteheads) strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char); write_merge_msg(&msg); if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg", - git_path("MERGE_MSG"), "merge", NULL)) + git_path_merge_msg(), "merge", NULL)) abort_commit(remoteheads, NULL); if (0 < option_edit) { - if (launch_editor(git_path("MERGE_MSG"), NULL, NULL)) + if (launch_editor(git_path_merge_msg(), NULL, NULL)) abort_commit(remoteheads, NULL); } read_merge_msg(&msg); @@ -865,7 +865,7 @@ static int suggest_conflicts(void) FILE *fp; struct strbuf msgbuf = STRBUF_INIT; - filename = git_path("MERGE_MSG"); + filename = git_path_merge_msg(); fp = fopen(filename, "a"); if (!fp) die_errno(_("Could not open '%s' for writing"), filename); @@ -967,7 +967,7 @@ static void write_merge_state(struct commit_list *remoteheads) } strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1)); } - filename = git_path("MERGE_HEAD"); + filename = git_path_merge_head(); fd = open(filename, O_WRONLY | O_CREAT, 0666); if (fd < 0) die_errno(_("Could not open '%s' for writing"), filename); @@ -977,7 +977,7 @@ static void write_merge_state(struct commit_list *remoteheads) strbuf_addch(&merge_msg, '\n'); write_merge_msg(&merge_msg); - filename = git_path("MERGE_MODE"); + filename = git_path_merge_mode(); fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) die_errno(_("Could not open '%s' for writing"), filename); @@ -1070,7 +1070,7 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge if (!merge_names) merge_names = &fetch_head_file; - filename = git_path("FETCH_HEAD"); + filename = git_path_fetch_head(); fd = open(filename, O_RDONLY); if (fd < 0) die_errno(_("could not open '%s' for reading"), filename); @@ -1204,7 +1204,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) int nargc = 2; const char *nargv[] = {"reset", "--merge", NULL}; - if (!file_exists(git_path("MERGE_HEAD"))) + if (!file_exists(git_path_merge_head())) die(_("There is no merge to abort (MERGE_HEAD missing).")); /* Invoke 'git reset --merge' */ @@ -1215,7 +1215,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (read_cache_unmerged()) die_resolve_conflict("merge"); - if (file_exists(git_path("MERGE_HEAD"))) { + if (file_exists(git_path_merge_head())) { /* * There is no unmerged entry, don't advise 'git * add/rm <file>', just 'git commit'. @@ -1226,7 +1226,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) else die(_("You have not concluded your merge (MERGE_HEAD exists).")); } - if (file_exists(git_path("CHERRY_PICK_HEAD"))) { + if (file_exists(git_path_cherry_pick_head())) { if (advice_resolve_conflict) die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n" "Please, commit your changes before you merge.")); diff --git a/builtin/notes.c b/builtin/notes.c index 63f95fc554..0423480827 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -19,6 +19,7 @@ #include "string-list.h" #include "notes-merge.h" #include "notes-utils.h" +#include "branch.h" static const char * const git_notes_usage[] = { N_("git notes [--ref <notes-ref>] [list [<object>]]"), @@ -825,10 +826,15 @@ static int merge(int argc, const char **argv, const char *prefix) update_ref(msg.buf, default_notes_ref(), result_sha1, NULL, 0, UPDATE_REFS_DIE_ON_ERR); else { /* Merge has unresolved conflicts */ + char *existing; /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */ update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL, 0, UPDATE_REFS_DIE_ON_ERR); /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */ + existing = find_shared_symref("NOTES_MERGE_REF", default_notes_ref()); + if (existing) + die(_("A notes merge into %s is already in-progress at %s"), + default_notes_ref(), existing); if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL)) die("Failed to store link to current notes ref (%s)", default_notes_ref()); diff --git a/builtin/pull.c b/builtin/pull.c index 722a83c51b..7e3c11ea63 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -15,6 +15,7 @@ #include "dir.h" #include "refs.h" #include "revision.h" +#include "tempfile.h" #include "lockfile.h" enum rebase_type { @@ -823,10 +824,14 @@ int cmd_pull(int argc, const char **argv, const char *prefix) hashclr(orig_head); if (opt_rebase) { + int autostash = 0; + if (is_null_sha1(orig_head) && !is_cache_unborn()) die(_("Updating an unborn branch with changes added to the index.")); - die_on_unclean_work_tree(prefix); + git_config_get_bool("rebase.autostash", &autostash); + if (!autostash) + die_on_unclean_work_tree(prefix); if (get_rebase_fork_point(rebase_fork_point, repo, *refspecs)) hashclr(rebase_fork_point); diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 4ced2eba3c..e6b93d0264 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -933,7 +933,7 @@ static const char *update(struct command *cmd, struct shallow_info *si) return "deletion prohibited"; } - if (!strcmp(namespaced_name, head_name)) { + if (head_name && !strcmp(namespaced_name, head_name)) { switch (deny_delete_current) { case DENY_IGNORE: break; diff --git a/builtin/reflog.c b/builtin/reflog.c index c2eb8ff840..7ed0e8501c 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -13,6 +13,8 @@ static const char reflog_expire_usage[] = "git reflog expire [--expire=<time>] [--expire-unreachable=<time>] [--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] [--verbose] [--all] <refs>..."; static const char reflog_delete_usage[] = "git reflog delete [--rewrite] [--updateref] [--dry-run | -n] [--verbose] <refs>..."; +static const char reflog_exists_usage[] = +"git reflog exists <ref>"; static unsigned long default_reflog_expire; static unsigned long default_reflog_expire_unreachable; @@ -699,12 +701,38 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) return status; } +static int cmd_reflog_exists(int argc, const char **argv, const char *prefix) +{ + int i, start = 0; + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (!strcmp(arg, "--")) { + i++; + break; + } + else if (arg[0] == '-') + usage(reflog_exists_usage); + else + break; + } + + start = i; + + if (argc - start != 1) + usage(reflog_exists_usage); + + if (check_refname_format(argv[start], REFNAME_ALLOW_ONELEVEL)) + die("invalid ref format: %s", argv[start]); + return !reflog_exists(argv[start]); +} + /* * main "reflog" */ static const char reflog_usage[] = -"git reflog [ show | expire | delete ]"; +"git reflog [ show | expire | delete | exists ]"; int cmd_reflog(int argc, const char **argv, const char *prefix) { @@ -724,5 +752,8 @@ int cmd_reflog(int argc, const char **argv, const char *prefix) if (!strcmp(argv[1], "delete")) return cmd_reflog_delete(argc - 1, argv + 1, prefix); + if (!strcmp(argv[1], "exists")) + return cmd_reflog_exists(argc - 1, argv + 1, prefix); + return cmd_log_reflog(argc, argv, prefix); } diff --git a/builtin/remote.c b/builtin/remote.c index cc3c7410f3..181668dedd 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -581,7 +581,6 @@ static int migrate_file(struct remote *remote) { struct strbuf buf = STRBUF_INIT; int i; - const char *path = NULL; strbuf_addf(&buf, "remote.%s.url", remote->name); for (i = 0; i < remote->url_nr; i++) @@ -601,11 +600,9 @@ static int migrate_file(struct remote *remote) return error(_("Could not append '%s' to '%s'"), remote->fetch_refspec[i], buf.buf); if (remote->origin == REMOTE_REMOTES) - path = git_path("remotes/%s", remote->name); + unlink_or_warn(git_path("remotes/%s", remote->name)); else if (remote->origin == REMOTE_BRANCHES) - path = git_path("branches/%s", remote->name); - if (path) - unlink_or_warn(path); + unlink_or_warn(git_path("branches/%s", remote->name)); return 0; } diff --git a/builtin/repack.c b/builtin/repack.c index af7340c7ba..70b9b1eaf1 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -285,8 +285,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) failed = 0; for_each_string_list_item(item, &names) { for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { - const char *fname_old; - char *fname; + char *fname, *fname_old; fname = mkpathdup("%s/pack-%s%s", packdir, item->string, exts[ext].name); if (!file_exists(fname)) { @@ -294,7 +293,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) continue; } - fname_old = mkpath("%s/old-%s%s", packdir, + fname_old = mkpathdup("%s/old-%s%s", packdir, item->string, exts[ext].name); if (file_exists(fname_old)) if (unlink(fname_old)) @@ -302,10 +301,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (!failed && rename(fname, fname_old)) { free(fname); + free(fname_old); failed = 1; break; } else { string_list_append(&rollback, fname); + free(fname_old); } } if (failed) @@ -314,13 +315,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (failed) { struct string_list rollback_failure = STRING_LIST_INIT_DUP; for_each_string_list_item(item, &rollback) { - const char *fname_old; - char *fname; + char *fname, *fname_old; fname = mkpathdup("%s/%s", packdir, item->string); - fname_old = mkpath("%s/old-%s", packdir, item->string); + fname_old = mkpathdup("%s/old-%s", packdir, item->string); if (rename(fname_old, fname)) string_list_append(&rollback_failure, fname); free(fname); + free(fname_old); } if (rollback_failure.nr) { @@ -368,13 +369,14 @@ int cmd_repack(int argc, const char **argv, const char *prefix) /* Remove the "old-" files */ for_each_string_list_item(item, &names) { for (ext = 0; ext < ARRAY_SIZE(exts); ext++) { - const char *fname; - fname = mkpath("%s/old-%s%s", - packdir, - item->string, - exts[ext].name); + char *fname; + fname = mkpathdup("%s/old-%s%s", + packdir, + item->string, + exts[ext].name); if (remove_path(fname)) warning(_("removing '%s' failed"), fname); + free(fname); } } diff --git a/builtin/reset.c b/builtin/reset.c index 4c08ddc1ca..c503e75a59 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -36,7 +36,7 @@ static const char *reset_type_names[] = { static inline int is_merge(void) { - return !access(git_path("MERGE_HEAD"), F_OK); + return !access(git_path_merge_head(), F_OK); } static int reset_index(const unsigned char *sha1, int reset_type, int quiet) diff --git a/builtin/rev-list.c b/builtin/rev-list.c index c0b4b53652..d80d1ed359 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -350,6 +350,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) revs.diff) usage(rev_list_usage); + if (revs.show_notes) + die(_("rev-list does not support display of notes")); + save_commit_buffer = (revs.verbose_header || revs.grep_filter.pattern_list || revs.grep_filter.header_list); diff --git a/builtin/send-pack.c b/builtin/send-pack.c index b961e5ae78..23b2962cb0 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -11,6 +11,7 @@ #include "transport.h" #include "version.h" #include "sha1-array.h" +#include "gpg-interface.h" static const char send_pack_usage[] = "git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] [<host>:]<directory> [<ref>...]\n" @@ -113,6 +114,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) int from_stdin = 0; struct push_cas_option cas = {0}; + git_config(git_gpg_config, NULL); + argv++; for (i = 1; i < argc; i++, argv++) { const char *arg = *argv; diff --git a/builtin/tag.c b/builtin/tag.c index 5f6cdc5a03..cccca99104 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -579,6 +579,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) struct create_tag_options opt; char *cleanup_arg = NULL; int annotate = 0, force = 0, lines = -1; + int create_reflog = 0; int cmdmode = 0; const char *msgfile = NULL, *keyid = NULL; struct msg_arg msg = { 0, STRBUF_INIT }; @@ -605,6 +606,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_STRING('u', "local-user", &keyid, N_("key-id"), N_("use another key to sign the tag")), OPT__FORCE(&force, N_("replace the tag if exists")), + OPT_BOOL(0, "create-reflog", &create_reflog, N_("create_reflog")), OPT_GROUP(N_("Tag listing options")), OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")), @@ -733,7 +735,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix) transaction = ref_transaction_begin(&err); if (!transaction || ref_transaction_update(transaction, ref.buf, object, prev, - 0, NULL, &err) || + create_reflog ? REF_FORCE_CREATE_REFLOG : 0, + NULL, &err) || ref_transaction_commit(transaction, &err)) die("%s", err.buf); ref_transaction_free(transaction); diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 6763cf1837..04dd00f734 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -14,6 +14,7 @@ static const char * const git_update_ref_usage[] = { static char line_termination = '\n'; static int update_flags; +static unsigned create_reflog_flag; static const char *msg; /* @@ -200,7 +201,8 @@ static const char *parse_cmd_update(struct ref_transaction *transaction, if (ref_transaction_update(transaction, refname, new_sha1, have_old ? old_sha1 : NULL, - update_flags, msg, &err)) + update_flags | create_reflog_flag, + msg, &err)) die("%s", err.buf); update_flags = 0; @@ -231,7 +233,8 @@ static const char *parse_cmd_create(struct ref_transaction *transaction, die("create %s: extra input: %s", refname, next); if (ref_transaction_create(transaction, refname, new_sha1, - update_flags, msg, &err)) + update_flags | create_reflog_flag, + msg, &err)) die("%s", err.buf); update_flags = 0; @@ -354,6 +357,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) unsigned char sha1[20], oldsha1[20]; int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0; unsigned int flags = 0; + int create_reflog = 0; struct option options[] = { OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")), OPT_BOOL('d', NULL, &delete, N_("delete the reference")), @@ -361,6 +365,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) N_("update <refname> not the one it points to")), OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")), OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")), + OPT_BOOL( 0 , "create-reflog", &create_reflog, N_("create_reflog")), OPT_END(), }; @@ -370,6 +375,8 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) if (msg && !*msg) die("Refusing to perform update with empty message."); + create_reflog_flag = create_reflog ? REF_FORCE_CREATE_REFLOG : 0; + if (read_stdin) { struct strbuf err = STRBUF_INIT; struct ref_transaction *transaction; @@ -431,5 +438,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) flags); else return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL, - flags, UPDATE_REFS_DIE_ON_ERR); + flags | create_reflog_flag, + UPDATE_REFS_DIE_ON_ERR); } diff --git a/builtin/worktree.c b/builtin/worktree.c index bbb169a5d0..71bb770f7a 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -3,6 +3,8 @@ #include "dir.h" #include "parse-options.h" #include "argv-array.h" +#include "branch.h" +#include "refs.h" #include "run-command.h" #include "sigchain.h" #include "refs.h" @@ -13,6 +15,13 @@ static const char * const worktree_usage[] = { NULL }; +struct add_opts { + int force; + int detach; + const char *new_branch; + int force_new_branch; +}; + static int show_only; static int verbose; static unsigned long expire; @@ -172,19 +181,35 @@ static const char *worktree_basename(const char *path, int *olen) return name; } -static int add_worktree(const char *path, const char **child_argv) +static int add_worktree(const char *path, const char *refname, + const struct add_opts *opts) { struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT; struct strbuf sb = STRBUF_INIT; const char *name; struct stat st; struct child_process cp; + struct argv_array child_env = ARGV_ARRAY_INIT; int counter = 0, len, ret; - unsigned char rev[20]; + struct strbuf symref = STRBUF_INIT; + struct commit *commit = NULL; if (file_exists(path) && !is_empty_dir(path)) die(_("'%s' already exists"), path); + /* is 'refname' a branch or commit? */ + if (opts->force_new_branch) /* definitely a branch */ + ; + else if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) && + ref_exists(symref.buf)) { /* it's a branch */ + if (!opts->force) + die_if_checked_out(symref.buf); + } else { /* must be a commit */ + commit = lookup_commit_reference_by_name(refname); + if (!commit) + die(_("invalid reference: %s"), refname); + } + name = worktree_basename(path, &len); strbuf_addstr(&sb_repo, git_path("worktrees/%.*s", (int)(path + len - name), name)); @@ -228,32 +253,40 @@ static int add_worktree(const char *path, const char **child_argv) real_path(get_git_common_dir()), name); /* * This is to keep resolve_ref() happy. We need a valid HEAD - * or is_git_directory() will reject the directory. Moreover, HEAD - * in the new worktree must resolve to the same value as HEAD in - * the current tree since the command invoked to populate the new - * worktree will be handed the branch/ref specified by the user. - * For instance, if the user asks for the new worktree to be based - * at HEAD~5, then the resolved HEAD~5 in the new worktree must - * match the resolved HEAD~5 in the current tree in order to match - * the user's expectation. + * or is_git_directory() will reject the directory. Any value which + * looks like an object ID will do since it will be immediately + * replaced by the symbolic-ref or update-ref invocation in the new + * worktree. */ - if (!resolve_ref_unsafe("HEAD", 0, rev, NULL)) - die(_("unable to resolve HEAD")); strbuf_reset(&sb); strbuf_addf(&sb, "%s/HEAD", sb_repo.buf); - write_file(sb.buf, "%s", sha1_to_hex(rev)); + write_file(sb.buf, "0000000000000000000000000000000000000000"); strbuf_reset(&sb); strbuf_addf(&sb, "%s/commondir", sb_repo.buf); write_file(sb.buf, "../.."); - fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name); + fprintf_ln(stderr, _("Preparing %s (identifier %s)"), path, name); - setenv("GIT_CHECKOUT_NEW_WORKTREE", "1", 1); - setenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1); - setenv(GIT_WORK_TREE_ENVIRONMENT, path, 1); + argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf); + argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path); memset(&cp, 0, sizeof(cp)); cp.git_cmd = 1; - cp.argv = child_argv; + + if (commit) + argv_array_pushl(&cp.args, "update-ref", "HEAD", + sha1_to_hex(commit->object.sha1), NULL); + else + argv_array_pushl(&cp.args, "symbolic-ref", "HEAD", + symref.buf, NULL); + cp.env = child_env.argv; + ret = run_command(&cp); + if (ret) + goto done; + + cp.argv = NULL; + argv_array_clear(&cp.args); + argv_array_pushl(&cp.args, "reset", "--hard", NULL); + cp.env = child_env.argv; ret = run_command(&cp); if (!ret) { is_junk = 0; @@ -262,10 +295,13 @@ static int add_worktree(const char *path, const char **child_argv) junk_work_tree = NULL; junk_git_dir = NULL; } +done: strbuf_reset(&sb); strbuf_addf(&sb, "%s/locked", sb_repo.buf); unlink_or_warn(sb.buf); + argv_array_clear(&child_env); strbuf_release(&sb); + strbuf_release(&symref); strbuf_release(&sb_repo); strbuf_release(&sb_git); return ret; @@ -273,47 +309,54 @@ static int add_worktree(const char *path, const char **child_argv) static int add(int ac, const char **av, const char *prefix) { - int force = 0, detach = 0; - const char *new_branch = NULL, *new_branch_force = NULL; + struct add_opts opts; + const char *new_branch_force = NULL; const char *path, *branch; - struct argv_array cmd = ARGV_ARRAY_INIT; struct option options[] = { - OPT__FORCE(&force, N_("checkout <branch> even if already checked out in other worktree")), - OPT_STRING('b', NULL, &new_branch, N_("branch"), + OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")), + OPT_STRING('b', NULL, &opts.new_branch, N_("branch"), N_("create a new branch")), OPT_STRING('B', NULL, &new_branch_force, N_("branch"), N_("create or reset a branch")), - OPT_BOOL(0, "detach", &detach, N_("detach HEAD at named commit")), + OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")), OPT_END() }; + memset(&opts, 0, sizeof(opts)); ac = parse_options(ac, av, prefix, options, worktree_usage, 0); - if (new_branch && new_branch_force) - die(_("-b and -B are mutually exclusive")); + if (!!opts.detach + !!opts.new_branch + !!new_branch_force > 1) + die(_("-b, -B, and --detach are mutually exclusive")); if (ac < 1 || ac > 2) usage_with_options(worktree_usage, options); path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0]; branch = ac < 2 ? "HEAD" : av[1]; - if (ac < 2 && !new_branch && !new_branch_force) { + opts.force_new_branch = !!new_branch_force; + if (opts.force_new_branch) + opts.new_branch = new_branch_force; + + if (ac < 2 && !opts.new_branch && !opts.detach) { int n; const char *s = worktree_basename(path, &n); - new_branch = xstrndup(s, n); + opts.new_branch = xstrndup(s, n); + } + + if (opts.new_branch) { + struct child_process cp; + memset(&cp, 0, sizeof(cp)); + cp.git_cmd = 1; + argv_array_push(&cp.args, "branch"); + if (opts.force_new_branch) + argv_array_push(&cp.args, "--force"); + argv_array_push(&cp.args, opts.new_branch); + argv_array_push(&cp.args, branch); + if (run_command(&cp)) + return -1; + branch = opts.new_branch; } - argv_array_push(&cmd, "checkout"); - if (force) - argv_array_push(&cmd, "--ignore-other-worktrees"); - if (new_branch) - argv_array_pushl(&cmd, "-b", new_branch, NULL); - if (new_branch_force) - argv_array_pushl(&cmd, "-B", new_branch_force, NULL); - if (detach) - argv_array_push(&cmd, "--detach"); - argv_array_push(&cmd, branch); - - return add_worktree(path, cmd.argv); + return add_worktree(path, branch, &opts); } int cmd_worktree(int ac, const char **av, const char *prefix) |